exceptional_synchrony 1.3.0.pre.lee.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d50d71b944ece103dd43d1efb29002bacec564abcab11ff3bf2b2f3d39cf68f8
4
- data.tar.gz: 54222d22252ea445f8ae8eab5cca785cded326979ca9665619c12873bef0819f
3
+ metadata.gz: 848bad0902a3f12ca5db9a3c357cc2ab3209799d74b347c578c626f23d699466
4
+ data.tar.gz: 5b0506f45a5dcd80127287d2fdd3cd7255f718b2c52866d0172fdf79fa27bd5c
5
5
  SHA512:
6
- metadata.gz: ac56deba463c441e282cf315866941bdfe5f399c0605e23778fb155960a7b5f469fdbc9e378fa3a72cb833102f528698148a7fe58d3df30effc46e51648bd3b8
7
- data.tar.gz: 4edd6057dcab5bb74334ec97cc95e02f27da421e89e56a8c78383f6103a12fc3a0a85048e06626839ebe97bc8b50c65981542c90518d5937eb6553d9aca24d65
6
+ metadata.gz: 263e887409ffe6620f687a79e0609cff55c46d989e4066f997846e00f08b582007e35fa12e46d991f2214a770da1312207c5432192ef23cb8f8eaa00916f2e3e
7
+ data.tar.gz: 5e628327433244d3bad52c4d0db5af754af69a8f941e57458437c891f8ba849a6583868860f95b336b311e49f8d262be349c51c7ef19dcb974d6531284782e3b
data/CHANGELOG.md CHANGED
@@ -6,15 +6,14 @@ Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0
6
6
 
7
7
  All notable changes to this project will be documented in this file.
8
8
 
9
- ## [1.3.0] - UNRELEASED
9
+ ## [1.4.0] - 2012-03-08
10
10
  ### Added
11
- - For users of `Faraday` connections, its `default_adapter` is configured to `:em_synchrony` when starting
12
- the `EventMachine` reactor so that the reactor does not get blocked when using `Faraday`
11
+ - Added use of Thread local variable to indicate when Eventmachine is running using EM::Synchrony
12
+ - Added faraday gem monkey patch to use the new Thread local variable to choose the adapter to use
13
13
 
14
- ## [1.1.1] - 2020-05-03
15
- - Replace hobo_support with invoca_utils
16
-
17
- [1.1.1]: https://github.com/Invoca/exceptional_synchrony/compare/v1.1.0...v1.1.1
14
+ ## [1.3.0] - 2021-02-04
15
+ ### Added
16
+ - Extend `EMP.defer` to have a new keyword argument, `wait_for_result` for the callers to control whether they should should block until the background thread returns. To preserve existing behavior, this option defaults to `true`, so `EMP.defer` will block in order to return the value (or raise an exception) from the deferred block. Callers can pass `wait_for_result: false` if they do not want to block.
18
17
 
19
18
  ## [1.2.0] - 2020-06-02
20
19
  ### Changed
@@ -27,4 +26,10 @@ All notable changes to this project will be documented in this file.
27
26
  We expect that outer edge handler to log the exception chain (the wrapper plus nested `cause` exception(s))
28
27
  and exit the process with a non-0 status code.
29
28
 
29
+ ## [1.1.1] - 2020-05-03
30
+ - Replace hobo_support with invoca_utils
31
+
32
+ [1.4.0]: https://github.com/Invoca/exceptional_synchrony/compare/v1.3.0...v1.4.0
33
+ [1.3.0]: https://github.com/Invoca/exceptional_synchrony/compare/v1.2.0...v1.3.0
30
34
  [1.2.0]: https://github.com/Invoca/exceptional_synchrony/compare/v1.1.1...v1.2.0
35
+ [1.1.1]: https://github.com/Invoca/exceptional_synchrony/compare/v1.1.0...v1.1.1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- exceptional_synchrony (1.3.0.pre.lee.1)
4
+ exceptional_synchrony (1.4.0)
5
5
  em-http-request
6
6
  em-synchrony
