exceptional_synchrony 1.0.1
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 +7 -0
- data/.gitignore +55 -0
- data/.ruby-gemset +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +128 -0
- data/README.md +4 -0
- data/Rakefile +2 -0
- data/Thorfile +42 -0
- data/exceptional_synchrony.gemspec +26 -0
- data/lib/exceptional_synchrony.rb +14 -0
- data/lib/exceptional_synchrony/callback_exceptions.rb +43 -0
- data/lib/exceptional_synchrony/event_machine_proxy.rb +96 -0
- data/lib/exceptional_synchrony/limited_work_queue.rb +52 -0
- data/lib/exceptional_synchrony/parallel_sync.rb +86 -0
- data/lib/exceptional_synchrony/version.rb +3 -0
- data/test/test_helper.rb +57 -0
- data/test/unit/callback_exceptions_test.rb +150 -0
- data/test/unit/event_machine_proxy_test.rb +100 -0
- data/test/unit/limited_work_queue_test.rb +139 -0
- data/test/unit/parallel_sync_test.rb +182 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6b536ff75b55f1cd20206a7d4b6befd78555c587
|
4
|
+
data.tar.gz: 9dcb17118c8e3be70aa63a9a1491f0d50d697c1e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 894cf6ac51e3b063e87ad8c125721a3ea3fecdd07ba307aa7e9430182adb8da94a25a6039e3859b5d3920e316e12def9f1b563a7819c5f005c82c4a9567a258c
|
7
|
+
data.tar.gz: c6fee4da47ba5665bc2e5841d31c0a1fce01ca90d9480a9ac52c8e7d6bca4aef883d6a2cfa93e85b5dcf512466833b70317d7a3b558bd1f894bb09640fd3c4bb
|
data/.gitignore
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# OS X
|
2
|
+
.DS_Store
|
3
|
+
.AppleDouble
|
4
|
+
.LSOverride
|
5
|
+
Icon
|
6
|
+
|
7
|
+
# Thumbnails
|
8
|
+
._*
|
9
|
+
|
10
|
+
# Files that might appear on external disk
|
11
|
+
.Spotlight-V100
|
12
|
+
.Trashes
|
13
|
+
|
14
|
+
# Numerous always-ignore extensions
|
15
|
+
*.diff
|
16
|
+
*.err
|
17
|
+
*.orig
|
18
|
+
*.rej
|
19
|
+
*.vi
|
20
|
+
|
21
|
+
# OS or Editor folders
|
22
|
+
.cache
|
23
|
+
.project
|
24
|
+
.settings
|
25
|
+
.tmproj
|
26
|
+
nbproject
|
27
|
+
Thumbs.db
|
28
|
+
|
29
|
+
# Folders to ignore
|
30
|
+
.hg
|
31
|
+
.svn
|
32
|
+
.CVS
|
33
|
+
intermediate
|
34
|
+
publish
|
35
|
+
.bundle
|
36
|
+
|
37
|
+
# Vim
|
38
|
+
*.s[a-w][a-z]
|
39
|
+
*.un~
|
40
|
+
Session.vim
|
41
|
+
.netrwhist
|
42
|
+
*~
|
43
|
+
|
44
|
+
# RubyMine
|
45
|
+
.idea/
|
46
|
+
|
47
|
+
# Ruby
|
48
|
+
log/
|
49
|
+
coverage/
|
50
|
+
|
51
|
+
# jmeter
|
52
|
+
*.jt
|
53
|
+
|
54
|
+
# else
|
55
|
+
*.log
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
exceptional_synchrony
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
exceptional_synchrony (1.0.1)
|
5
|
+
em-http-request
|
6
|
+
em-synchrony (~> 1.0.3)
|
7
|
+
eventmachine (~> 1.0.3)
|
8
|
+
exception_handling
|
9
|
+
hobo_support
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
actionmailer (3.2.13)
|
15
|
+
actionpack (= 3.2.13)
|
16
|
+
mail (~> 2.5.3)
|
17
|
+
actionpack (3.2.13)
|
18
|
+
activemodel (= 3.2.13)
|
19
|
+
activesupport (= 3.2.13)
|
20
|
+
builder (~> 3.0.0)
|
21
|
+
erubis (~> 2.7.0)
|
22
|
+
journey (~> 1.0.4)
|
23
|
+
rack (~> 1.4.5)
|
24
|
+
rack-cache (~> 1.2)
|
25
|
+
rack-test (~> 0.6.1)
|
26
|
+
sprockets (~> 2.2.1)
|
27
|
+
activemodel (3.2.13)
|
28
|
+
activesupport (= 3.2.13)
|
29
|
+
builder (~> 3.0.0)
|
30
|
+
activerecord (3.2.13)
|
31
|
+
activemodel (= 3.2.13)
|
32
|
+
activesupport (= 3.2.13)
|
33
|
+
arel (~> 3.0.2)
|
34
|
+
tzinfo (~> 0.3.29)
|
35
|
+
activeresource (3.2.13)
|
36
|
+
activemodel (= 3.2.13)
|
37
|
+
activesupport (= 3.2.13)
|
38
|
+
activesupport (3.2.13)
|
39
|
+
i18n (= 0.6.1)
|
40
|
+
multi_json (~> 1.0)
|
41
|
+
addressable (2.3.5)
|
42
|
+
arel (3.0.3)
|
43
|
+
builder (3.0.4)
|
44
|
+
cookiejar (0.3.0)
|
45
|
+
crack (0.4.1)
|
46
|
+
safe_yaml (~> 0.9.0)
|
47
|
+
em-http-request (1.1.2)
|
48
|
+
addressable (>= 2.3.4)
|
49
|
+
cookiejar
|
50
|
+
em-socksify (>= 0.3)
|
51
|
+
eventmachine (>= 1.0.3)
|
52
|
+
http_parser.rb (>= 0.6.0)
|
53
|
+
em-socksify (0.3.0)
|
54
|
+
eventmachine (>= 1.0.0.beta.4)
|
55
|
+
em-synchrony (1.0.3)
|
56
|
+
eventmachine (>= 1.0.0.beta.1)
|
57
|
+
erubis (2.7.0)
|
58
|
+
eventmachine (1.0.3)
|
59
|
+
exception_handling (0.1.2)
|
60
|
+
actionmailer (= 3.2.13)
|
61
|
+
actionpack (= 3.2.13)
|
62
|
+
activesupport (= 3.2.13)
|
63
|
+
eventmachine (>= 0.12.10)
|
64
|
+
hike (1.2.3)
|
65
|
+
hobo_support (2.0.1)
|
66
|
+
rails (~> 3.2.0)
|
67
|
+
http_parser.rb (0.6.0)
|
68
|
+
i18n (0.6.1)
|
69
|
+
journey (1.0.4)
|
70
|
+
json (1.8.1)
|
71
|
+
mail (2.5.4)
|
72
|
+
mime-types (~> 1.16)
|
73
|
+
treetop (~> 1.4.8)
|
74
|
+
mime-types (1.25.1)
|
75
|
+
minitest (4.7.5)
|
76
|
+
multi_json (1.8.4)
|
77
|
+
polyglot (0.3.3)
|
78
|
+
rack (1.4.5)
|
79
|
+
rack-cache (1.2)
|
80
|
+
rack (>= 0.4)
|
81
|
+
rack-ssl (1.3.3)
|
82
|
+
rack
|
83
|
+
rack-test (0.6.2)
|
84
|
+
rack (>= 1.0)
|
85
|
+
rails (3.2.13)
|
86
|
+
actionmailer (= 3.2.13)
|
87
|
+
actionpack (= 3.2.13)
|
88
|
+
activerecord (= 3.2.13)
|
89
|
+
activeresource (= 3.2.13)
|
90
|
+
activesupport (= 3.2.13)
|
91
|
+
bundler (~> 1.0)
|
92
|
+
railties (= 3.2.13)
|
93
|
+
railties (3.2.13)
|
94
|
+
actionpack (= 3.2.13)
|
95
|
+
activesupport (= 3.2.13)
|
96
|
+
rack-ssl (~> 1.3.2)
|
97
|
+
rake (>= 0.8.7)
|
98
|
+
rdoc (~> 3.4)
|
99
|
+
thor (>= 0.14.6, < 2.0)
|
100
|
+
rake (10.1.1)
|
101
|
+
rdoc (3.12.2)
|
102
|
+
json (~> 1.4)
|
103
|
+
rr (1.1.2)
|
104
|
+
safe_yaml (0.9.7)
|
105
|
+
sprockets (2.2.2)
|
106
|
+
hike (~> 1.2)
|
107
|
+
multi_json (~> 1.0)
|
108
|
+
rack (~> 1.0)
|
109
|
+
tilt (~> 1.1, != 1.3.0)
|
110
|
+
thor (0.18.1)
|
111
|
+
tilt (1.4.1)
|
112
|
+
treetop (1.4.15)
|
113
|
+
polyglot
|
114
|
+
polyglot (>= 0.3.1)
|
115
|
+
tzinfo (0.3.38)
|
116
|
+
webmock (1.17.1)
|
117
|
+
addressable (>= 2.2.7)
|
118
|
+
crack (>= 0.3.2)
|
119
|
+
|
120
|
+
PLATFORMS
|
121
|
+
ruby
|
122
|
+
|
123
|
+
DEPENDENCIES
|
124
|
+
exceptional_synchrony!
|
125
|
+
minitest
|
126
|
+
rr (~> 1.1.2)
|
127
|
+
thor
|
128
|
+
webmock (~> 1.17.1)
|
data/README.md
ADDED
data/Rakefile
ADDED
data/Thorfile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift('lib')
|
4
|
+
|
5
|
+
class Dev < Thor
|
6
|
+
package_name 'dev'
|
7
|
+
|
8
|
+
desc 'test' , 'Run the entire test suite or a single file.'
|
9
|
+
method_options :file => :string, :n => :string
|
10
|
+
def test
|
11
|
+
|
12
|
+
ENV['RACK_ENV'] = 'test'
|
13
|
+
|
14
|
+
require 'bundler'
|
15
|
+
Bundler.setup(:default, :development)
|
16
|
+
|
17
|
+
require './test/test_helper.rb'
|
18
|
+
|
19
|
+
# Minitest is very bad and piggy backs on the Global arguments just like everyone else.
|
20
|
+
# Let's stop it from trying to parse Thor's arguments.
|
21
|
+
clear_args
|
22
|
+
|
23
|
+
add_args('-n', options[:n]) if options[:n]
|
24
|
+
|
25
|
+
# If we have a single file run it, otherwise run them all.
|
26
|
+
if single_file = options[:file]
|
27
|
+
require single_file
|
28
|
+
else
|
29
|
+
Dir.glob('./test/**/*_test.rb') { |f| require f }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def clear_args
|
35
|
+
ARGV.shift(ARGV.length)
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_args(*args)
|
39
|
+
args.each { |arg| ARGV << arg }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../lib/exceptional_synchrony/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'exceptional_synchrony'
|
5
|
+
gem.date = '2014-01-16'
|
6
|
+
gem.summary = 'Extensions to EventMachine/Synchrony to work well with exceptions'
|
7
|
+
gem.description = %q{}
|
8
|
+
gem.authors = ['Colin Kelley']
|
9
|
+
gem.email = 'colin@invoca.com'
|
10
|
+
gem.homepage = 'https://github.com/Invoca/exceptional_synchrony'
|
11
|
+
gem.license = 'MIT'
|
12
|
+
gem.files = `git ls-files`.split($/)
|
13
|
+
gem.version = ExceptionalSynchrony::VERSION
|
14
|
+
|
15
|
+
gem.add_dependency 'exception_handling'
|
16
|
+
gem.add_dependency 'eventmachine', '~> 1.0.3'
|
17
|
+
gem.add_dependency 'em-synchrony', '~> 1.0.3'
|
18
|
+
gem.add_dependency 'em-http-request'
|
19
|
+
gem.add_dependency 'hobo_support'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'thor'
|
22
|
+
gem.add_development_dependency 'minitest'
|
23
|
+
gem.add_development_dependency 'webmock', '~> 1.17.1'
|
24
|
+
|
25
|
+
gem.add_development_dependency 'rr', '~> 1.1.2'
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'hobo_support'
|
2
|
+
require 'em-synchrony'
|
3
|
+
require 'em-synchrony/em-http'
|
4
|
+
require 'exception_handling'
|
5
|
+
|
6
|
+
|
7
|
+
module ExceptionalSynchrony
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'exceptional_synchrony/callback_exceptions'
|
11
|
+
require_relative 'exceptional_synchrony/event_machine_proxy'
|
12
|
+
require_relative 'exceptional_synchrony/limited_work_queue'
|
13
|
+
require_relative 'exceptional_synchrony/parallel_sync'
|
14
|
+
require_relative 'exceptional_synchrony/version'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ExceptionalSynchrony
|
2
|
+
module CallbackExceptions
|
3
|
+
class Failure < StandardError; end
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def ensure_callback(deferrable, &block)
|
7
|
+
result = return_exception(&block)
|
8
|
+
deferrable.succeed(*Array(result))
|
9
|
+
end
|
10
|
+
|
11
|
+
def map_deferred_result(deferrable)
|
12
|
+
deferred_status = deferrable.instance_variable_get(:@deferred_status)
|
13
|
+
deferred_args = deferrable.instance_variable_get(:@deferred_args)
|
14
|
+
result = (deferred_args && deferred_args.size == 1) ? deferred_args.first : deferred_args
|
15
|
+
|
16
|
+
case deferred_status
|
17
|
+
when :succeeded
|
18
|
+
if result.is_a?(Exception)
|
19
|
+
raise result
|
20
|
+
else
|
21
|
+
result
|
22
|
+
end
|
23
|
+
when :failed
|
24
|
+
if result.respond_to?(:error) && result.error =~ /timeout/i
|
25
|
+
raise Timeout::Error
|
26
|
+
else
|
27
|
+
raise Failure, result.inspect
|
28
|
+
end
|
29
|
+
else
|
30
|
+
raise ArgumentError, "No deferred status set yet: #{deferred_status.inspect} #{result.inspect}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def return_exception(*args)
|
35
|
+
begin
|
36
|
+
yield *args
|
37
|
+
rescue Exception => ex
|
38
|
+
ex
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# This class is for Dependency Injection of EventMachine. All EventMachine interactions should go through here.
|
2
|
+
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'em-http'
|
5
|
+
require 'em-synchrony/em-http'
|
6
|
+
|
7
|
+
module ExceptionalSynchrony
|
8
|
+
class EventMachineProxy
|
9
|
+
|
10
|
+
attr_reader :connection
|
11
|
+
|
12
|
+
WRAP_WITH_ENSURE_COMPLETELY_SAFE = (ENV['RACK_ENV'] != 'test')
|
13
|
+
|
14
|
+
def initialize(proxy_class, connection_class)
|
15
|
+
@proxy_class = proxy_class
|
16
|
+
@synchrony = defined?(@proxy_class::Synchrony) ? @proxy_class::Synchrony : @proxy_class
|
17
|
+
@connection = connection_class
|
18
|
+
|
19
|
+
proxy_class.error_handler do |error|
|
20
|
+
ExceptionHandling.log_error(error, "ExceptionalSynchrony uncaught exception: ")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_timer(seconds, &block)
|
25
|
+
@synchrony.add_timer(seconds) do
|
26
|
+
ensure_completely_safe("add_timer") do
|
27
|
+
block.call
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_periodic_timer(*args, &block)
|
33
|
+
@synchrony.add_periodic_timer(*args) do
|
34
|
+
ensure_completely_safe("add_periodic_timer") do
|
35
|
+
block.call
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def sleep(seconds)
|
41
|
+
@synchrony.sleep(seconds)
|
42
|
+
end
|
43
|
+
|
44
|
+
def next_tick(&block)
|
45
|
+
@synchrony.next_tick do
|
46
|
+
ensure_completely_safe("next_tick") do
|
47
|
+
block.call
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop
|
53
|
+
@proxy_class.stop
|
54
|
+
@proxy_class.next_tick { } #Fake out EventMachine's epoll mechanism so we don't block until timers fire
|
55
|
+
end
|
56
|
+
|
57
|
+
def connect(server, port = nil, handler = nil, *args, &block)
|
58
|
+
@proxy_class.connect(server, port, handler, *args, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def run(&block)
|
62
|
+
ensure_completely_safe("run") do
|
63
|
+
if @proxy_class.respond_to?(:synchrony)
|
64
|
+
@proxy_class.synchrony(&block)
|
65
|
+
else
|
66
|
+
@proxy_class.run(&block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def reactor_running?
|
72
|
+
@proxy_class.reactor_running?
|
73
|
+
end
|
74
|
+
|
75
|
+
def run_and_stop
|
76
|
+
ret = nil
|
77
|
+
run do
|
78
|
+
ret = yield
|
79
|
+
stop
|
80
|
+
end
|
81
|
+
ret
|
82
|
+
end
|
83
|
+
|
84
|
+
def ensure_completely_safe(message)
|
85
|
+
if WRAP_WITH_ENSURE_COMPLETELY_SAFE
|
86
|
+
ExceptionHandling.ensure_completely_safe(message) do
|
87
|
+
yield
|
88
|
+
end
|
89
|
+
else
|
90
|
+
yield
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
EMP = EventMachineProxy.new(EventMachine, EventMachine::HttpRequest)
|
96
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ExceptionalSynchrony
|
2
|
+
class LimitedWorkQueue
|
3
|
+
def initialize(em, limit)
|
4
|
+
@em = em
|
5
|
+
limit > 0 or raise ArgumentError, "limit must be positive"
|
6
|
+
@limit = limit
|
7
|
+
@worker_count = 0
|
8
|
+
@job_procs = []
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adds a job_proc to work.
|
12
|
+
def add(proc = nil, &block)
|
13
|
+
job = proc || block
|
14
|
+
job.respond_to?(:call) or raise "Must respond_to?(:call)! #{job.inspect}"
|
15
|
+
if @job_procs.any? && job.respond_to?(:merge) && (merged_queue = job.merge(@job_procs))
|
16
|
+
@job_procs = merged_queue
|
17
|
+
else
|
18
|
+
@job_procs << job
|
19
|
+
end
|
20
|
+
work!
|
21
|
+
end
|
22
|
+
|
23
|
+
def workers_empty?
|
24
|
+
@worker_count.zero?
|
25
|
+
end
|
26
|
+
|
27
|
+
def workers_full?
|
28
|
+
@worker_count >= @limit
|
29
|
+
end
|
30
|
+
|
31
|
+
def queue_empty?
|
32
|
+
@job_procs.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def work!
|
37
|
+
until queue_empty? || workers_full?
|
38
|
+
job_proc = @job_procs.shift
|
39
|
+
@worker_count += 1
|
40
|
+
Fiber.new do
|
41
|
+
job_proc.call
|
42
|
+
worker_done
|
43
|
+
end.resume
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def worker_done
|
48
|
+
@worker_count -= 1
|
49
|
+
work!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|