redis-em-mutex 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,162 @@
1
+ $:.unshift "lib"
2
+ require 'securerandom'
3
+ require 'em-synchrony'
4
+ require 'em-synchrony/thread'
5
+ require 'redis-em-mutex'
6
+
7
+ describe Redis::EM::Mutex do
8
+
9
+ it "should lock and sleep forever until woken up" do
10
+ begin
11
+ mutex = described_class.lock(*@lock_names)
12
+ mutex.owned?.should be true
13
+ fiber = Fiber.current
14
+ start = Time.now
15
+ ::EM.add_timer(0.25) do
16
+ mutex.wakeup(fiber)
17
+ end
18
+ mutex.sleep.should be_within(0.01).of(0.25)
19
+ (Time.now - start).should be_within(0.01).of(0.25)
20
+ mutex.owned?.should be true
21
+ mutex.unlock!.should be_true
22
+ ensure
23
+ mutex.unlock if mutex
24
+ end
25
+ end
26
+
27
+ it "should raise MutexError on sleep if unlocked" do
28
+ mutex = described_class.new(*@lock_names)
29
+ expect {
30
+ mutex.sleep
31
+ }.to raise_error(Redis::EM::Mutex::MutexError, /can't sleep #{described_class} wasn't locked/)
32
+ end
33
+
34
+ it "should lock and sleep with timeout" do
35
+ begin
36
+ mutex = described_class.lock(*@lock_names)
37
+ mutex.owned?.should be true
38
+ start = Time.now
39
+ mutex.sleep(0.25).should be_within(0.01).of(0.25)
40
+ (Time.now - start).should be_within(0.01).of(0.25)
41
+ mutex.owned?.should be true
42
+ mutex.unlock!.should be_true
43
+ ensure
44
+ mutex.unlock if mutex
45
+ end
46
+ end
47
+
48
+ it "should lock and sleep with timeout but woken up in the middle of a sleep" do
49
+ begin
50
+ mutex = described_class.lock(*@lock_names)
51
+ mutex.owned?.should be true
52
+ fiber = Fiber.current
53
+ start = Time.now
54
+ ::EM.add_timer(0.15) do
55
+ mutex.wakeup(fiber)
56
+ end
57
+ mutex.sleep(0.25).should be_within(0.001).of(0.15)
58
+ (Time.now - start).should be_within(0.001).of(0.15)
59
+ mutex.owned?.should be true
60
+ mutex.unlock!.should be_true
61
+ ensure
62
+ mutex.unlock if mutex
63
+ end
64
+ end
65
+
66
+ it "should lock and sleep and raise MutexTimeout on wakeup" do
67
+ begin
68
+ mutex = described_class.lock(*@lock_names, block: 0)
69
+ mutex.owned?.should be true
70
+ fiber = Fiber.current
71
+ ::EM::Synchrony.next_tick do
72
+ begin
73
+ mutex.owned?.should be false
74
+ mutex.lock.should be true
75
+ mutex.owned?.should be true
76
+ mutex.wakeup(fiber)
77
+ ::EM::Synchrony.sleep(0.2)
78
+ mutex.unlock!.should be_true
79
+ rescue Exception => e
80
+ @exception = e
81
+ mutex.unlock
82
+ end
83
+ end
84
+ start = Time.now
85
+ expect {
86
+ mutex.sleep
87
+ }.to raise_error(Redis::EM::Mutex::MutexTimeout)
88
+ (Time.now - start).should be_within(0.002).of(0.003)
89
+ mutex.owned?.should be false
90
+ mutex.unlock!.should be false
91
+ mutex.block_timeout = nil
92
+ start = Time.now
93
+ mutex.lock.should be true
94
+ (Time.now - start).should be_within(0.01).of(0.2)
95
+ mutex.owned?.should be true
96
+ mutex.unlock!.should be_true
97
+ rescue Exception => e
98
+ ::EM::Synchrony.sleep(0.3)
99
+ raise e
100
+ ensure
101
+ mutex.unlock
102
+ end
103
+ end
104
+
105
+ it "should work with EM::Synchrony::Thread::ConditionVariable" do
106
+ mutex = described_class.new(*@lock_names)
107
+ resource = ::EM::Synchrony::Thread::ConditionVariable.new
108
+ signal = nil
109
+ fiber = Fiber.current
110
+ ::EM::Synchrony.next_tick do
111
+ mutex.synchronize {
112
+ resource.wait(mutex)
113
+ fiber.resume Time.now
114
+ }
115
+ end
116
+ ::EM::Synchrony.next_tick do
117
+ mutex.synchronize {
118
+ ::EM::Synchrony.sleep(0.2)
119
+ resource.signal
120
+ signal = Time.now
121
+ }
122
+ end
123
+ start = Time.now
124
+ now = Fiber.yield
125
+ (now - signal).should be_within(0.001).of(0.001)
126
+ (now - start).should be_within(0.01).of(0.2)
127
+ mutex.synchronize do
128
+ signal = nil
129
+ end
130
+ signal.should be_nil
131
+ end
132
+
133
+ around(:each) do |testcase|
134
+ @after_em_stop = nil
135
+ @exception = nil
136
+ ::EM.synchrony do
137
+ begin
138
+ testcase.call
139
+ raise @exception if @exception
140
+ described_class.stop_watcher
141
+ rescue => e
142
+ described_class.stop_watcher(true)
143
+ raise e
144
+ ensure
145
+ ::EM.stop
146
+ end
147
+ end
148
+ @after_em_stop.call if @after_em_stop
149
+ end
150
+
151
+ before(:all) do
152
+ @redis_options = {:driver => :synchrony}
153
+ described_class.setup @redis_options.merge(size: 4)
154
+ @lock_names = 2.times.map {
155
+ SecureRandom.random_bytes
156
+ }
157
+ end
158
+
159
+ after(:all) do
160
+ # @lock_names
161
+ end
162
+ end
@@ -32,11 +32,11 @@ describe Redis::EM::Mutex do
32
32
  it "should lock and allow locking on the same semaphore name with different namespace" do