7
7
  eventmachine
@@ -11,30 +11,30 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- actionmailer (6.1.1)
15
- actionpack (= 6.1.1)
16
- actionview (= 6.1.1)
17
- activejob (= 6.1.1)
18
- activesupport (= 6.1.1)
14
+ actionmailer (6.1.3)
15
+ actionpack (= 6.1.3)
16
+ actionview (= 6.1.3)
17
+ activejob (= 6.1.3)
18
+ activesupport (= 6.1.3)
19
19
  mail (~> 2.5, >= 2.5.4)
20
20
  rails-dom-testing (~> 2.0)
21
- actionpack (6.1.1)
22
- actionview (= 6.1.1)
23
- activesupport (= 6.1.1)
21
+ actionpack (6.1.3)
22
+ actionview (= 6.1.3)
23
+ activesupport (= 6.1.3)
24
24
  rack (~> 2.0, >= 2.0.9)
25
25
  rack-test (>= 0.6.3)
26
26
  rails-dom-testing (~> 2.0)
27
27
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
28
- actionview (6.1.1)
29
- activesupport (= 6.1.1)
28
+ actionview (6.1.3)
29
+ activesupport (= 6.1.3)
30
30
  builder (~> 3.1)
31
31
  erubi (~> 1.4)
32
32
  rails-dom-testing (~> 2.0)
33
33
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
34
- activejob (6.1.1)
35
- activesupport (= 6.1.1)
34
+ activejob (6.1.3)
35
+ activesupport (= 6.1.3)
36
36
  globalid (>= 0.3.6)
37
- activesupport (6.1.1)
37
+ activesupport (6.1.3)
38
38
  concurrent-ruby (~> 1.0, >= 1.0.2)
39
39
  i18n (>= 1.6, < 2)
40
40
  minitest (>= 5.1)
@@ -64,19 +64,21 @@ GEM
64
64
  em-synchrony (1.0.6)
65
65
  eventmachine (>= 1.0.0.beta.1)
66
66
  erubi (1.10.0)
67
+ escalate (0.3.0)
67
68
  eventmachine (1.2.7)
68
- exception_handling (2.8.1)
69
+ exception_handling (2.9.0)
69
70
  actionmailer (>= 4.2, < 7.0)
70
71
  actionpack (>= 4.2, < 7.0)
71
72
  activesupport (>= 4.2, < 7.0)
72
73
  contextual_logger (~> 0.7)
74
+ escalate (~> 0.2)
73
75
  eventmachine (~> 1.0)
74
76
  invoca-utils (~> 0.3)
75
77
  globalid (0.4.2)
76
78
  activesupport (>= 4.2.0)
77
79
  hashdiff (1.0.1)
78
80
  http_parser.rb (0.6.0)
79
- i18n (1.8.8)
81
+ i18n (1.8.9)
80
82
  concurrent-ruby (~> 1.0)
81
83
  invoca-utils (0.4.1)
82
84
  json (2.5.1)
data/Rakefile CHANGED
@@ -7,6 +7,7 @@ require 'rake/testtask'
7
7
  task default: :test
8
8
 
9
9
  Rake::TestTask.new do |t|
10
+ t.warning = false
10
11
  t.pattern = "test/**/*_test.rb"
11
12
  end
12
13
 
@@ -3,6 +3,7 @@
3
3
  require 'eventmachine'
4
4
  require 'em-http'
5
5
  require 'em-synchrony/em-http'
6
+ require_relative 'faraday_monkey_patch'
6
7
 
7
8
  module ExceptionalSynchrony
8
9
  # It is important for this exception to be inherited from Exception so that
@@ -62,17 +63,22 @@ module ExceptionalSynchrony
62
63
  def stop
63
64
  @proxy_class.stop
64
65
  @proxy_class.next_tick { } #Fake out EventMachine's epoll mechanism so we don't block until timers fire
66
+ Thread.current.thread_variable_set(:em_synchrony_reactor_thread, false)
67
+ end
68
+
69
+ def defers_finished?
70
+ @proxy_class.defers_finished?
65
71
  end
