tsafe 0.0.1 → 0.0.2
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.
- data/Gemfile.lock +2 -2
- data/VERSION +1 -1
- data/include/monarray.rb +12 -0
- data/include/monhash.rb +12 -0
- data/include/monitored.rb +29 -0
- data/include/mrswlock.rb +75 -0
- data/include/mutexed.rb +29 -0
- data/include/proxy.rb +29 -0
- data/lib/tsafe.rb +7 -110
- data/spec/mrswlock_spec.rb +83 -0
- data/tsafe.gemspec +9 -2
- metadata +10 -3
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/include/monarray.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#Predefined synchronized array.
|
2
|
+
#
|
3
|
+
#===Examples
|
4
|
+
# arr = Tsafe::MonArray.new
|
5
|
+
# arr << 5
|
6
|
+
# ret = arr[0]
|
7
|
+
class Tsafe::MonArray < ::Array
|
8
|
+
@@tsafe_rwmutex_w_methods = [:<<, :collect, :collect!, :compact!, :delete, :delete_at, :delete_if, :drop, :drop_while, :fill, :flatten!, :insert, :keep_if, :map, :map!, :replace, :shuffle!, :slice!, :shift, :sort!, :sort_by!, :unshift]
|
9
|
+
@@tsafe_rwmutex_r_methods = [:each, :each_index, :take_while]
|
10
|
+
|
11
|
+
include Tsafe::SynModule
|
12
|
+
end
|
data/include/monhash.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#Predefined synchronized hash.
|
2
|
+
#
|
3
|
+
#===Examples
|
4
|
+
# h = Tsafe::MonHash.new
|
5
|
+
# h['test'] = 'trala'
|
6
|
+
# ret = h['test']
|
7
|
+
class Tsafe::MonHash < ::Hash
|
8
|
+
@@tsafe_rwmutex_w_methods = [:[]=, :clear, :delete, :delete_if, :keep_if, :merge!, :rehash, :reject!, :replace, :select!, :shift, :store, :update, :values_at]
|
9
|
+
@@tsafe_rwmutex_r_methods = [:each, :each_key, :each_pair, :each_value]
|
10
|
+
|
11
|
+
include Tsafe::Mrswlock::SynModule
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#This module can be included on a class to make all method-calls synchronized (by using monitor). Examples with array and hash are below.
|
2
|
+
#
|
3
|
+
#===Examples
|
4
|
+
# class MySyncedClass < SomeOtherClassThatNeedsToBeSynchronized
|
5
|
+
# include Tsafe::Monitored
|
6
|
+
# end
|
7
|
+
module Tsafe::Monitored
|
8
|
+
def self.included(base)
|
9
|
+
base.to_s.split("::").inject(Object, :const_get).class_eval do
|
10
|
+
self.instance_methods.each do |method_name|
|
11
|
+
#These two methods create warnings under JRuby.
|
12
|
+
if RUBY_ENGINE == "jruby"
|
13
|
+
next if method_name == :instance_exec or method_name == :instance_eval
|
14
|
+
end
|
15
|
+
|
16
|
+
new_method_name = "_ts_#{method_name}"
|
17
|
+
alias_method(new_method_name, method_name)
|
18
|
+
|
19
|
+
define_method method_name do |*args, &block|
|
20
|
+
#Need to use monitor, since the internal calls might have to run not-synchronized, and we have just overwritten the internal methods.
|
21
|
+
@_ts_mutex = Monitor.new if !@_ts_mutex
|
22
|
+
@_ts_mutex.synchronize do
|
23
|
+
return self._ts___send__(new_method_name, *args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/include/mrswlock.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#A class that allows many simultanious read-synchronizations but locks both reading and writing while calling the write-synchronzie-method.
|
2
|
+
class Tsafe::Mrswlock
|
3
|
+
@@debug = false
|
4
|
+
|
5
|
+
#Sets various variables.
|
6
|
+
def initialize
|
7
|
+
@reads = 0
|
8
|
+
@w_mutex = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
#Runs the given block through the read-synchronization.
|
12
|
+
def rsync
|
13
|
+
begin
|
14
|
+
while @w_mutex.locked?
|
15
|
+
Thread.pass
|
16
|
+
print "Passed because lock.\n" if @@debug
|
17
|
+
end
|
18
|
+
|
19
|
+
@reads += 1
|
20
|
+
print "Reading more than one at a time! (#{@reads})\n" if @@debug and @reads >= 2
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
@reads -= 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#Runs the given block through the write-synchronization (locks both reading and writing).
|
28
|
+
def wsync
|
29
|
+
@w_mutex.synchronize do
|
30
|
+
#Wait for any reads to finish that might have started while we were getting the lock.
|
31
|
+
while @reads > 0
|
32
|
+
Thread.pass
|
33
|
+
print "Passed because reading.\n" if @@debug
|
34
|
+
end
|
35
|
+
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module SynModule
|
41
|
+
def self.included(base)
|
42
|
+
base.to_s.split("::").inject(Object, :const_get).class_eval do
|
43
|
+
#Rename initialize.
|
44
|
+
alias_method(:initialize_rwmutex, :initialize)
|
45
|
+
|
46
|
+
define_method(:initialize) do |*args, &block|
|
47
|
+
@tsafe_rwmutex = Tsafe::Mrswlock.new
|
48
|
+
return initialize_rwmutex(*args, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
base.class_variable_get(:@@tsafe_rwmutex_r_methods).each do |mname|
|
52
|
+
newmname = "tsafe_rwmutex_#{mname}".to_sym
|
53
|
+
alias_method(newmname, mname)
|
54
|
+
|
55
|
+
define_method(mname) do |*args, &block|
|
56
|
+
@tsafe_rwmutex.rsync do
|
57
|
+
return self.__send__(newmname, *args, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
base.class_variable_get(:@@tsafe_rwmutex_w_methods).each do |mname|
|
63
|
+
newmname = "tsafe_rwmutex_#{mname}".to_sym
|
64
|
+
alias_method(newmname, mname)
|
65
|
+
|
66
|
+
define_method(mname) do |*args, &block|
|
67
|
+
@tsafe_rwmutex.wsync do
|
68
|
+
return self.__send__(newmname, *args, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/include/mutexed.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#This module can be included on a class to make all method-calls synchronized (by using mutex). Examples with array and hash are below.
|
2
|
+
#
|
3
|
+
#===Examples
|
4
|
+
# class MySyncedClass < SomeOtherClassThatNeedsToBeSynchronized
|
5
|
+
# include Tsafe::Mutexed
|
6
|
+
# end
|
7
|
+
module Tsafe::Mutexed
|
8
|
+
def self.included(base)
|
9
|
+
base.to_s.split("::").inject(Object, :const_get).class_eval do
|
10
|
+
self.instance_methods.each do |method_name|
|
11
|
+
#These two methods create warnings under JRuby.
|
12
|
+
if RUBY_ENGINE == "jruby"
|
13
|
+
next if method_name == :instance_exec or method_name == :instance_eval
|
14
|
+
end
|
15
|
+
|
16
|
+
new_method_name = "_ts_#{method_name}"
|
17
|
+
alias_method(new_method_name, method_name)
|
18
|
+
|
19
|
+
define_method method_name do |*args, &block|
|
20
|
+
#Need to use monitor, since the internal calls might have to run not-synchronized, and we have just overwritten the internal methods.
|
21
|
+
@_ts_mutex = Mutex.new if !@_ts_mutex
|
22
|
+
@_ts_mutex.synchronize do
|
23
|
+
return self._ts___send__(new_method_name, *args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/include/proxy.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#Instances of this class proxies calls to a given-object by using a mutex or monitor.
|
2
|
+
#
|
3
|
+
#==== Examples
|
4
|
+
# threadsafe_array = Tsafe::Proxy.new(:obj => [])
|
5
|
+
# threadsafe_array << 5
|
6
|
+
# ret = threadsafe_array[0]
|
7
|
+
#
|
8
|
+
# threadsafe_array = Tsafe::Proxy.new(:obj => [], :monitor => true)
|
9
|
+
class Tsafe::Proxy
|
10
|
+
#Spawn needed vars.
|
11
|
+
def initialize(args)
|
12
|
+
if args[:monitor]
|
13
|
+
@mutex = Monitor.new
|
14
|
+
elsif args[:mutex]
|
15
|
+
@mutex = args[:mutex]
|
16
|
+
else
|
17
|
+
@mutex = Mutex.new
|
18
|
+
end
|
19
|
+
|
20
|
+
@obj = args[:obj]
|
21
|
+
end
|
22
|
+
|
23
|
+
#Proxies all calls to this object through the mutex.
|
24
|
+
def method_missing(method_name, *args, &block)
|
25
|
+
@mutex.synchronize do
|
26
|
+
@obj.__send__(method_name, *args, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/tsafe.rb
CHANGED
@@ -2,119 +2,16 @@ require "monitor"
|
|
2
2
|
|
3
3
|
#This module contains various tools to handle thread-safety easily and pretty.
|
4
4
|
module Tsafe
|
5
|
+
#Autoloader for subclasses.
|
6
|
+
def self.const_missing(name)
|
7
|
+
require "#{File.dirname(__FILE__)}/../include/#{name.to_s.downcase}.rb"
|
8
|
+
raise "Still not loaded: '#{name}'." if !Tsafe.const_defined?(name)
|
9
|
+
return Tsafe.const_get(name)
|
10
|
+
end
|
11
|
+
|
5
12
|
#JRuby can corrupt an array in a threadded env. Use this method to only get a synchronized array when running JRuby and not having to write "if RUBY_ENGINE"-stuff.
|
6
13
|
def self.std_array
|
7
14
|
return MonArray.new if RUBY_ENGINE == "jruby"
|
8
15
|
return []
|
9
16
|
end
|
10
|
-
|
11
|
-
#Instances of this class proxies calls to a given-object by using a mutex or monitor.
|
12
|
-
#
|
13
|
-
#==== Examples
|
14
|
-
# threadsafe_array = Tsafe::Proxy.new(:obj => [])
|
15
|
-
# threadsafe_array << 5
|
16
|
-
# ret = threadsafe_array[0]
|
17
|
-
#
|
18
|
-
# threadsafe_array = Tsafe::Proxy.new(:obj => [], :monitor => true)
|
19
|
-
class Proxy
|
20
|
-
#Spawn needed vars.
|
21
|
-
def initialize(args)
|
22
|
-
if args[:monitor]
|
23
|
-
@mutex = Monitor.new
|
24
|
-
elsif args[:mutex]
|
25
|
-
@mutex = args[:mutex]
|
26
|
-
else
|
27
|
-
@mutex = Mutex.new
|
28
|
-
end
|
29
|
-
|
30
|
-
@obj = args[:obj]
|
31
|
-
end
|
32
|
-
|
33
|
-
#Proxies all calls to this object through the mutex.
|
34
|
-
def method_missing(method_name, *args, &block)
|
35
|
-
@mutex.synchronize do
|
36
|
-
@obj.__send__(method_name, *args, &block)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
#This module can be included on a class to make all method-calls synchronized (by using monitor). Examples with array and hash are below.
|
42
|
-
#
|
43
|
-
#===Examples
|
44
|
-
# class MySyncedClass < SomeOtherClassThatNeedsToBeSynchronized
|
45
|
-
# include Tsafe::Monitored
|
46
|
-
# end
|
47
|
-
module Monitored
|
48
|
-
def self.included(base)
|
49
|
-
base.to_s.split("::").inject(Object, :const_get).class_eval do
|
50
|
-
self.instance_methods.each do |method_name|
|
51
|
-
#These two methods create warnings under JRuby.
|
52
|
-
if RUBY_ENGINE == "jruby"
|
53
|
-
next if method_name == :instance_exec or method_name == :instance_eval
|
54
|
-
end
|
55
|
-
|
56
|
-
new_method_name = "_ts_#{method_name}"
|
57
|
-
alias_method(new_method_name, method_name)
|
58
|
-
|
59
|
-
define_method method_name do |*args, &block|
|
60
|
-
#Need to use monitor, since the internal calls might have to run not-synchronized, and we have just overwritten the internal methods.
|
61
|
-
@_ts_mutex = Monitor.new if !@_ts_mutex
|
62
|
-
@_ts_mutex.synchronize do
|
63
|
-
return self._ts___send__(new_method_name, *args, &block)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
#This module can be included on a class to make all method-calls synchronized (by using mutex). Examples with array and hash are below.
|
72
|
-
#
|
73
|
-
#===Examples
|
74
|
-
# class MySyncedClass < SomeOtherClassThatNeedsToBeSynchronized
|
75
|
-
# include Tsafe::Mutexed
|
76
|
-
# end
|
77
|
-
module Mutexed
|
78
|
-
def self.included(base)
|
79
|
-
base.to_s.split("::").inject(Object, :const_get).class_eval do
|
80
|
-
self.instance_methods.each do |method_name|
|
81
|
-
#These two methods create warnings under JRuby.
|
82
|
-
if RUBY_ENGINE == "jruby"
|
83
|
-
next if method_name == :instance_exec or method_name == :instance_eval
|
84
|
-
end
|
85
|
-
|
86
|
-
new_method_name = "_ts_#{method_name}"
|
87
|
-
alias_method(new_method_name, method_name)
|
88
|
-
|
89
|
-
define_method method_name do |*args, &block|
|
90
|
-
#Need to use monitor, since the internal calls might have to run not-synchronized, and we have just overwritten the internal methods.
|
91
|
-
@_ts_mutex = Mutex.new if !@_ts_mutex
|
92
|
-
@_ts_mutex.synchronize do
|
93
|
-
return self._ts___send__(new_method_name, *args, &block)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
#Predefined synchronized array.
|
102
|
-
#
|
103
|
-
#===Examples
|
104
|
-
# arr = Tsafe::MonArray.new
|
105
|
-
# arr << 5
|
106
|
-
# ret = arr[0]
|
107
|
-
class MonArray < ::Array
|
108
|
-
include Monitored
|
109
|
-
end
|
110
|
-
|
111
|
-
#Predefined synchronized hash.
|
112
|
-
#
|
113
|
-
#===Examples
|
114
|
-
# h = Tsafe::MonHash.new
|
115
|
-
# h['test'] = 'trala'
|
116
|
-
# ret = h['test']
|
117
|
-
class MonHash < ::Hash
|
118
|
-
include Monitored
|
119
|
-
end
|
120
17
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Tsafe::Rwmutex" do
|
4
|
+
it "should work!" do
|
5
|
+
Thread.abort_on_exception = true
|
6
|
+
debug = false
|
7
|
+
|
8
|
+
print "Setting initial values.\n" if debug
|
9
|
+
hash = Tsafe::MonHash.new
|
10
|
+
0.upto(15) do |count|
|
11
|
+
realcount = 100000000 - count
|
12
|
+
hash[realcount] = realcount
|
13
|
+
end
|
14
|
+
|
15
|
+
ts = []
|
16
|
+
|
17
|
+
1.upto(20) do |tcount|
|
18
|
+
print "Starting thread #{tcount}\n" if debug
|
19
|
+
ts << Thread.new do
|
20
|
+
1.upto(10000) do |count|
|
21
|
+
hash[count] = count
|
22
|
+
|
23
|
+
hash[count]
|
24
|
+
hash.key?(count)
|
25
|
+
|
26
|
+
hash.delete(count)
|
27
|
+
|
28
|
+
hash.each do |key, val|
|
29
|
+
#nothing...
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ts.each do |t|
|
36
|
+
print "Joining #{t.__id__}\n" if debug
|
37
|
+
t.join
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should work!" do
|
42
|
+
debug = false
|
43
|
+
|
44
|
+
hash = {}
|
45
|
+
0.upto(15) do |count|
|
46
|
+
realcount = 100000000 - count
|
47
|
+
hash[realcount] = realcount
|
48
|
+
end
|
49
|
+
|
50
|
+
rwm = Tsafe::Mrswlock.new
|
51
|
+
ts = []
|
52
|
+
|
53
|
+
1.upto(20) do
|
54
|
+
ts << Thread.new do
|
55
|
+
1.upto(10000) do |count|
|
56
|
+
rwm.wsync do
|
57
|
+
hash[count] = count
|
58
|
+
end
|
59
|
+
|
60
|
+
rwm.rsync do
|
61
|
+
hash[count]
|
62
|
+
hash.key?(count)
|
63
|
+
end
|
64
|
+
|
65
|
+
rwm.wsync do
|
66
|
+
hash.delete(count)
|
67
|
+
end
|
68
|
+
|
69
|
+
rwm.rsync do
|
70
|
+
hash.each do |key, val|
|
71
|
+
#nothing...
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
ts.each do |t|
|
79
|
+
print "Joining #{t.__id__}\n" if debug
|
80
|
+
t.join
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/tsafe.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{tsafe}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kasper Johansen"]
|
12
|
-
s.date = %q{2012-
|
12
|
+
s.date = %q{2012-07-25}
|
13
13
|
s.description = %q{Proxy-objects for making another object threadsafe by proxying calls through mutex and method_missing. Monitored array and hash where all methods are going through monitor. Threadsafe class for including into a class that extends another class in order to make it threadsafe.}
|
14
14
|
s.email = %q{k@spernj.org}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,7 +25,14 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.rdoc",
|
26
26
|
"Rakefile",
|
27
27
|
"VERSION",
|
28
|
+
"include/monarray.rb",
|
29
|
+
"include/monhash.rb",
|
30
|
+
"include/monitored.rb",
|
31
|
+
"include/mrswlock.rb",
|
32
|
+
"include/mutexed.rb",
|
33
|
+
"include/proxy.rb",
|
28
34
|
"lib/tsafe.rb",
|
35
|
+
"spec/mrswlock_spec.rb",
|
29
36
|
"spec/spec_helper.rb",
|
30
37
|
"spec/tsafe_spec.rb",
|
31
38
|
"tsafe.gemspec"
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: tsafe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Kasper Johansen
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-07-25 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -75,7 +75,14 @@ files:
|
|
75
75
|
- README.rdoc
|
76
76
|
- Rakefile
|
77
77
|
- VERSION
|
78
|
+
- include/monarray.rb
|
79
|
+
- include/monhash.rb
|
80
|
+
- include/monitored.rb
|
81
|
+
- include/mrswlock.rb
|
82
|
+
- include/mutexed.rb
|
83
|
+
- include/proxy.rb
|
78
84
|
- lib/tsafe.rb
|
85
|
+
- spec/mrswlock_spec.rb
|
79
86
|
- spec/spec_helper.rb
|
80
87
|
- spec/tsafe_spec.rb
|
81
88
|
- tsafe.gemspec
|
@@ -93,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
100
|
requirements:
|
94
101
|
- - ">="
|
95
102
|
- !ruby/object:Gem::Version
|
96
|
-
hash:
|
103
|
+
hash: -2173245037077318773
|
97
104
|
segments:
|
98
105
|
- 0
|
99
106
|
version: "0"
|