33
33
  begin
34
34
  mutex = described_class.lock(*@lock_names)
35
- mutex.locked?.should be_true
36
- mutex.owned?.should be_true
35
+ mutex.locked?.should be true
36
+ mutex.owned?.should be true
37
37
  ns_mutex = described_class.lock(*@lock_names, ns: :MutexLocalTEST)
38
- ns_mutex.locked?.should be_true
39
- ns_mutex.owned?.should be_true
38
+ ns_mutex.locked?.should be true
39
+ ns_mutex.owned?.should be true
40
40
  expect {
41
41
  mutex.lock
42
42
  }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
@@ -44,11 +44,11 @@ describe Redis::EM::Mutex do
44
44
  ns_mutex.lock
45
45
  }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
46
46
  mutex.unlock
47
- mutex.locked?.should be_false
48
- mutex.owned?.should be_false
47
+ mutex.locked?.should be false
48
+ mutex.owned?.should be false
49
49
  ns_mutex.unlock
50
- ns_mutex.locked?.should be_false
51
- ns_mutex.owned?.should be_false
50
+ ns_mutex.locked?.should be false
51
+ ns_mutex.owned?.should be false
52
52
  ensure
53
53
  mutex.unlock if mutex
54
54
  ns_mutex.unlock if ns_mutex
@@ -59,11 +59,11 @@ describe Redis::EM::Mutex do
59
59
  begin
60
60
  ns = described_class::NS.new(:MutexCustomTEST)
61
61
  mutex1 = ns.lock(@lock_names.first)
62
- mutex1.locked?.should be_true
63
- mutex1.owned?.should be_true
62
+ mutex1.locked?.should be true
63
+ mutex1.owned?.should be true
64
64
  mutex2 = ns.lock(@lock_names.last)
65
- mutex2.locked?.should be_true
66
- mutex2.owned?.should be_true
65
+ mutex2.locked?.should be true
66
+ mutex2.owned?.should be true
67
67
  expect {
68
68
  mutex1.lock
69
69
  }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
@@ -71,11 +71,11 @@ describe Redis::EM::Mutex do
71
71
  mutex2.lock
72
72
  }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
73
73
  mutex1.unlock
74
- mutex1.locked?.should be_false
75
- mutex1.owned?.should be_false
74
+ mutex1.locked?.should be false
75
+ mutex1.owned?.should be false
76
76
  mutex2.unlock
77
- mutex2.locked?.should be_false
78
- mutex2.owned?.should be_false
77
+ mutex2.locked?.should be false
78
+ mutex2.owned?.should be false
79
79
  ensure
80
80
  mutex1.unlock if mutex1
81
81
  mutex2.unlock if mutex2
