tsafe 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -3,12 +3,12 @@ GEM
3
3
  specs:
4
4
  diff-lcs (1.1.3)
5
5
  git (1.2.5)
6
- jeweler (1.8.3)
6
+ jeweler (1.8.4)
7
7
  bundler (~> 1.0)
8
8
  git (>= 1.2.5)
9
9
  rake
10
10
  rdoc
11
- json (1.7.1)
11
+ json (1.7.3)
12
12
  rake (0.9.2.2)
13
13
  rdoc (3.12)
14
14
  json (~> 1.4)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -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
@@ -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
@@ -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
@@ -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.1"
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-05-12}
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.1
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-05-12 00:00:00 +02:00
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: 1960429092658754527
103
+ hash: -2173245037077318773
97
104
  segments:
98
105
  - 0
99
106
  version: "0"