66
72
 
67
73
  def connect(server, port = nil, handler = nil, *args, &block)
68
74
  @proxy_class.connect(server, port, handler, *args, &block)
69
75
  end
70
76
 
77
+ # This method starts the EventMachine reactor.
71
78
  # The on_error option has these possible values:
72
79
  # :log - log any rescued StandardError exceptions and continue
73
80
  # :raise - raise FatalRunError for any rescued StandardError exceptions
74
81
  def run(on_error: :log, &block)
75
- configure_faraday
76
82
  case on_error
77
83
  when :log then run_with_error_logging(&block)
78
84
  when :raise then run_with_error_raising(&block)
@@ -80,16 +86,21 @@ module ExceptionalSynchrony
80
86
  end
81
87
  end
82
88
 
83
- def defer(context, &block)
84
- deferrable = EventMachine::DefaultDeferrable.new
85
-
86
- callback = -> (result) { deferrable.succeed(result) }
87
-
88
- EventMachine.defer(nil, callback) { CallbackExceptions.return_exception(&block) }
89
-
90
- EventMachine::Synchrony.sync(deferrable)
89
+ # This method will execute the block on the background thread pool
90
+ # By default, it will block the caller until the background thread has finished, so that the result can be returned
91
+ # :wait_for_result - setting this to false will prevent the caller from being blocked by this deferred work
92
+ def defer(context, wait_for_result: true, &block)
93
+ if wait_for_result
94
+ deferrable = EventMachine::DefaultDeferrable.new
95
+ callback = -> (result) { deferrable.succeed(result) }
91
96
 
92
- CallbackExceptions.map_deferred_result(deferrable)
97
+ EventMachine.defer(nil, callback) { CallbackExceptions.return_exception(&block) }
98
+ EventMachine::Synchrony.sync(deferrable)
99
+ CallbackExceptions.map_deferred_result(deferrable)
100
+ else
101
+ EventMachine.defer { ExceptionHandling.ensure_completely_safe("defer", &block) }
102
+ nil
103
+ end
93
104
  end
94
105
 
95
106
  def reactor_running?
@@ -125,15 +136,10 @@ module ExceptionalSynchrony
125
136
 
126
137
  private
127
138
 
128
- def configure_faraday
129
- if defined?(Faraday)
130
- Faraday.default_adapter = :em_synchrony
131
- end
132
- end
133
-
134
139
  def run_with_error_logging(&block)
135
140
  ensure_completely_safe("run_with_error_logging") do
136
141
  if @proxy_class.respond_to?(:synchrony)
142
+ Thread.current.thread_variable_set(:em_synchrony_reactor_thread, true)
137
143
  @proxy_class.synchrony(&block)
138
144
  else
139
145
  @proxy_class.run(&block)
@@ -146,6 +152,7 @@ module ExceptionalSynchrony
146
152
 
147
153
  rescue_exceptions_and_ensure_exit("run_with_error_raising") do
148
154
  if @proxy_class.respond_to?(:synchrony)
155
+ Thread.current.thread_variable_set(:em_synchrony_reactor_thread, true)
149
156
  @proxy_class.synchrony(&run_block)
150
157
  else
