redis-em-mutex 0.1.2 → 0.2.0

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.
@@ -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