abstractivator 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/abstractivator/fiber_defer.rb +79 -17
- data/lib/abstractivator/version.rb +1 -1
- data/spec/lib/abstractivator/fiber_defer_spec.rb +168 -13
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58e09417a37a0401715ae76dd45139fe0b5cba42
|
4
|
+
data.tar.gz: f7d97a9ff2a8b887022ab5bf80f05f35f7c2f97a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0982af424cdd32e91469cadbbb54de940b9872c09de0a2170af1896fe856d5474d4e59ee402a47005e1e11223d5b70764292712c301fe2672f8b4a7693208ddb
|
7
|
+
data.tar.gz: 8baad053d8a4dcec57c37ebb2ecc50bb8d1179402ac105989160fda70cf6fcfc8ac18012a4ddfed68397c87bb1a7e07272e9e960ba89f17da2db3a81bebd9c32
|
@@ -7,17 +7,92 @@ rescue => e
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module Abstractivator
|
10
|
+
|
11
|
+
# Provides a pair of functions for handling long-running requests in a thread pool.
|
12
|
+
# Uses fibers to maintain a somewhat normal coding style (hides explicit continuations).
|
13
|
+
# with_fiber_defer defines the lexical scope of all work being done.
|
14
|
+
# fiber_defer defines the portion of the work to be done in the worker thread.
|
15
|
+
# Control passes from the calling thread, to the worker thread, and back to the calling thread.
|
16
|
+
# The code is capable of propagating thread variables (e.g., Mongoid::Threaded.database_override)
|
17
|
+
# across these thread/fiber transitions.
|
18
|
+
# See EventMachine::defer for more information.
|
10
19
|
module FiberDefer
|
11
20
|
ROOT_FIBER = Fiber.current
|
12
21
|
|
13
|
-
def with_fiber_defer(&block)
|
22
|
+
def with_fiber_defer(thread_var_guard=nil, &block)
|
14
23
|
raise 'this method requires an eventmachine reactor to be running' unless EM.reactor_running?
|
15
|
-
|
24
|
+
raise 'with_fiber_defer cannot be nested within with_fiber_defer' if Thread.current[:fiber_defer_guard_proc]
|
25
|
+
raise 'with_fiber_defer cannot be nested within fiber_defer' if Thread.current[:inside_fiber_defer]
|
26
|
+
return unless block
|
27
|
+
guard_proc = make_guard_proc(thread_var_guard)
|
28
|
+
f = Fiber.new do
|
29
|
+
guard_proc.call
|
30
|
+
begin
|
31
|
+
Thread.current[:fiber_defer_guard_proc] = guard_proc # make available to fiber_defer calls
|
32
|
+
block.call
|
33
|
+
ensure
|
34
|
+
Thread.current[:fiber_defer_guard_proc] = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
f.resume
|
16
38
|
end
|
17
39
|
|
18
|
-
def fiber_defer(&action)
|
19
|
-
|
40
|
+
def fiber_defer(thread_var_guard=nil, &action)
|
41
|
+
# we start out in the caller thread
|
42
|
+
inherited_guard_proc = Thread.current[:fiber_defer_guard_proc]
|
43
|
+
raise 'fiber_defer cannot be nested within fiber_defer' if Thread.current[:inside_fiber_defer]
|
44
|
+
raise 'fiber_defer must be called within a with_fiber_defer block' unless inherited_guard_proc
|
20
45
|
raise 'fiber_defer must be passed an action to defer (the block)' unless action
|
46
|
+
local_guard_proc = make_guard_proc(thread_var_guard)
|
47
|
+
guard_proc = proc do
|
48
|
+
inherited_guard_proc.call
|
49
|
+
local_guard_proc.call
|
50
|
+
end
|
51
|
+
begin
|
52
|
+
basic_fiber_defer do
|
53
|
+
# in the background thread now
|
54
|
+
begin
|
55
|
+
Thread.current[:inside_fiber_defer] = true
|
56
|
+
guard_proc.call
|
57
|
+
action.call
|
58
|
+
ensure
|
59
|
+
Thread.current[:inside_fiber_defer] = false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
ensure
|
63
|
+
# back in the caller thread
|
64
|
+
guard_proc.call
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def mongoid_fiber_defer(&action)
|
69
|
+
thread_vars = {
|
70
|
+
Mongoid::Threaded.database_override => proc { |db| Mongoid.override_database(db) }
|
71
|
+
}
|
72
|
+
fiber_defer(thread_vars, &action)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def make_guard_proc(x)
|
78
|
+
case x
|
79
|
+
when Proc
|
80
|
+
x
|
81
|
+
when Hash
|
82
|
+
proc do
|
83
|
+
x.each do |value, setter|
|
84
|
+
setter.call(value)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
when nil
|
88
|
+
proc { }
|
89
|
+
else
|
90
|
+
raise "Cannot turn #{x.inspect} into a guard proc"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def basic_fiber_defer(&action)
|
95
|
+
f = Fiber.current
|
21
96
|
raise 'fiber_defer must be called within a with_fiber_defer block' if f == ROOT_FIBER
|
22
97
|
|
23
98
|
safe_action = proc do
|
@@ -33,18 +108,5 @@ module Abstractivator
|
|
33
108
|
raise error if error
|
34
109
|
result
|
35
110
|
end
|
36
|
-
|
37
|
-
def mongoid_fiber_defer(&action)
|
38
|
-
db = Mongoid::Threaded.database_override
|
39
|
-
begin
|
40
|
-
fiber_defer do
|
41
|
-
# in the background thread
|
42
|
-
Mongoid.override_database(db) # set the db to what it was in the main thread
|
43
|
-
action.call
|
44
|
-
end
|
45
|
-
ensure
|
46
|
-
Mongoid.override_database(db) # main thread has moved on before we resume here. restore the db override.
|
47
|
-
end
|
48
|
-
end
|
49
111
|
end
|
50
112
|
end
|
@@ -7,38 +7,90 @@ describe Abstractivator::FiberDefer do
|
|
7
7
|
include Abstractivator::FiberDefer
|
8
8
|
|
9
9
|
describe '#with_fiber_defer' do
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
it 'raises an error when an eventmachine reactor is not running' do
|
11
|
+
expect{with_fiber_defer}.to raise_error /reactor/
|
12
|
+
end
|
13
|
+
it 'does nothing when no block is provided' do
|
14
|
+
EM.run do
|
15
|
+
with_fiber_defer
|
16
|
+
EM.stop
|
17
|
+
end
|
18
|
+
end
|
19
|
+
it 'calls the block' do
|
20
|
+
EM.run do
|
21
|
+
expect{|b| with_fiber_defer(&b)}.to yield_control
|
22
|
+
EM.stop
|
13
23
|
end
|
14
24
|
end
|
15
|
-
context 'when
|
16
|
-
it '
|
25
|
+
context 'when a proc guard is provided' do
|
26
|
+
it 'invokes the guard upon entering the block' do
|
27
|
+
called_it = false
|
28
|
+
guard = proc { called_it = true }
|
17
29
|
EM.run do
|
18
|
-
|
19
|
-
|
30
|
+
with_fiber_defer(guard) do
|
31
|
+
expect(called_it).to be true
|
32
|
+
EM.stop
|
33
|
+
end
|
20
34
|
end
|
21
35
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
36
|
+
end
|
37
|
+
context 'when a hash guard is provided' do
|
38
|
+
before(:each) { Thread.current[:meaning] = 42 }
|
39
|
+
after(:each) { Thread.current[:meaning] = nil }
|
40
|
+
it 'propagates the thread/fiber-local variables into the block' do
|
41
|
+
EM.run do
|
42
|
+
guard = {
|
43
|
+
Thread.current[:meaning] => proc { |x| Thread.current[:meaning] = x }
|
44
|
+
}
|
45
|
+
with_fiber_defer(guard) do
|
46
|
+
expect(Thread.current[:meaning]).to eql 42
|
26
47
|
EM.stop
|
27
48
|
end
|
28
49
|
end
|
29
50
|
end
|
30
51
|
end
|
52
|
+
context 'when an invalid guard is provided' do
|
53
|
+
it 'raises an error' do
|
54
|
+
EM.run do
|
55
|
+
expect{with_fiber_defer(3){ }}.to raise_error /guard/
|
56
|
+
EM.stop
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
it 'cannot be nested in with_fiber_defer' do
|
61
|
+
EM.run do
|
62
|
+
with_fiber_defer do
|
63
|
+
expect{with_fiber_defer{}}.to raise_error /nested/
|
64
|
+
EM.stop
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
it 'cannot be nested in fiber_defer' do
|
69
|
+
EM.run do
|
70
|
+
with_fiber_defer do
|
71
|
+
fiber_defer do
|
72
|
+
expect{with_fiber_defer{}}.to raise_error /nested/
|
73
|
+
end
|
74
|
+
EM.stop
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
31
78
|
end
|
32
79
|
|
33
80
|
describe '#fiber_defer' do
|
34
|
-
context 'when
|
81
|
+
context 'when called outside a with_fiber_defer block' do
|
35
82
|
it 'raises an error' do
|
36
83
|
expect{fiber_defer{}}.to raise_error /with_fiber_defer/
|
37
84
|
end
|
38
85
|
end
|
39
86
|
context 'when it is not passed a block' do
|
40
87
|
it 'raises an error' do
|
41
|
-
|
88
|
+
EM.run do
|
89
|
+
with_fiber_defer do
|
90
|
+
expect{fiber_defer}.to raise_error /must be passed an action/
|
91
|
+
EM.stop
|
92
|
+
end
|
93
|
+
end
|
42
94
|
end
|
43
95
|
end
|
44
96
|
it 'executes the block on a background thread' do
|
@@ -95,6 +147,108 @@ describe Abstractivator::FiberDefer do
|
|
95
147
|
end
|
96
148
|
end
|
97
149
|
end
|
150
|
+
context 'when a proc guard is provided' do
|
151
|
+
it 'invokes the guard upon entering and exiting the block' do
|
152
|
+
called_count = 0
|
153
|
+
guard = proc { called_count += 1 }
|
154
|
+
EM.run do
|
155
|
+
with_fiber_defer do
|
156
|
+
fiber_defer(guard) do
|
157
|
+
expect(called_count).to eql 1
|
158
|
+
end
|
159
|
+
expect(called_count).to eql 2
|
160
|
+
EM.stop
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
context 'and an inherited guard is provided' do
|
165
|
+
it 'invokes the inherited guard and then the local guard upon entering and exiting the block' do
|
166
|
+
the_log = []
|
167
|
+
guard1 = proc { the_log << 'guard1' }
|
168
|
+
guard2 = proc { the_log << 'guard2' }
|
169
|
+
EM.run do
|
170
|
+
with_fiber_defer(guard1) do
|
171
|
+
expect(the_log).to eql %w(guard1)
|
172
|
+
fiber_defer(guard2) do
|
173
|
+
expect(the_log).to eql %w(guard1 guard1 guard2)
|
174
|
+
end
|
175
|
+
expect(the_log).to eql %w(guard1 guard1 guard2 guard1 guard2)
|
176
|
+
EM.stop
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
context 'when a hash guard is provided' do
|
183
|
+
before(:each) do
|
184
|
+
Thread.current[:a] = 1
|
185
|
+
Thread.current[:b] = 2
|
186
|
+
end
|
187
|
+
after(:each) do
|
188
|
+
Thread.current[:a] = nil
|
189
|
+
Thread.current[:b] = nil
|
190
|
+
end
|
191
|
+
it 'propagates the thread/fiber-local variables into the block' do
|
192
|
+
EM.run do
|
193
|
+
with_fiber_defer do
|
194
|
+
Thread.current[:a] = 42
|
195
|
+
guard = { Thread.current[:a] => proc {|x| Thread.current[:a] = x} }
|
196
|
+
fiber_defer(guard) do
|
197
|
+
expect(Thread.current[:a]).to eql 42
|
198
|
+
end
|
199
|
+
expect(Thread.current[:a]).to eql 42
|
200
|
+
EM.stop
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
context 'and an inherited guard is provided' do
|
205
|
+
it 'applies the inherited guard and then the local guard upon entering and exiting the block' do
|
206
|
+
EM.run do
|
207
|
+
guard1 = {
|
208
|
+
Thread.current[:a] => proc {|x| Thread.current[:a] = x},
|
209
|
+
Thread.current[:b] => proc {|x| Thread.current[:b] = x}
|
210
|
+
}
|
211
|
+
with_fiber_defer(guard1) do
|
212
|
+
Thread.current[:b] = 22
|
213
|
+
Thread.current[:c] = 33
|
214
|
+
guard2 = {
|
215
|
+
Thread.current[:b] => proc {|x| Thread.current[:b] = x},
|
216
|
+
Thread.current[:c] => proc {|x| Thread.current[:c] = x}
|
217
|
+
}
|
218
|
+
fiber_defer(guard2) do
|
219
|
+
expect(Thread.current[:a]).to eql 1
|
220
|
+
expect(Thread.current[:b]).to eql 22
|
221
|
+
expect(Thread.current[:c]).to eql 33
|
222
|
+
end
|
223
|
+
expect(Thread.current[:a]).to eql 1
|
224
|
+
expect(Thread.current[:b]).to eql 22
|
225
|
+
expect(Thread.current[:c]).to eql 33
|
226
|
+
EM.stop
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
context 'when an invalid guard is provided' do
|
233
|
+
it 'raises an error' do
|
234
|
+
EM.run do
|
235
|
+
with_fiber_defer do
|
236
|
+
expect{fiber_defer(3){ }}.to raise_error /guard/
|
237
|
+
end
|
238
|
+
EM.stop
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
it 'cannot be nested in fiber_defer' do
|
243
|
+
EM.run do
|
244
|
+
with_fiber_defer do
|
245
|
+
fiber_defer do
|
246
|
+
expect{fiber_defer{}}.to raise_error /nested/
|
247
|
+
end
|
248
|
+
EM.stop
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
98
252
|
end
|
99
253
|
|
100
254
|
describe '#mongoid_fiber_defer' do
|
@@ -130,4 +284,5 @@ describe Abstractivator::FiberDefer do
|
|
130
284
|
end
|
131
285
|
end
|
132
286
|
end
|
287
|
+
|
133
288
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abstractivator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Winton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|