@@ -0,0 +1,156 @@
1
+ $:.unshift "lib"
2
+ require 'securerandom'
3
+ require 'em-synchrony'
4
+ require 'em-synchrony/fiber_iterator'
5
+ require 'redis-em-mutex'
6
+
7
+ describe Redis::EM::Mutex do
8
+
9
+ it "should share a custom owner lock between fibers" do
10
+ begin
11
+ mutex = described_class.lock(*@lock_names, owner: 'my')
12
+ mutex.should be_an_instance_of described_class
13
+ mutex.names.should eq @lock_names
14
+ mutex.locked?.should be true
15
+ mutex.owned?.should be true
16
+ expect {
17
+ mutex.lock
18
+ }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
19
+ fiber = Fiber.current
20
+ ::EM::Synchrony.next_tick do
21
+ begin
22
+ mutex.try_lock.should be false
23
+ mutex.locked?.should be true
24
+ mutex.owned?.should be true
25
+ expect {
26
+ mutex.lock
27
+ }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
28
+ mutex.refresh.should be true
29
+ rescue Exception => e
30
+ @exception = e
31
+ ensure
32
+ ::EM.next_tick { fiber.resume }
33
+ end
34
+ end
35
+ Fiber.yield
36
+ mutex.locked?.should be true
37
+ mutex.owned?.should be true
38
+ mutex.unlock.should be_an_instance_of described_class
39
+ ::EM::Synchrony.next_tick do
40
+ begin
41
+ mutex.locked?.should be false
42
+ mutex.owned?.should be false
43
+ mutex.lock.should be true
44
+ mutex.locked?.should be true
45
+ mutex.owned?.should be true
46
+ expect {
47
+ mutex.lock
48
+ }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
49
+ rescue Exception => e
50
+ @exception = e
51
+ ensure
52
+ ::EM.next_tick { fiber.resume }
53
+ end
54
+ end
55
+ Fiber.yield
56
+ mutex.locked?.should be true
57
+ mutex.owned?.should be true
58
+ expect {
59
+ mutex.lock
60
+ }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
61
+ mutex.unlock.should be_an_instance_of described_class
62
+ mutex.locked?.should be false
63
+ mutex.owned?.should be false
64
+ mutex.try_lock.should be true
65
+ ensure
66
+ mutex.unlock if mutex
67
+ end
68
+ end
69
+
70
+ it "should share custom owner locks concurrently between group of fibers" do
71
+ begin
72
+ mutex1 = described_class.new(*@lock_names, owner: 'my1', block: 0)
73
+ mutex2 = described_class.new(*@lock_names, owner: 'my2', block: 0)
74
+ [mutex1, mutex2].each do |mutex|
75
+ mutex.should be_an_instance_of described_class
76
+ mutex.names.should eq @lock_names
77
+ mutex.locked?.should be false
78
+ mutex.owned?.should be false
79
+ end
80
+ mutex1.lock.should be true
81
+ mutex2.lock.should be false
82
+ mutex1.locked?.should be true
83
+ mutex1.owned?.should be true
84
+ mutex2.locked?.should be true
85
+ mutex2.owned?.should be false
86
+ expect {
87
+ mutex1.lock
88
+ }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
89
+ fiber = Fiber.current
90
+ ::EM::Synchrony.next_tick do
91
+ begin
92
+ mutex1.locked?.should be true
93
+ mutex1.owned?.should be true
94
+ mutex2.locked?.should be true
95
+ mutex2.owned?.should be false
96
+ expect {
97
+ mutex1.lock
98
+ }.to raise_error(Redis::EM::Mutex::MutexError, /deadlock; recursive locking/)
99
+ mutex2.lock.should be false
100
+ mutex1.refresh.should be true
101
+ mutex2.refresh.should be false
102
+ mutex2.block_timeout = nil
103
+ ::EM.next_tick { fiber.resume }
104
+ start = Time.now
105
+ mutex2.lock.should be true
106
+ (Time.now - start).should be_within(0.01).of(0.5)
107
+ rescue Exception => e
108
+ @exception = e
109
+ ensure
110
+ ::EM.next_tick { fiber.resume }
111
+ end
112
+ end
113
+ Fiber.yield
114
+ EM::Synchrony.sleep 0.5
115
+ mutex1.refresh.should be true
116
+ mutex2.refresh.should be false
117
+ mutex1.unlock.should be_an_instance_of described_class
118
+ Fiber.yield
119
+ mutex1.refresh.should be false
120
+ mutex2.refresh.should be true
121
+ ensure
122
+ mutex1.unlock if mutex1
123
+ mutex2.unlock if mutex2
124
+ end
125
+ end
126
+
127
+ around(:each) do |testcase|
128
+ @after_em_stop = nil
129
+ @exception = nil
130
+ ::EM.synchrony do
131
+ begin
132
+ testcase.call
133
+ raise @exception if @exception
134
+ described_class.stop_watcher
135
+ rescue => e
136
+ described_class.stop_watcher(true)
137
+ raise e
138
+ ensure
139
+ ::EM.stop
140
+ end
141
+ end
142
+ @after_em_stop.call if @after_em_stop
143
+ end
144
+
145
+ before(:all) do
146
+ @redis_options = {:driver => :synchrony}
147
+ described_class.setup @redis_options.merge(size: 11)
148
+ @lock_names = 10.times.map {
149
+ SecureRandom.random_bytes
150
+ }
151
+ end
152
+
153
+ after(:all) do
154
+ # @lock_names
155
+ end
156
+ end