151
158
  @proxy_class.run(&run_block)
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Monkey patch for the Faraday method that creates the adapter used for a connection.
4
+ # If the thread local variable :em_synchrony_reactor_thread is true, it overrides this method
5
+ # in order to force use of the :em_synchrony adapter rather than the :net_http adapter.
6
+ # This ensures that the Eventmachine reactor does not get blocked by connection i/o.
7
+ begin
8
+ require 'faraday'
9
+
10
+ module ExceptionalSynchrony
11
+ # Patch built relative to faraday v0.17.3
12
+ module FaradayAdapterPatch_v0
13
+ def adapter(key, *args, &block)
14
+
15
+ # BEGIN PATCH
16
+ if key == :net_http && Thread.current.thread_variable_get(:em_synchrony_reactor_thread)
17
+ key = :em_synchrony
18
+ end
19
+ # END PATCH
20
+
21
+ use_symbol(Faraday::Adapter, key, *args, &block)
22
+ end
23
+ end
24
+
25
+ # Patch built relative to faraday v1.3.0 although the ruby2_keywords prefix
26
+ # was dropped from the adapter method definition to simplify this code
27
+ module FaradayPatch_v1
28
+ def adapter(klass = NO_ARGUMENT, *args, &block)
29
+ return @adapter if klass == NO_ARGUMENT
30
+
31
+ klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
32
+
33
+ # BEGIN PATCH
34
+ if klass == Faraday::Adapter::NetHttp && Thread.current.thread_variable_get(:em_synchrony_reactor_thread)
35
+ klass = Faraday::Adapter::EMSynchrony
36
+ end
37
+ # END PATCH
38
+
39
+ @adapter = self.class::Handler.new(klass, *args, &block)
40
+ end
41
+ end
42
+ end
43
+
44
+ if Faraday::VERSION.start_with?("0")
45
+ Faraday::RackBuilder.prepend ExceptionalSynchrony::FaradayAdapterPatch_v0
46
+ else
47
+ Faraday::RackBuilder.prepend ExceptionalSynchrony::FaradayAdapterPatch_v1
48
+ end
49
+
50
+ rescue LoadError
51
+ # Monkey patch is not needed if faraday is not available
52
+ end
@@ -1,3 +1,3 @@
1
1
  module ExceptionalSynchrony
2
- VERSION = '1.3.0.pre.lee.1'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -24,6 +24,14 @@ describe ExceptionalSynchrony::EventMachineProxy do
24
24
  end
25
25
  end
26
26
 
27
+ def stop_em_after_defers_finish!(em)
28
+ check_finished_counter = 0
29
+ em.add_periodic_timer(0.1) do
30
+ (check_finished_counter += 1) > 20 and raise "defer never finished!"
31
+ em.defers_finished? and em.stop
32
+ end
33
+ end
34
+
27
35
  before do
28
36
  @em = ExceptionalSynchrony::EventMachineProxy.new(EventMachine, nil)
29
37
  @yielded_value = nil
@@ -56,6 +64,14 @@ describe ExceptionalSynchrony::EventMachineProxy do
56
64
  @em.stop
57
65
  end
58
66
 
67
+ it "should set thread variable :em_synchrony_reactor_thread running to false when stop" do
68
+ @em.run do
69
+ assert_equal true, Thread.current.thread_variable_get(:em_synchrony_reactor_thread)
70
+ @em.stop
71
+ assert_equal false, Thread.current.thread_variable_get(:em_synchrony_reactor_thread)
72
+ end
73
+ end
74
+
59
75
  it "should proxy connect" do
60
76
  ServerClass = Class.new
61
77
  mock(EventMachine).connect(ServerClass, 8080, :handler, :extra_arg).yields(:called)
@@ -78,18 +94,41 @@ describe ExceptionalSynchrony::EventMachineProxy do
78
94
  end
79
95
 
80
96
  describe "#defer" do
81
- it "should output its block's output when it doesn't raise an error" do
82
- ExceptionHandling.logger = Logger.new(STDERR)
97
+ before do
98
+ logger = Logger.new(STDERR)
99
+ logger.extend ContextualLogger::LoggerMixin
100
+ ExceptionHandling.logger = logger
101
+ end
83
102
 
103
+ it "should output its block's output when it doesn't raise an error, by default" do
84
104
  @em.run do
85
105
  assert_equal 12, @em.defer("#defer success") { 12 }
86
106
  @em.stop
87
107
  end
88
108
  end
89
109
 
