monitor 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 72702da1a3456b7e030cff3224eb4d8ab598ba0a7b4d1dfcd751b3586b7d871f
4
+ data.tar.gz: 2c5fc8302e7c38198b45dbaa287a68b64ff82790586646053054836b7bb1d641
5
+ SHA512:
6
+ metadata.gz: 2b95f512ce9d5de335ca4e44386b1e5fee2767ea5c6716bf3265eec1fe3411f74b87d2f12c056c0116acb9f70eb7501c8c1ed9ef39de7e507f7bae9c92634e7e
7
+ data.tar.gz: 9fb8a3b869fc1db34cb0fdb1347e9160653a7b36d57b7513100cf5dd6912bd02603a3e15ec630e3215fc4b2c69f44ec6d46450700bd82d54ce9b79695731a94c
@@ -0,0 +1,24 @@
1
+ name: ubuntu
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
+ strategy:
9
+ matrix:
10
+ ruby: [ 2.7, 2.6, 2.5, 2.4, head ]
11
+ os: [ ubuntu-latest, macos-latest ]
12
+ runs-on: ${{ matrix.os }}
13
+ steps:
14
+ - uses: actions/checkout@master
15
+ - name: Set up Ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: Install dependencies
20
+ run: |
21
+ gem install bundler --no-document
22
+ bundle install
23
+ - name: Run test
24
+ run: rake test
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.dll
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "bundler"
7
+ gem "rake"
8
+ gem "test-unit"
9
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,72 @@
1
+ # Monitor
2
+
3
+ In concurrent programming, a monitor is an object or module intended to be
4
+ used safely by more than one thread. The defining characteristic of a
5
+ monitor is that its methods are executed with mutual exclusion. That is, at
6
+ each point in time, at most one thread may be executing any of its methods.
7
+ This mutual exclusion greatly simplifies reasoning about the implementation
8
+ of monitors compared to reasoning about parallel code that updates a data
9
+ structure.
10
+
11
+ You can read more about the general principles on the Wikipedia page for
12
+ Monitors[http://en.wikipedia.org/wiki/Monitor_%28synchronization%29]
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'monitor'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle install
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install monitor
29
+
30
+ ## Usage
31
+
32
+ ```ruby
33
+ require 'monitor.rb'
34
+
35
+ buf = []
36
+ buf.extend(MonitorMixin)
37
+ empty_cond = buf.new_cond
38
+
39
+ # consumer
40
+ Thread.start do
41
+ loop do
42
+ buf.synchronize do
43
+ empty_cond.wait_while { buf.empty? }
44
+ print buf.shift
45
+ end
46
+ end
47
+ end
48
+
49
+ # producer
50
+ while line = ARGF.gets
51
+ buf.synchronize do
52
+ buf.push(line)
53
+ empty_cond.signal
54
+ end
55
+ end
56
+ ```
57
+
58
+ The consumer thread waits for the producer thread to push a line to buf
59
+ while <tt>buf.empty?</tt>. The producer thread (main thread) reads a
60
+ line from ARGF and pushes it into buf then calls <tt>empty_cond.signal</tt>
61
+ to notify the consumer thread of new data.
62
+
63
+ ## Development
64
+
65
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
66
+
67
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
68
+
69
+ ## Contributing
70
+
71
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/monitor.
72
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test/lib"
6
+ t.ruby_opts << "-rhelper"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "monitor"
5
+
6
+ require "irb"
7
+ IRB.start(__FILE__)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,320 @@
1
+ # frozen_string_literal: false
2
+ # = monitor.rb
3
+ #
4
+ # Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
5
+ #
6
+ # This library is distributed under the terms of the Ruby license.
7
+ # You can freely distribute/modify this library.
8
+ #
9
+
10
+ #
11
+ # In concurrent programming, a monitor is an object or module intended to be
12
+ # used safely by more than one thread. The defining characteristic of a
13
+ # monitor is that its methods are executed with mutual exclusion. That is, at
14
+ # each point in time, at most one thread may be executing any of its methods.
15
+ # This mutual exclusion greatly simplifies reasoning about the implementation
16
+ # of monitors compared to reasoning about parallel code that updates a data
17
+ # structure.
18
+ #
19
+ # You can read more about the general principles on the Wikipedia page for
20
+ # Monitors[http://en.wikipedia.org/wiki/Monitor_%28synchronization%29]
21
+ #
22
+ # == Examples
23
+ #
24
+ # === Simple object.extend
25
+ #
26
+ # require 'monitor.rb'
27
+ #
28
+ # buf = []
29
+ # buf.extend(MonitorMixin)
30
+ # empty_cond = buf.new_cond
31
+ #
32
+ # # consumer
33
+ # Thread.start do
34
+ # loop do
35
+ # buf.synchronize do
36
+ # empty_cond.wait_while { buf.empty? }
37
+ # print buf.shift
38
+ # end
39
+ # end
40
+ # end
41
+ #
42
+ # # producer
43
+ # while line = ARGF.gets
44
+ # buf.synchronize do
45
+ # buf.push(line)
46
+ # empty_cond.signal
47
+ # end
48
+ # end
49
+ #
50
+ # The consumer thread waits for the producer thread to push a line to buf
51
+ # while <tt>buf.empty?</tt>. The producer thread (main thread) reads a
52
+ # line from ARGF and pushes it into buf then calls <tt>empty_cond.signal</tt>
53
+ # to notify the consumer thread of new data.
54
+ #
55
+ # === Simple Class include
56
+ #
57
+ # require 'monitor'
58
+ #
59
+ # class SynchronizedArray < Array
60
+ #
61
+ # include MonitorMixin
62
+ #
63
+ # def initialize(*args)
64
+ # super(*args)
65
+ # end
66
+ #
67
+ # alias :old_shift :shift
68
+ # alias :old_unshift :unshift
69
+ #
70
+ # def shift(n=1)
71
+ # self.synchronize do
72
+ # self.old_shift(n)
73
+ # end
74
+ # end
75
+ #
76
+ # def unshift(item)
77
+ # self.synchronize do
78
+ # self.old_unshift(item)
79
+ # end
80
+ # end
81
+ #
82
+ # # other methods ...
83
+ # end
84
+ #
85
+ # +SynchronizedArray+ implements an Array with synchronized access to items.
86
+ # This Class is implemented as subclass of Array which includes the
87
+ # MonitorMixin module.
88
+ #
89
+ module MonitorMixin
90
+ #
91
+ # FIXME: This isn't documented in Nutshell.
92
+ #
93
+ # Since MonitorMixin.new_cond returns a ConditionVariable, and the example
94
+ # above calls while_wait and signal, this class should be documented.
95
+ #
96
+ class ConditionVariable
97
+ class Timeout < Exception; end
98
+
99
+ #
100
+ # Releases the lock held in the associated monitor and waits; reacquires the lock on wakeup.
101
+ #
102
+ # If +timeout+ is given, this method returns after +timeout+ seconds passed,
103
+ # even if no other thread doesn't signal.
104
+ #
105
+ def wait(timeout = nil)
106
+ Thread.handle_interrupt(Exception => :never) do
107
+ @monitor.__send__(:mon_check_owner)
108
+ count = @monitor.__send__(:mon_exit_for_cond)
109
+ begin
110
+ Thread.handle_interrupt(Exception => :immediate) do
111
+ @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout)
112
+ end
113
+ return true
114
+ ensure
115
+ @monitor.__send__(:mon_enter_for_cond, count)
116
+ end
117
+ end
118
+ end
119
+
120
+ #
121
+ # Calls wait repeatedly while the given block yields a truthy value.
122
+ #
123
+ def wait_while
124
+ while yield
125
+ wait
126
+ end
127
+ end
128
+
129
+ #
130
+ # Calls wait repeatedly until the given block yields a truthy value.
131
+ #
132
+ def wait_until
133
+ until yield
134
+ wait
135
+ end
136
+ end
137
+
138
+ #
139
+ # Wakes up the first thread in line waiting for this lock.
140
+ #
141
+ def signal
142
+ @monitor.__send__(:mon_check_owner)
143
+ @cond.signal
144
+ end
145
+
146
+ #
147
+ # Wakes up all threads waiting for this lock.
148
+ #
149
+ def broadcast
150
+ @monitor.__send__(:mon_check_owner)
151
+ @cond.broadcast
152
+ end
153
+
154
+ private
155
+
156
+ def initialize(monitor)
157
+ @monitor = monitor
158
+ @cond = Thread::ConditionVariable.new
159
+ end
160
+ end
161
+
162
+ def self.extend_object(obj)
163
+ super(obj)
164
+ obj.__send__(:mon_initialize)
165
+ end
166
+
167
+ #
168
+ # Attempts to enter exclusive section. Returns +false+ if lock fails.
169
+ #
170
+ def mon_try_enter
171
+ if @mon_owner != Thread.current
172
+ unless @mon_mutex.try_lock
173
+ return false
174
+ end
175
+ @mon_owner = Thread.current
176
+ @mon_count = 0
177
+ end
178
+ @mon_count += 1
179
+ return true
180
+ end
181
+ # For backward compatibility
182
+ alias try_mon_enter mon_try_enter
183
+
184
+ #
185
+ # Enters exclusive section.
186
+ #
187
+ def mon_enter
188
+ if @mon_owner != Thread.current
189
+ @mon_mutex.lock
190
+ @mon_owner = Thread.current
191
+ @mon_count = 0
192
+ end
193
+ @mon_count += 1
194
+ end
195
+
196
+ #
197
+ # Leaves exclusive section.
198
+ #
199
+ def mon_exit
200
+ mon_check_owner
201
+ @mon_count -=1
202
+ if @mon_count == 0
203
+ @mon_owner = nil
204
+ @mon_mutex.unlock
205
+ end
206
+ end
207
+
208
+ #
209
+ # Returns true if this monitor is locked by any thread
210
+ #
211
+ def mon_locked?
212
+ @mon_mutex.locked?
213
+ end
214
+
215
+ #
216
+ # Returns true if this monitor is locked by current thread.
217
+ #
218
+ def mon_owned?
219
+ @mon_mutex.locked? && @mon_owner == Thread.current
220
+ end
221
+
222
+ #
223
+ # Enters exclusive section and executes the block. Leaves the exclusive
224
+ # section automatically when the block exits. See example under
225
+ # +MonitorMixin+.
226
+ #
227
+ def mon_synchronize
228
+ # Prevent interrupt on handling interrupts; for example timeout errors
229
+ # it may break locking state.
230
+ Thread.handle_interrupt(Exception => :never){ mon_enter }
231
+ begin
232
+ yield
233
+ ensure
234
+ Thread.handle_interrupt(Exception => :never){ mon_exit }
235
+ end
236
+ end
237
+ alias synchronize mon_synchronize
238
+
239
+ #
240
+ # Creates a new MonitorMixin::ConditionVariable associated with the
241
+ # receiver.
242
+ #
243
+ def new_cond
244
+ return ConditionVariable.new(self)
245
+ end
246
+
247
+ private
248
+
249
+ # Use <tt>extend MonitorMixin</tt> or <tt>include MonitorMixin</tt> instead
250
+ # of this constructor. Have look at the examples above to understand how to
251
+ # use this module.
252
+ def initialize(*args)
253
+ super
254
+ mon_initialize
255
+ end
256
+
257
+ # Initializes the MonitorMixin after being included in a class or when an
258
+ # object has been extended with the MonitorMixin
259
+ def mon_initialize
260
+ if defined?(@mon_mutex) && @mon_mutex_owner_object_id == object_id
261
+ raise ThreadError, "already initialized"
262
+ end
263
+ @mon_mutex = Thread::Mutex.new
264
+ @mon_mutex_owner_object_id = object_id
265
+ @mon_owner = nil
266
+ @mon_count = 0
267
+ end
268
+
269
+ def mon_check_owner
270
+ if @mon_owner != Thread.current
271
+ raise ThreadError, "current thread not owner"
272
+ end
273
+ end
274
+
275
+ def mon_enter_for_cond(count)
276
+ @mon_owner = Thread.current
277
+ @mon_count = count
278
+ end
279
+
280
+ def mon_exit_for_cond
281
+ count = @mon_count
282
+ @mon_owner = nil
283
+ @mon_count = 0
284
+ return count
285
+ end
286
+ end
287
+
288
+ # Use the Monitor class when you want to have a lock object for blocks with
289
+ # mutual exclusion.
290
+ #
291
+ # require 'monitor'
292
+ #
293
+ # lock = Monitor.new
294
+ # lock.synchronize do
295
+ # # exclusive access
296
+ # end
297
+ #
298
+ class Monitor
299
+ include MonitorMixin
300
+ alias try_enter try_mon_enter
301
+ alias enter mon_enter
302
+ alias exit mon_exit
303
+ end
304
+
305
+
306
+ # Documentation comments:
307
+ # - All documentation comes from Nutshell.
308
+ # - MonitorMixin.new_cond appears in the example, but is not documented in
309
+ # Nutshell.
310
+ # - All the internals (internal modules Accessible and Initializable, class
311
+ # ConditionVariable) appear in RDoc. It might be good to hide them, by
312
+ # making them private, or marking them :nodoc:, etc.
313
+ # - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
314
+ # not synchronize.
315
+ # - mon_owner is in Nutshell, but appears as an accessor in a separate module
316
+ # here, so is hard/impossible to RDoc. Some other useful accessors
317
+ # (mon_count and some queue stuff) are also in this module, and don't appear
318
+ # directly in the RDoc output.
319
+ # - in short, it may be worth changing the code layout in this file to make the
320
+ # documentation easier
@@ -0,0 +1,3 @@
1
+ class Monitor
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'lib/monitor/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "monitor"
5
+ spec.version = Monitor::VERSION
6
+ spec.authors = ["Hiroshi SHIBATA"]
7
+ spec.email = ["hsbt@ruby-lang.org"]
8
+
9
+ spec.summary = %q{Provides an object or module to use safely by more than one thread}
10
+ spec.description = %q{Provides an object or module to use safely by more than one thread}
11
+ spec.homepage = "https://github.com/ruby/monitor"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+ spec.license = "BSD-2-Clause"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Hiroshi SHIBATA
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-04-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Provides an object or module to use safely by more than one thread
14
+ email:
15
+ - hsbt@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".github/workflows/test.yml"
21
+ - ".gitignore"
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - bin/console
27
+ - bin/setup
28
+ - lib/monitor.rb
29
+ - lib/monitor/version.rb
30
+ - monitor.gemspec
31
+ homepage: https://github.com/ruby/monitor
32
+ licenses:
33
+ - BSD-2-Clause
34
+ metadata:
35
+ homepage_uri: https://github.com/ruby/monitor
36
+ source_code_uri: https://github.com/ruby/monitor
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 2.3.0
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.2.0.pre1
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Provides an object or module to use safely by more than one thread
56
+ test_files: []