Skip to content

Commit 35dc1b9

Browse files
committed
More thread safety: Create new Executor instance for each stack start.
It in turn creates new instances of Middleware chain.
1 parent 828ae90 commit 35dc1b9

File tree

1 file changed

+43
-32
lines changed

1 file changed

+43
-32
lines changed

lib/modware/stack.rb

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,81 @@ def initialize(env:)
88
when Class then env
99
else KeyStruct[*env]
1010
end
11-
@middlewares = []
11+
@middleware_mods = []
1212
end
1313

1414
def add(mod)
15-
middleware = Middleware.new(self, mod)
16-
@middlewares.last._next = middleware if @middlewares.any?
17-
@middlewares << middleware
15+
@middleware_mods << mod
1816
end
1917

20-
def start(*args, &implementation)
18+
def start(*args, &base_implementation)
2119
env = @env_klass.new(*args)
22-
execute_stack(env, implementation)
20+
Executor.new(@middleware_mods).execute(env, base_implementation)
2321
env
2422
end
2523

2624
private
2725

28-
def execute_stack(env, base_implementation)
29-
return call_implementation(env, base_implementation) unless @middlewares.any?
30-
31-
@middlewares.each do |middleware|
32-
middleware.before env if middleware.respond_to? :before
26+
class Executor
27+
def initialize(middleware_mods)
28+
prev = nil
29+
@middlewares = middleware_mods.map { |mod|
30+
Middleware.new(self, mod).tap { |middleware|
31+
prev._modware_next = middleware if prev
32+
prev = middleware
33+
}
34+
}
3335
end
3436

35-
@middlewares.first._call(env, base_implementation)
37+
def execute(env, base_implementation)
38+
return call_implementation(env, base_implementation) if @middlewares.empty?
39+
40+
@middlewares.each do |middleware|
41+
middleware.before env if middleware.respond_to? :before
42+
end
43+
44+
@middlewares.first._modware_call(env, base_implementation)
3645

37-
@middlewares.each do |middleware|
38-
middleware.after env if middleware.respond_to? :after
46+
@middlewares.each do |middleware|
47+
middleware.after env if middleware.respond_to? :after
48+
end
3949
end
40-
end
4150

42-
def call_implementation(env, base_implementation)
43-
if middleware = @middlewares.select(&it.respond_to?(:implement)).last
44-
middleware.implement(env)
45-
elsif base_implementation
46-
base_implementation.call env
47-
else
48-
raise StackError, "No base implementation nor middleware implementation in stack"
51+
def call_implementation(env, base_implementation)
52+
if middleware = @middlewares.select(&it.respond_to?(:implement)).last
53+
middleware.implement(env)
54+
elsif base_implementation
55+
base_implementation.call env
56+
else
57+
raise StackError, "No base implementation nor middleware implementation in stack"
58+
end
4959
end
5060
end
5161

62+
5263
class Middleware
53-
attr_accessor :_next
64+
attr_accessor :_modware_next
5465

55-
def initialize(stack, mod)
56-
@stack = stack
66+
def initialize(executor, mod)
67+
@executor = executor
5768
singleton_class.send :include, mod
5869
end
5970

60-
def _call(env, base_implementation)
71+
def _modware_call(env, base_implementation)
6172
if respond_to? :around
6273
around(env) { |env|
63-
_continue env, base_implementation
74+
_modware_continue env, base_implementation
6475
}
6576
else
66-
_continue env, base_implementation
77+
_modware_continue env, base_implementation
6778
end
6879
end
6980

70-
def _continue(env, base_implementation)
71-
if self._next
72-
self._next._call(env, base_implementation)
81+
def _modware_continue(env, base_implementation)
82+
if self._modware_next
83+
self._modware_next._modware_call(env, base_implementation)
7384
else
74-
@stack.send :call_implementation, env, base_implementation
85+
@executor.call_implementation env, base_implementation
7586
end
7687
end
7788
end

0 commit comments

Comments
 (0)