90
- it "should raise an error when its block raises an error" do
91
- ExceptionHandling.logger = Logger.new(STDERR)
110
+ it "should not wait for its block to run if option is passed" do
111
+ @block_ran = false
112
+
113
+ @em.run do
114
+ assert_nil @em.defer("#defer success", wait_for_result: false) { @block_ran = true; 12 }
115
+ refute @block_ran
116
+ stop_em_after_defers_finish!(@em)
117
+ end
118
+
119
+ assert @block_ran
120
+ end
121
+
122
+ it "should handle exceptions when not waiting for its block to run" do
123
+ mock(ExceptionHandling).log_error(is_a(RuntimeError), "defer", {})
124
+
125
+ @em.run do
126
+ assert_nil @em.defer("#defer success", wait_for_result: false) { raise RuntimeError, "error in defer" }
127
+ stop_em_after_defers_finish!(@em)
128
+ end
129
+ end
92
130
 
131
+ it "should raise an error when its block raises an error" do
93
132
  @em.run do
94
133
  ex = assert_raises(ArgumentError) do
95
134
  @em.defer("#defer raising an error") { raise ArgumentError, "!!!" }
@@ -127,27 +166,6 @@ describe ExceptionalSynchrony::EventMachineProxy do
127
166
  end
128
167
  end
129
168
 
130
- describe "run with faraday" do
131
- it "should conigure Faraday default_adapter to :em_synchrony if Faraday is defined" do
132
- class Faraday
133
- class << self
134
- attr_reader :default_adapter
135
-
136
- def default_adapter=(adapter)
137
- @default_adapter = adapter
138
- end
139
- end
140
-
141
- self.default_adapter = :net_http
142
- end
143
-
144
- mock(@em).run_with_error_logging
145
- assert_equal :net_http, Faraday.default_adapter
146
- @em.run
147
- assert_equal :em_synchrony, Faraday.default_adapter
148
- end
149
- end
150
-
151
169
  { synchrony: SynchronyProxyMock, run: RunProxyMock }.each do |method, proxy_mock|
152
170
  describe "run" do
153
171
  before do
@@ -161,6 +179,10 @@ describe ExceptionalSynchrony::EventMachineProxy do
161
179
  end
162
180
 
163
181
  describe "without error" do
182
+ before do
183
+ Thread.current.thread_variable_set(:em_synchrony_reactor_thread, nil)
184
+ end
185
+
164
186
  [:log, :raise].each do |on_error|
165
187
  describe "when using #{method} and on_error = #{on_error}" do
166
188
  it "should dispatch to the proxy's synchrony method instead of run iff synchrony" do
@@ -168,6 +190,15 @@ describe ExceptionalSynchrony::EventMachineProxy do
168
190
  assert_equal method, (@proxy.run(on_error: on_error) { dispatched = true })
169
191
  assert_equal true, dispatched
170
192
  end
193
+
194
+ if method == :synchrony
195
+ it "should set thread variable :em_synchrony_reactor_thread running to true" do
196
+ assert_nil Thread.current.thread_variable_get(:em_synchrony_reactor_thread)
197
+ @proxy.run(on_error: on_error) do
198
+ assert_equal true, Thread.current.thread_variable_get(:em_synchrony_reactor_thread)
199
+ end
200
+ end
201
+ end
171
202
  end
172
203
  end
173
204
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exceptional_synchrony
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0.pre.lee.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca
@@ -101,6 +101,7 @@ files:
101
101
  - lib/exceptional_synchrony.rb
102
102
  - lib/exceptional_synchrony/callback_exceptions.rb
103
103
  - lib/exceptional_synchrony/event_machine_proxy.rb
104
+ - lib/exceptional_synchrony/faraday_monkey_patch.rb
104
105
  - lib/exceptional_synchrony/limited_work_queue.rb
105
106
  - lib/exceptional_synchrony/parallel_sync.rb
106
107
  - lib/exceptional_synchrony/version.rb
@@ -126,9 +127,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
127
  version: '0'
127
128
  required_rubygems_version: !ruby/object:Gem::Requirement
128
129
  requirements:
129
- - - ">"
130
+ - - ">="
130
131
  - !ruby/object:Gem::Version
131
- version: 1.3.1
132
+ version: '0'
132
133
  requirements: []
133
134
  rubygems_version: 3.0.3
134
135
  signing_key: