threads 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a5db04c914b7f8a558b48e026ab0be0336a4cc3723a9c77999fc3d7e20a276c
4
- data.tar.gz: f694b29951c0174472b993239f30fe5318f2102eb467b44ef33af714b8fa1015
3
+ metadata.gz: 35751a30196f3827eac923a47ea3d72475405ad55eb46c10207385974b4d421a
4
+ data.tar.gz: 618ed7f1bf2d6afd9af5cb5c4ca4ca8fbdc08e861cb1526efe5276e696f26a70
5
5
  SHA512:
6
- metadata.gz: 8e0cd08f073de1d481622deec525bf6b3ba676619260a377a89211d94201988bb5d20a2a9397fe1e7aa73f13d28332a5b276495b527c81736c49ae47a1af9dd3
7
- data.tar.gz: cc0cea726d7a3c424c3cde5c571c1a32c1b408ce55731b7a684038e0ae7c5c417c9f081870e026bea4088afa6db4905e4c969b99f394d78f06d9776f1d617ae0
6
+ metadata.gz: 4f7b4de3013c5e0b37a6cfe7a10b6f4eaa6a88e67c57a55684b07941692bca5e2bdb4e1dc537c01542488c296f6f25c9abcc509b96949527a0393fe631b3ef7a
7
+ data.tar.gz: 54368c9d740d5af73be9f5816aae8b4af5a3bfbf731aa2b28a42ecf591a464147168a129af9a96e2c7cb36d8a6c54b16e7fb7c8a3c1fd8182a3256b0cd88a57e
data/Gemfile CHANGED
@@ -1,31 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018-2023 Yegor Bugayenko
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the 'Software'), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in all
13
- # copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- # SOFTWARE.
3
+ # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
22
5
 
23
6
  source 'https://rubygems.org'
24
7
  gemspec
25
8
 
