abstractivator 0.5.0 → 0.6.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.
- 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
|