pax_global_header00006660000000000000000000000064136172444760014530gustar00rootroot0000000000000052 comment=64e2d386c410b1b6f3c37340baf6837d31ec45d8 ruby-sync-0.5.0/000077500000000000000000000000001361724447600134655ustar00rootroot00000000000000ruby-sync-0.5.0/.gitignore000066400000000000000000000001111361724447600154460ustar00rootroot00000000000000/.bundle/ /.yardoc /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ ruby-sync-0.5.0/.travis.yml000066400000000000000000000001301361724447600155700ustar00rootroot00000000000000sudo: false language: ruby rvm: - 2.6.0 before_install: gem install bundler -v 1.16.2 ruby-sync-0.5.0/Gemfile000066400000000000000000000001551361724447600147610ustar00rootroot00000000000000source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gemspec ruby-sync-0.5.0/LICENSE.txt000066400000000000000000000024021361724447600153060ustar00rootroot00000000000000Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ruby-sync-0.5.0/README.md000066400000000000000000000030651361724447600147500ustar00rootroot00000000000000# Sync A module that provides a two-phase lock with a counter. ## Installation Add this line to your application's Gemfile: ```ruby gem 'sync' ``` And then execute: $ bundle Or install it yourself as: $ gem install sync ## Usage ### Sync_m, Synchronizer_m ``` obj.extend(Sync_m) ``` or ``` class Foo include Sync_m : end ``` ``` Sync_m#sync_mode Sync_m#sync_locked?, locked? Sync_m#sync_shared?, shared? Sync_m#sync_exclusive?, sync_exclusive? Sync_m#sync_try_lock, try_lock Sync_m#sync_lock, lock Sync_m#sync_unlock, unlock ``` ### Sync, Synchronizer: ``` sync = Sync.new ``` ``` Sync#mode Sync#locked? Sync#shared? Sync#exclusive? Sync#try_lock(mode) -- mode = :EX, :SH, :UN Sync#lock(mode) -- mode = :EX, :SH, :UN Sync#unlock Sync#synchronize(mode) {...} ``` ## Development 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. 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). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/sync. ## License The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause). ruby-sync-0.5.0/Rakefile000066400000000000000000000003061361724447600151310ustar00rootroot00000000000000require "bundler/gem_tasks" require "rake/testtask" Rake::TestTask.new(:test) do |t| t.libs << "test" t.libs << "lib" t.test_files = FileList["test/**/test_*.rb"] end task :default => :test ruby-sync-0.5.0/bin/000077500000000000000000000000001361724447600142355ustar00rootroot00000000000000ruby-sync-0.5.0/bin/console000077500000000000000000000001571361724447600156300ustar00rootroot00000000000000#!/usr/bin/env ruby require "bundler/setup" require_relative "../lib/sync" require "irb" IRB.start(__FILE__) ruby-sync-0.5.0/bin/setup000077500000000000000000000001121361724447600153150ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install ruby-sync-0.5.0/lib/000077500000000000000000000000001361724447600142335ustar00rootroot00000000000000ruby-sync-0.5.0/lib/sync.rb000066400000000000000000000163471361724447600155470ustar00rootroot00000000000000# frozen_string_literal: false # # sync.rb - 2 phase lock with counter # $Release Version: 1.0$ # $Revision$ # by Keiju ISHITSUKA(keiju@ishitsuka.com) # # -- # Sync_m, Synchronizer_m # Usage: # obj.extend(Sync_m) # or # class Foo # include Sync_m # : # end # # Sync_m#sync_mode # Sync_m#sync_locked?, locked? # Sync_m#sync_shared?, shared? # Sync_m#sync_exclusive?, sync_exclusive? # Sync_m#sync_try_lock, try_lock # Sync_m#sync_lock, lock # Sync_m#sync_unlock, unlock # # Sync, Synchronizer: # Usage: # sync = Sync.new # # Sync#mode # Sync#locked? # Sync#shared? # Sync#exclusive? # Sync#try_lock(mode) -- mode = :EX, :SH, :UN # Sync#lock(mode) -- mode = :EX, :SH, :UN # Sync#unlock # Sync#synchronize(mode) {...} # # ## # A module that provides a two-phase lock with a counter. module Sync_m # lock mode UN = :UN SH = :SH EX = :EX # exceptions class Err < StandardError def Err.Fail(*opt) fail self, sprintf(self::Message, *opt) end class UnknownLocker < Err Message = "Thread(%s) not locked." def UnknownLocker.Fail(th) super(th.inspect) end end class LockModeFailer < Err Message = "Unknown lock mode(%s)" def LockModeFailer.Fail(mode) if mode.id2name mode = mode.id2name end super(mode) end end end def Sync_m.define_aliases(cl) cl.module_eval %q{ alias locked? sync_locked? alias shared? sync_shared? alias exclusive? sync_exclusive? alias lock sync_lock alias unlock sync_unlock alias try_lock sync_try_lock alias synchronize sync_synchronize } end def Sync_m.append_features(cl) super # do nothing for Modules # make aliases for Classes. define_aliases(cl) unless cl.instance_of?(Module) self end def Sync_m.extend_object(obj) super obj.sync_extend end def sync_extend unless (defined? locked? and defined? shared? and defined? exclusive? and defined? lock and defined? unlock and defined? try_lock and defined? synchronize) Sync_m.define_aliases(singleton_class) end sync_initialize end # accessing def sync_locked? sync_mode != UN end def sync_shared? sync_mode == SH end def sync_exclusive? sync_mode == EX end # locking methods. def sync_try_lock(mode = EX) return unlock if mode == UN @sync_mutex.synchronize do sync_try_lock_sub(mode) end end def sync_lock(m = EX) return unlock if m == UN Thread.handle_interrupt(StandardError => :on_blocking) do while true @sync_mutex.synchronize do begin if sync_try_lock_sub(m) return self else if sync_sh_locker[Thread.current] sync_upgrade_waiting.push [Thread.current, sync_sh_locker[Thread.current]] sync_sh_locker.delete(Thread.current) else unless sync_waiting.include?(Thread.current) || sync_upgrade_waiting.reverse_each.any?{|w| w.first == Thread.current } sync_waiting.push Thread.current end end @sync_mutex.sleep end ensure sync_waiting.delete(Thread.current) end end end end self end def sync_unlock(m = EX) wakeup_threads = [] @sync_mutex.synchronize do if sync_mode == UN Err::UnknownLocker.Fail(Thread.current) end m = sync_mode if m == EX and sync_mode == SH runnable = false case m when UN Err::UnknownLocker.Fail(Thread.current) when EX if sync_ex_locker == Thread.current if (self.sync_ex_count = sync_ex_count - 1) == 0 self.sync_ex_locker = nil if sync_sh_locker.include?(Thread.current) self.sync_mode = SH else self.sync_mode = UN end runnable = true end else Err::UnknownLocker.Fail(Thread.current) end when SH if (count = sync_sh_locker[Thread.current]).nil? Err::UnknownLocker.Fail(Thread.current) else if (sync_sh_locker[Thread.current] = count - 1) == 0 sync_sh_locker.delete(Thread.current) if sync_sh_locker.empty? and sync_ex_count == 0 self.sync_mode = UN runnable = true end end end end if runnable if sync_upgrade_waiting.size > 0 th, count = sync_upgrade_waiting.shift sync_sh_locker[th] = count th.wakeup wakeup_threads.push th else wait = sync_waiting self.sync_waiting = [] for th in wait th.wakeup wakeup_threads.push th end end end end for th in wakeup_threads th.run end self end def sync_synchronize(mode = EX) Thread.handle_interrupt(StandardError => :on_blocking) do sync_lock(mode) begin yield ensure sync_unlock end end end attr_accessor :sync_mode attr_accessor :sync_waiting attr_accessor :sync_upgrade_waiting attr_accessor :sync_sh_locker attr_accessor :sync_ex_locker attr_accessor :sync_ex_count def sync_inspect sync_iv = instance_variables.select{|iv| /^@sync_/ =~ iv.id2name}.collect{|iv| iv.id2name + '=' + instance_eval(iv.id2name).inspect}.join(",") print "<#{self.class}.extend Sync_m: #{inspect}, " end private def sync_initialize @sync_mode = UN @sync_waiting = [] @sync_upgrade_waiting = [] @sync_sh_locker = Hash.new @sync_ex_locker = nil @sync_ex_count = 0 @sync_mutex = Thread::Mutex.new end def initialize(*args) super sync_initialize end def sync_try_lock_sub(m) case m when SH case sync_mode when UN self.sync_mode = m sync_sh_locker[Thread.current] = 1 ret = true when SH count = 0 unless count = sync_sh_locker[Thread.current] sync_sh_locker[Thread.current] = count + 1 ret = true when EX # in EX mode, lock will upgrade to EX lock if sync_ex_locker == Thread.current self.sync_ex_count = sync_ex_count + 1 ret = true else ret = false end end when EX if sync_mode == UN or sync_mode == SH && sync_sh_locker.size == 1 && sync_sh_locker.include?(Thread.current) self.sync_mode = m self.sync_ex_locker = Thread.current self.sync_ex_count = 1 ret = true elsif sync_mode == EX && sync_ex_locker == Thread.current self.sync_ex_count = sync_ex_count + 1 ret = true else ret = false end else Err::LockModeFailer.Fail m end return ret end end ## # An alias for Sync_m from sync.rb Synchronizer_m = Sync_m ## # A class that provides two-phase lock with a counter. See Sync_m for # details. class Sync VERSION = "0.5.0" include Sync_m end ## # An alias for Sync from sync.rb. See Sync_m for details. Synchronizer = Sync ruby-sync-0.5.0/sync.gemspec000066400000000000000000000017451361724447600160150ustar00rootroot00000000000000begin require_relative "lib/sync" rescue LoadError # for Ruby core repository require_relative "sync" end Gem::Specification.new do |spec| spec.name = "sync" spec.version = Sync::VERSION spec.authors = ["Keiju ISHITSUKA"] spec.email = ["keiju@ruby-lang.org"] spec.summary = %q{A module that provides a two-phase lock with a counter.} spec.description = %q{A module that provides a two-phase lock with a counter.} spec.homepage = "https://github.com/ruby/sync" spec.license = "BSD-2-Clause" spec.files = [".gitignore", ".travis.yml", "Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "lib/sync.rb", "sync.gemspec"] spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] spec.add_development_dependency "bundler" spec.add_development_dependency "rake" spec.add_development_dependency "test-unit" end ruby-sync-0.5.0/test/000077500000000000000000000000001361724447600144445ustar00rootroot00000000000000ruby-sync-0.5.0/test/test_sync.rb000066400000000000000000000025561361724447600170140ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require 'sync' require 'timeout' class SyncTest < Test::Unit::TestCase class Tester include Sync_m end def test_sync_lock_and_wakeup tester = Tester.new tester.sync_lock(:EX) t = Thread.new { tester.sync_lock(:EX) } sleep 0.1 until t.stop? t.wakeup sleep 0.1 until t.stop? assert_equal(tester.sync_waiting.uniq, tester.sync_waiting) ensure t.kill t.join end def test_sync_upgrade_and_wakeup tester = Tester.new tester.sync_lock(:SH) t = Thread.new do tester.sync_lock(:SH) tester.sync_lock(:EX) end sleep 0.1 until t.stop? t.wakeup sleep 0.1 until t.stop? tester.sync_upgrade_waiting.each { |ary| assert(!tester.sync_waiting.include?(ary[0])) } assert_equal(tester.sync_waiting.uniq, tester.sync_waiting) assert_equal(tester.sync_waiting, []) ensure t.kill t.join end def test_sync_lock_and_raise tester= Tester.new tester.sync_lock(:EX) t = Thread.new { assert_raise(RuntimeError) { tester.sync_lock(:EX) } } sleep 0.1 until t.stop? sleep 1 if RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait t.raise t.join assert_equal(tester.sync_waiting.uniq, tester.sync_waiting) assert_equal(tester.sync_waiting, []) end end