26
- gem 'minitest', '~>5', require: false
27
- gem 'rake', '13.0.6', require: false
28
- gem 'rdoc', '6.5.0', require: false
29
- gem 'rubocop', '1.55.1', require: false
30
- gem 'rubocop-rspec', '2.23.0', require: false
31
- gem 'simplecov', '0.22.0', require: false
9
+ gem 'minitest', '~>5.25', require: false
10
+ gem 'minitest-reporters', '~>1.7', require: false
11
+ gem 'rake', '~>13.2', require: false
12
+ gem 'rdoc', '~>6.12', require: false
13
+ gem 'rubocop', '~>1.75', require: false
14
+ gem 'rubocop-minitest', '~>0.38', require: false
15
+ gem 'rubocop-performance', '~>1.25', require: false
16
+ gem 'rubocop-rake', '~>0.7', require: false
17
+ gem 'simplecov', '~>0.22', require: false
18
+ gem 'simplecov-cobertura', '~>3.0', require: false
19
+ gem 'yard', '~>0.9', require: false
data/Gemfile.lock ADDED
@@ -0,0 +1,110 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ threads (0.0.0)
5
+ backtrace (~> 0)
6
+ concurrent-ruby (~> 1.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ansi (1.5.0)
12
+ ast (2.4.3)
13
+ backtrace (0.4.1)
14
+ builder (3.3.0)
15
+ concurrent-ruby (1.3.5)
16
+ date (3.5.0)
17
+ docile (1.4.1)
18
+ erb (6.0.0)
19
+ json (2.16.0)
20
+ language_server-protocol (3.17.0.5)
21
+ lint_roller (1.1.0)
22
+ minitest (5.26.2)
23
+ minitest-reporters (1.7.1)
24
+ ansi
25
+ builder
26
+ minitest (>= 5.0)
27
+ ruby-progressbar
28
+ parallel (1.27.0)
29
+ parser (3.3.10.0)
30
+ ast (~> 2.4.1)
31
+ racc
32
+ prism (1.6.0)
33
+ psych (5.2.6)
34
+ date
35
+ stringio
36
+ racc (1.8.1)
37
+ rainbow (3.1.1)
38
+ rake (13.3.1)
39
+ rdoc (6.15.1)
40
+ erb
41
+ psych (>= 4.0.0)
42
+ tsort
43
+ regexp_parser (2.11.3)
44
+ rexml (3.4.4)
45
+ rubocop (1.81.7)
46
+ json (~> 2.3)
47
+ language_server-protocol (~> 3.17.0.2)
48
+ lint_roller (~> 1.1.0)
49
+ parallel (~> 1.10)
50
+ parser (>= 3.3.0.2)
51
+ rainbow (>= 2.2.2, < 4.0)
52
+ regexp_parser (>= 2.9.3, < 3.0)
53
+ rubocop-ast (>= 1.47.1, < 2.0)
54
+ ruby-progressbar (~> 1.7)
55
+ unicode-display_width (>= 2.4.0, < 4.0)
56
+ rubocop-ast (1.48.0)
57
+ parser (>= 3.3.7.2)
58
+ prism (~> 1.4)
59
+ rubocop-minitest (0.38.2)
60
+ lint_roller (~> 1.1)
61
+ rubocop (>= 1.75.0, < 2.0)
62
+ rubocop-ast (>= 1.38.0, < 2.0)
63
+ rubocop-performance (1.26.1)
64
+ lint_roller (~> 1.1)
65
+ rubocop (>= 1.75.0, < 2.0)
66
+ rubocop-ast (>= 1.47.1, < 2.0)
67
+ rubocop-rake (0.7.1)
68
+ lint_roller (~> 1.1)
69
+ rubocop (>= 1.72.1)
70
+ ruby-progressbar (1.13.0)
71
+ simplecov (0.22.0)
72
+ docile (~> 1.1)
73
+ simplecov-html (~> 0.11)
74
+ simplecov_json_formatter (~> 0.1)
75
+ simplecov-cobertura (3.1.0)
76
+ rexml
77
+ simplecov (~> 0.19)
78
+ simplecov-html (0.13.2)
79
+ simplecov_json_formatter (0.1.4)
80
+ stringio (3.1.8)
81
+ tsort (0.2.0)
82
+ unicode-display_width (3.2.0)
83
+ unicode-emoji (~> 4.1)
84
+ unicode-emoji (4.1.0)
85
+ yard (0.9.37)
86
+
87
+ PLATFORMS
88
+ arm64-darwin-22
89
+ arm64-darwin-23
90
+ arm64-darwin-24
91
+ x64-mingw-ucrt
92
+ x86_64-darwin-24
93
+ x86_64-linux
94
+
95
+ DEPENDENCIES
96
+ minitest (~> 5.25)
97
+ minitest-reporters (~> 1.7)
98
+ rake (~> 13.2)
99
+ rdoc (~> 6.12)
100
+ rubocop (~> 1.75)
101
+ rubocop-minitest (~> 0.38)
102
+ rubocop-performance (~> 1.25)
103
+ rubocop-rake (~> 0.7)
104
+ simplecov (~> 0.22)
105
+ simplecov-cobertura (~> 3.0)
106
+ threads!
107
+ yard (~> 0.9)
108
+
109
+ BUNDLED WITH
110
+ 2.6.8
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019 Yegor Bugayenko
3
+ Copyright (c) 2018-2025 Yegor Bugayenko
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/LICENSES/MIT.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018-2025 Yegor Bugayenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included
13
+ in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,30 +1,33 @@
1
- <img src="/logo.svg" width="64px" height="64px"/>
1
+ # Runs Code Block in Many Threads
2
2
 
3
3
  [![EO principles respected here](https://www.elegantobjects.org/badge.svg)](https://www.elegantobjects.org)
4
- [![DevOps By Rultor.com](http://www.rultor.com/b/yegor256/threads)](http://www.rultor.com/p/yegor256/threads)
4
+ [![DevOps By Rultor.com](https://www.rultor.com/b/yegor256/threads)](https://www.rultor.com/p/yegor256/threads)
5
5
  [![We recommend RubyMine](https://www.elegantobjects.org/rubymine.svg)](https://www.jetbrains.com/ruby/)
6
6
 
7
7
  [![rake](https://github.com/yegor256/threads/actions/workflows/rake.yml/badge.svg)](https://github.com/yegor256/threads/actions/workflows/rake.yml)
8
- [![Gem Version](https://badge.fury.io/rb/threads.svg)](http://badge.fury.io/rb/threads)
8
+ [![Gem Version](https://badge.fury.io/rb/threads.svg)](https://badge.fury.io/rb/threads)
9
9
  [![Maintainability](https://api.codeclimate.com/v1/badges/24fc3acdf781d98b8749/maintainability)](https://codeclimate.com/github/yegor256/threads/maintainability)
10
- [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/yegor256/threads/master/frames)
10
+ [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](https://rubydoc.info/github/yegor256/threads/master/frames)
11
11
  [![Test Coverage](https://img.shields.io/codecov/c/github/yegor256/threads.svg)](https://codecov.io/github/yegor256/threads?branch=master)
12
12
  [![Hits-of-Code](https://hitsofcode.com/github/yegor256/threads)](https://hitsofcode.com/view/github/yegor256/threads)
13
13
 
14
- Read this blog post first: [_Do You Test Ruby Code for Thread Safety?_](https://www.yegor256.com/2018/11/06/ruby-threads.html)
14
+ Read this blog post first: [Do You Test Ruby Code for Thread Safety?][blog]
15
15
 
16
16
  When you need to test your code for thread safety, what do you do?
17
17
  That's right, you just don't test it.
18
- That's [wrong](https://www.yegor256.com/2018/03/27/how-to-test-thread-safety.html)!
19
- This simple gem helps you test your code with just two additional lines of code.
18
+ That's
19
+ [wrong](https://www.yegor256.com/2018/03/27/how-to-test-thread-safety.html)!
20
+ This simple gem helps you test your code
21
+ with just two additional lines of code.
20
22
 
21
23
  First, install it:
22
24
 
23
25
  ```bash
24
- $ gem install threads
26
+ gem install threads
25
27
  ```
26
28
 
27
- Then, use it like this, to test your code from multiple concurrently running threads:
29
+ Then, use it like this, to test your code from multiple
30
+ concurrently running threads:
28
31
 
29
32
  ```ruby
30
33
  require 'threads'
@@ -33,9 +36,11 @@ Threads.new(5).assert do |i|
33
36
  end
34
37
  ```
35
38
 
36
- You can put whatever you want into the block. The code will be executed from five threads, concurrently.
39
+ You can put whatever you want into the block. The code will be executed
40
+ from five threads, concurrently.
37
41
  You can also make sure the code block runs only a specific number of times
38
- specifying the argument in the `assert` method (it can't be smaller than the amount of threads):
42
+ specifying the argument in the `assert` method (it can't be smaller
43
+ than the amount of threads):
39
44
 
40
45
  ```ruby
41
46
  Threads.new(5).assert(20) do |i, r|
@@ -56,14 +61,18 @@ That's it.
56
61
 
57
62
  ## How to contribute
58
63
 
59
- Read [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
60
- Make sure you build is green before you contribute
61
- your pull request. You will need to have [Ruby](https://www.ruby-lang.org/en/) 2.3+ and
64
+ Read
65
+ [these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
66
+ Make sure your build is green before you contribute
67
+ your pull request. You will need to have
68
+ [Ruby](https://www.ruby-lang.org/en/) 2.3+ and
62
69
  [Bundler](https://bundler.io/) installed. Then:
63
70
 
64
- ```
65
- $ bundle update
66
- $ bundle exec rake
71
+ ```bash
72
+ bundle update
73
+ bundle exec rake
67
74
  ```
68
75
 
69
76
  If it's clean and you don't see any error messages, submit your pull request.
77
+
78
+ [blog]: https://www.yegor256.com/2018/11/06/ruby-threads.html
data/REUSE.toml ADDED
@@ -0,0 +1,36 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ version = 1
5
+ [[annotations]]
6
+ path = [
7
+ ".DS_Store",
8
+ ".gitattributes",
9
+ ".gitignore",
10
+ ".pdd",
11
+ "**.json",
12
+ "**.md",
13
+ "**.png",
14
+ "**.svg",
15
+ "**.txt",
16
+ "**/.DS_Store",
17
+ "**/.gitignore",
18
+ "**/.pdd",
19
+ "**/*.csv",
20
+ "**/*.jpg",
21
+ "**/*.json",
22
+ "**/*.md",
23
+ "**/*.pdf",
24
+ "**/*.png",
25
+ "**/*.svg",
26
+ "**/*.txt",
27
+ "**/*.vm",
28
+ "**/CNAME",
29
+ "**/Gemfile.lock",
30
+ "Gemfile.lock",
31
+ "README.md",
32
+ "renovate.json",
33
+ ]
34
+ precedence = "override"
35
+ SPDX-FileCopyrightText = "Copyright (c) 2025 Yegor Bugayenko"
36
+ SPDX-License-Identifier = "MIT"
data/Rakefile CHANGED
@@ -1,24 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018-2023 Yegor Bugayenko
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the 'Software'), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in all
13
- # copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- # SOFTWARE.
3
+ # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
22
5
 
23
6
  require 'rubygems'
24
7
  require 'rake'
@@ -34,7 +17,7 @@ def version
34
17
  Gem::Specification.load(Dir['*.gemspec'].first).version
35
18
  end
36
19
 
37
- task default: %i[clean test rubocop]
20
+ task default: %i[clean test rubocop yard]
38
21
 
39
22
  require 'rake/testtask'
40
23
  desc 'Run all unit tests'
@@ -44,9 +27,15 @@ Rake::TestTask.new(:test) do |test|
44
27
  test.verbose = false
45
28
  end
46
29
 
30
+ require 'yard'
31
+ desc 'Build Yard documentation'
32
+ YARD::Rake::YardocTask.new do |t|
33
+ t.files = ['lib/**/*.rb']
34
+ t.options = ['--fail-on-warning']
35
+ end
36
+
47
37
  require 'rubocop/rake_task'
48
38
  desc 'Run RuboCop on all directories'
49
39
  RuboCop::RakeTask.new(:rubocop) do |task|
50
40
  task.fail_on_error = true
51
- task.requires << 'rubocop-rspec'
52
41
  end
data/lib/threads.rb CHANGED
@@ -1,75 +1,110 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (The MIT License)
4
- #
5
- # Copyright (c) 2018-2023 Yegor Bugayenko
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the 'Software'), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in all
15
- # copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
- # SOFTWARE.
3
+ # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
24
5
 
25
6
  require 'concurrent'
26
7
  require 'backtrace'
27
8
 
28
9
  # Threads.
10
+ #
11
+ # This class may help you test your code for thread-safety by running
12
+ # it multiple times in a few parallel threads:
13
+ #
14
+ # require 'threads'
15
+ # Threads.new(5).assert do |i|
16
+ # puts "Hello from the thread no.#{i}"
17
+ # end
18
+ #
19
+ # Here, the message will be printed to the console five times, once in every
20
+ # thread.
21
+ #
29
22
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
- # Copyright:: Copyright (c) 2018-2023 Yegor Bugayenko
23
+ # Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko
31
24
  # License:: MIT
32
25
  class Threads
33
- def initialize(total = Concurrent.processor_count * 8, log: $stdout)
26
+ # Constructor.
27
+ #
28
+ # @param [Number] total How many threads to run
29
+ # @param [Logger] log Where to print output
30
+ # @param [Number] task_timeout How many seconds to wait for all tasks to finish
31
+ # @param [Number] shutdown_timeout How many seconds to wait for the thread pool to shut down
32
+ def initialize(total = Concurrent.processor_count * 8, log: $stdout, task_timeout: 10, shutdown_timeout: 30)
34
33
  raise "Total can't be nil" if total.nil?
35
34
  raise "Total can't be negative or zero: #{total}" unless total.positive?
36
-
37
35
  @total = total
38
36
  raise "Log can't be nil" if log.nil?
39
-
40
37
  @log = log
38
+ validate('task_timeout', task_timeout)
39
+ @task_timeout = task_timeout
40
+ validate('shutdown_timeout', shutdown_timeout)
41
+ @shutdown_timeout = shutdown_timeout
41
42
  end
42
43
 
43
- def assert(reps = @total)
44
- raise "Repetition counter #{reps} can't be smaller than #{@total}" if reps < @total
44
+ # Validate a timeout value.
45
+ # @param [String] label Label to use in the error message
46
+ # @param [Number] timeout Timeout value to validate
47
+ def validate(label, timeout)
48
+ raise "#{label} can't be nil" if timeout.nil?
49
+ raise "#{label} can't be negative or zero: #{timeout}" unless timeout.positive?
50
+ end
45
51
 
52
+ # Run them all and assert that all of them finished successfully.
53
+ #
54
+ # For example, test a counter for thread safety:
55
+ #
56
+ # counter = 0
57
+ # mutex = Mutex.new
58
+ # Threads.new(10).assert do |t|
59
+ # mutex.synchronize { counter += 1 }
60
+ # end
61
+ # puts "Final counter value: #{counter}"
62
+ #
63
+ # You can also access the repetition index:
64
+ #
65
+ # Threads.new(4).assert(100) do |t, r|
66
+ # puts "Thread #{t}, repetition #{r}"
67
+ # end
68
+ #
69
+ # @param [Number] reps How many times to repeat the testing cycle
70
+ # @param [Number] task_timeout How many seconds to wait for all tasks to finish
71
+ # @param [Number] shutdown_timeout How many seconds to wait for the thread pool to shut down
72
+ # @return nil
73
+ def assert(reps = @total, task_timeout: @task_timeout, shutdown_timeout: @shutdown_timeout)
74
+ raise "Repetition counter #{reps} can't be smaller than #{@total}" if reps < @total
75
+ validate('task_timeout', task_timeout)
76
+ validate('shutdown_timeout', shutdown_timeout)
46
77
  done = Concurrent::AtomicFixnum.new
47
78
  rep = Concurrent::AtomicFixnum.new
48
79
  pool = Concurrent::FixedThreadPool.new(@total)
49
80
  latch = Concurrent::CountDownLatch.new(1)
81
+ finish = Concurrent::CountDownLatch.new(@total)
50
82
  @total.times do |t|
51
83
  pool.post do
52
84
  Thread.current.name = "assert-thread-#{t}"
53
85
  latch.wait(10)
54
- loop do
55
- r = rep.increment
56
- break if r > reps
57
-
58
- begin
59
- yield(t, r - 1)
60
- rescue StandardError => e
61
- print(Backtrace.new(e))
62
- raise e
86
+ begin
87
+ loop do
88
+ r = rep.increment
89
+ break if r > reps
90
+ begin
91
+ yield(t, r - 1)
92
+ rescue StandardError => e
93
+ print(Backtrace.new(e))
94
+ raise e
95
+ end
63
96
  end
97
+ done.increment
98
+ ensure
99
+ finish.count_down
64
100
  end
65
- done.increment
66
101
  end
67
102
  end
68
103
  latch.count_down
104
+ finish.wait(task_timeout)
69
105
  pool.shutdown
70
- raise "Can't stop the pool" unless pool.wait_for_termination(30)
106
+ raise "Can't stop the pool" unless pool.wait_for_termination(shutdown_timeout)
71
107
  return if done.value == @total
72
-
73
108
  raise "Only #{done.value} out of #{@total} threads completed successfully"
74
109
  end
75
110
 
data/logo.svg CHANGED
@@ -16,4 +16,4 @@
16
16
  </g>
17
17
  </g>
18
18
  </g>
19
- </svg>
19
+ </svg>
data/threads.gemspec CHANGED
@@ -1,43 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # (The MIT License)
4
- #
5
- # Copyright (c) 2018-2023 Yegor Bugayenko
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the 'Software'), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in all
15
- # copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
- # SOFTWARE.
3
+ # SPDX-FileCopyrightText: Copyright (c) 2018-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
24
5
 
25
6
  require 'English'
26
7
  Gem::Specification.new do |s|
27
8
  s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
28
9
  s.required_ruby_version = '>=2.3'
29
10
  s.name = 'threads'
30
- s.version = '0.4.0'
11
+ s.version = '0.5.0'
31
12
  s.license = 'MIT'
32
13
  s.summary = 'Test threads'
33
- s.description = 'Rugy Gem to test a piece of code in concurrent threads'
14
+ s.description = 'Ruby Gem to test a piece of code in concurrent threads'
34
15
  s.authors = ['Yegor Bugayenko']
35
16
  s.email = 'yegor256@gmail.com'
36
- s.homepage = 'http://github.com/yegor256/threads'
37
- s.files = `git ls-files`.split($RS)
17
+ s.homepage = 'https://github.com/yegor256/threads'
18
+ s.files = `git ls-files | grep -v -E '^(test/|\\.|renovate)'`.split($RS)
38
19
  s.rdoc_options = ['--charset=UTF-8']
39
20
  s.extra_rdoc_files = ['README.md']
40
- s.add_runtime_dependency 'backtrace', '~>0'
41
- s.add_runtime_dependency 'concurrent-ruby', '~>1.0'
21
+ s.add_dependency 'backtrace', '~>0'
22
+ s.add_dependency 'concurrent-ruby', '~>1.0'
42
23
  s.metadata['rubygems_mfa_required'] = 'true'
43
24
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: threads
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-08-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: backtrace
@@ -38,39 +37,28 @@ dependencies:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
39
  version: '1.0'
41
- description: Rugy Gem to test a piece of code in concurrent threads
40
+ description: Ruby Gem to test a piece of code in concurrent threads
42
41
  email: yegor256@gmail.com
43
42
  executables: []
44
43
  extensions: []
45
44
  extra_rdoc_files:
46
45
  - README.md
47
46
  files:
48
- - ".0pdd.yml"
49
- - ".github/workflows/codecov.yml"
50
- - ".github/workflows/pdd.yml"
51
- - ".github/workflows/rake.yml"
52
- - ".github/workflows/xcop.yml"
53
- - ".gitignore"
54
- - ".pdd"
55
- - ".rubocop.yml"
56
- - ".rultor.yml"
57
- - ".travis.yml"
58
47
  - Gemfile
48
+ - Gemfile.lock
59
49
  - LICENSE.txt
50
+ - LICENSES/MIT.txt
60
51
  - README.md
52
+ - REUSE.toml
61
53
  - Rakefile
62
54
  - lib/threads.rb
63
55
  - logo.svg
64
- - renovate.json
65
- - test/test__helper.rb
66
- - test/test_threads.rb
67
56
  - threads.gemspec
68
- homepage: http://github.com/yegor256/threads
57
+ homepage: https://github.com/yegor256/threads
69
58
  licenses:
70
59
  - MIT
71
60
  metadata:
72
61
  rubygems_mfa_required: 'true'
73
- post_install_message:
74
62
  rdoc_options:
75
63
  - "--charset=UTF-8"
76
64
  require_paths:
@@ -86,8 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
74
  - !ruby/object:Gem::Version
87
75
  version: '0'
88
76
  requirements: []
89
- rubygems_version: 3.2.15
90
- signing_key:
77
+ rubygems_version: 3.6.9
91
78
  specification_version: 4
92
79
  summary: Test threads
93
80
  test_files: []
data/.0pdd.yml DELETED
@@ -1,9 +0,0 @@
1
- errors:
2
- - yegor256@gmail.com
3
- # alerts:
4
- # github:
5
- # - yegor256
6
-
7
- tags:
8
- - pdd
9
- - bug
@@ -1,20 +0,0 @@
1
- ---
2
- name: codecov
3
- on:
4
- push:
5
- branches:
6
- - master
7
- jobs:
8
- codecov:
9
- runs-on: ubuntu-20.04
10
- steps:
11
- - uses: actions/checkout@v3
12
- - uses: ruby/setup-ruby@v1
13
- with:
14
- ruby-version: 2.7
15
- - run: bundle update
16
- - run: bundle exec rake
17
- - uses: codecov/codecov-action@v3
18
- with:
19
- file: coverage/.resultset.json
20
- fail_ci_if_error: true
@@ -1,11 +0,0 @@
1
- ---
2
- name: pdd
3
- on:
4
- push:
5
- pull_request:
6
- jobs:
7
- pdd:
8
- runs-on: ubuntu-20.04
9
- steps:
10
- - uses: actions/checkout@v3
11
- - uses: g4s8/pdd-action@master
@@ -1,24 +0,0 @@
1
- ---
2
- name: rake
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
- branches:
9
- - master
10
- jobs:
11
- test:
12
- name: test
13
- strategy:
14
- matrix:
15
- os: [ubuntu-20.04, macos-12]
16
- ruby: [2.7, 3.2]
17
- runs-on: ${{ matrix.os }}
18
- steps:
19
- - uses: actions/checkout@v3
20
- - uses: ruby/setup-ruby@v1
21
- with:
22
- ruby-version: ${{ matrix.ruby }}
23
- - run: bundle update
24
- - run: bundle exec rake
@@ -1,11 +0,0 @@
1
- ---
2
- name: xcop
3
- on:
4
- push:
5
- pull_request:
6
- jobs:
7
- xcop:
8
- runs-on: ubuntu-20.04
9
- steps:
10
- - uses: actions/checkout@v3
11
- - uses: g4s8/xcop-action@master
data/.gitignore DELETED
@@ -1,6 +0,0 @@
1
- Gemfile.lock
2
- *.gem
3
- .bundle/
4
- .DS_Store
5
- rdoc/
6
- coverage/
data/.pdd DELETED
@@ -1,5 +0,0 @@
1
- --source=.
2
- --verbose
3
- --rule min-words:20
4
- --rule min-estimate:15
5
- --rule max-estimate:90
data/.rubocop.yml DELETED
@@ -1,14 +0,0 @@
1
- AllCops:
2
- DisplayCopNames: true
3
- TargetRubyVersion: 2.3.3
4
- NewCops: enable
5
- SuggestExtensions: false
6
-
7
- Layout/MultilineMethodCallIndentation:
8
- Enabled: false
9
- Metrics/AbcSize:
10
- Max: 30
11
- Metrics/MethodLength:
12
- Max: 30
13
- Metrics/BlockLength:
14
- Max: 30
data/.rultor.yml DELETED
@@ -1,19 +0,0 @@
1
- docker:
2
- image: yegor256/rultor-image:1.19.0
3
- assets:
4
- rubygems.yml: yegor256/home#assets/rubygems.yml
5
- install: |-
6
- sudo bundle install --no-color "--gemfile=$(pwd)/Gemfile"
7
- release:
8
- script: |-
9
- bundle exec rake
10
- rm -rf *.gem
11
- sed -i "s/0\.0\.0/${tag}/g" threads.gemspec
12
- git add threads.gemspec
13
- git commit -m "Version set to ${tag}"
14
- gem build threads.gemspec
15
- chmod 0600 ../rubygems.yml
16
- gem push *.gem --config-file ../rubygems.yml
17
- merge:
18
- script: |-
19
- bundle exec rake
data/.travis.yml DELETED
@@ -1,14 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.6.0
4
- branches:
5
- only:
6
- - master
7
- install:
8
- - bundle install
9
- script:
10
- - set -e
11
- - bundle exec rake
12
- after_success:
13
- - "bash <(curl -s https://codecov.io/bash)"
14
-
data/renovate.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": [
4
- "config:base"
5
- ]
6
- }
data/test/test__helper.rb DELETED
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright (c) 2019 Yegor Bugayenko
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the 'Software'), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in all
13
- # copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- # SOFTWARE.
22
-
23
- $stdout.sync = true
24
-
25
- require 'simplecov'
26
- SimpleCov.start
data/test/test_threads.rb DELETED
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # (The MIT License)
4
- #
5
- # Copyright (c) 2018-2023 Yegor Bugayenko
6
- #
7
- # Permission is hereby granted, free of charge, to any person obtaining a copy
8
- # of this software and associated documentation files (the 'Software'), to deal
9
- # in the Software without restriction, including without limitation the rights
10
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- # copies of the Software, and to permit persons to whom the Software is
12
- # furnished to do so, subject to the following conditions:
13
- #
14
- # The above copyright notice and this permission notice shall be included in all
15
- # copies or substantial portions of the Software.
16
- #
17
- # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
- # SOFTWARE.
24
-
25
- require 'minitest/autorun'
26
- require 'concurrent'
27
- require_relative '../lib/threads'
28
-
29
- # Threads test.
30
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
- # Copyright:: Copyright (c) 2018-2023 Yegor Bugayenko
32
- # License:: MIT
33
- class ThreadsTest < Minitest::Test
34
- def test_multiple_threads
35
- done = Concurrent::AtomicFixnum.new
36
- Threads.new(10).assert do
37
- done.increment
38
- end
39
- assert_equal(10, done.value)
40
- end
41
-
42
- def test_multiple_threads_with_cap
43
- done = Concurrent::AtomicFixnum.new
44
- Threads.new(3).assert(20) do
45
- done.increment
46
- end
47
- assert_equal(20, done.value)
48
- end
49
-
50
- def test_multiple_threads_with_errors
51
- log = FakeLog.new
52
- assert_raises do
53
- Threads.new(3, log: log).assert(20) do
54
- this_code_is_broken
55
- end
56
- end
57
- assert_equal(3, log.logs.count)
58
- end
59
-
60
- class FakeLog
61
- attr_reader :logs
62
-
63
- def initialize
64
- @logs = []
65
- end
66
-
67
- def error(msg)
68
- @logs << msg
69
- end
70
- end
71
- end