zk 0.9.1 → 1.0.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -2
- data/Gemfile +3 -4
- data/README.markdown +14 -5
- data/RELEASES.markdown +69 -1
- data/Rakefile +50 -18
- data/docs/examples/block_until_node_deleted_ex.rb +63 -0
- data/docs/examples/events_01.rb +36 -0
- data/docs/examples/events_02.rb +41 -0
- data/lib/{z_k → zk}/client/base.rb +87 -30
- data/lib/{z_k → zk}/client/conveniences.rb +0 -1
- data/lib/{z_k → zk}/client/state_mixin.rb +0 -0
- data/lib/zk/client/threaded.rb +196 -0
- data/lib/{z_k → zk}/client/unixisms.rb +0 -0
- data/lib/zk/client.rb +59 -0
- data/lib/zk/core_ext.rb +75 -0
- data/lib/{z_k → zk}/election.rb +0 -0
- data/lib/zk/event.rb +168 -0
- data/lib/{z_k → zk}/event_handler.rb +53 -28
- data/lib/zk/event_handler_subscription.rb +68 -0
- data/lib/{z_k → zk}/exceptions.rb +38 -23
- data/lib/zk/extensions.rb +79 -0
- data/lib/{z_k → zk}/find.rb +0 -0
- data/lib/{z_k → zk}/locker.rb +0 -0
- data/lib/{z_k → zk}/logging.rb +0 -0
- data/lib/{z_k → zk}/message_queue.rb +8 -4
- data/lib/{z_k → zk}/mongoid.rb +0 -0
- data/lib/{z_k → zk}/pool.rb +0 -0
- data/lib/zk/stat.rb +115 -0
- data/lib/{z_k → zk}/threadpool.rb +52 -4
- data/lib/zk/version.rb +3 -0
- data/lib/zk.rb +238 -1
- data/spec/message_queue_spec.rb +2 -2
- data/spec/shared/client_contexts.rb +8 -20
- data/spec/shared/client_examples.rb +136 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/event_catcher.rb +11 -0
- data/spec/support/exist_matcher.rb +6 -0
- data/spec/support/logging.rb +2 -1
- data/spec/watch_spec.rb +194 -10
- data/spec/{z_k → zk}/client/locking_and_session_death_spec.rb +0 -32
- data/spec/zk/client_spec.rb +23 -0
- data/spec/{z_k → zk}/election_spec.rb +0 -0
- data/spec/{z_k → zk}/extensions_spec.rb +0 -0
- data/spec/{z_k → zk}/locker_spec.rb +0 -40
- data/spec/zk/module_spec.rb +185 -0
- data/spec/{z_k → zk}/mongoid_spec.rb +0 -2
- data/spec/{z_k → zk}/pool_spec.rb +0 -2
- data/spec/{z_k → zk}/threadpool_spec.rb +32 -4
- data/spec/zookeeper_spec.rb +1 -6
- data/zk.gemspec +2 -2
- metadata +64 -56
- data/lib/z_k/client/continuation_proxy.rb +0 -109
- data/lib/z_k/client/drop_box.rb +0 -98
- data/lib/z_k/client/multiplexed.rb +0 -28
- data/lib/z_k/client/threaded.rb +0 -76
- data/lib/z_k/client.rb +0 -35
- data/lib/z_k/event_handler_subscription.rb +0 -36
- data/lib/z_k/extensions.rb +0 -155
- data/lib/z_k/version.rb +0 -3
- data/lib/z_k.rb +0 -97
- data/spec/z_k/client/drop_box_spec.rb +0 -90
- data/spec/z_k/client/multiplexed_spec.rb +0 -20
- data/spec/z_k/client_spec.rb +0 -7
@@ -0,0 +1,68 @@
|
|
1
|
+
module ZK
|
2
|
+
# the subscription object that is passed back from subscribing
|
3
|
+
# to events.
|
4
|
+
# @see ZK::Client::Base#register
|
5
|
+
class EventHandlerSubscription
|
6
|
+
# the event handler associated with this subscription
|
7
|
+
# @return [EventHandler]
|
8
|
+
attr_accessor :event_handler
|
9
|
+
|
10
|
+
# the path this subscription is for
|
11
|
+
# @return [String]
|
12
|
+
attr_accessor :path
|
13
|
+
|
14
|
+
# the block associated with the path
|
15
|
+
# @return [Proc]
|
16
|
+
attr_accessor :callback
|
17
|
+
|
18
|
+
# an array of what kinds of events this handler is interested in receiving
|
19
|
+
#
|
20
|
+
# @return [Set] containing any combination of :create, :change, :delete,
|
21
|
+
# or :children
|
22
|
+
#
|
23
|
+
# @private
|
24
|
+
attr_accessor :interests
|
25
|
+
|
26
|
+
ALL_EVENTS = [:created, :deleted, :changed, :child].freeze unless defined?(ALL_EVENTS)
|
27
|
+
ALL_EVENT_SET = Set.new(ALL_EVENTS).freeze unless defined?(ALL_EVENT_SET)
|
28
|
+
|
29
|
+
# @private
|
30
|
+
def initialize(event_handler, path, callback, interests)
|
31
|
+
@event_handler, @path, @callback = event_handler, path, callback
|
32
|
+
@interests = prep_interests(interests)
|
33
|
+
end
|
34
|
+
|
35
|
+
# unsubscribe from the path or state you were watching
|
36
|
+
# @see ZK::Client::Base#register
|
37
|
+
def unsubscribe
|
38
|
+
@event_handler.unregister(self)
|
39
|
+
end
|
40
|
+
alias :unregister :unsubscribe
|
41
|
+
|
42
|
+
# @private
|
43
|
+
def call(event)
|
44
|
+
callback.call(event)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def prep_interests(a)
|
49
|
+
return ALL_EVENT_SET if a.nil?
|
50
|
+
|
51
|
+
rval =
|
52
|
+
case a
|
53
|
+
when Array
|
54
|
+
Set.new(a)
|
55
|
+
when Symbol
|
56
|
+
Set.new([a])
|
57
|
+
else
|
58
|
+
raise ArgumentError, "Don't know how to handle interests: #{a.inspect}"
|
59
|
+
end
|
60
|
+
|
61
|
+
rval.tap do |rv|
|
62
|
+
invalid = (rv - ALL_EVENT_SET)
|
63
|
+
raise ArgumentError, "Invalid event name(s) #{invalid.to_a.inspect} given" unless invalid.empty?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
@@ -1,27 +1,29 @@
|
|
1
1
|
module ZK
|
2
2
|
module Exceptions
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
3
|
+
silence_warnings do
|
4
|
+
OK = 0
|
5
|
+
# System and server-side errors
|
6
|
+
SYSTEMERROR = -1
|
7
|
+
RUNTIMEINCONSISTENCY = SYSTEMERROR - 1
|
8
|
+
DATAINCONSISTENCY = SYSTEMERROR - 2
|
9
|
+
CONNECTIONLOSS = SYSTEMERROR - 3
|
10
|
+
MARSHALLINGERROR = SYSTEMERROR - 4
|
11
|
+
UNIMPLEMENTED = SYSTEMERROR - 5
|
12
|
+
OPERATIONTIMEOUT = SYSTEMERROR - 6
|
13
|
+
BADARGUMENTS = SYSTEMERROR - 7
|
14
|
+
# API errors
|
15
|
+
APIERROR = -100;
|
16
|
+
NONODE = APIERROR - 1 # Node does not exist
|
17
|
+
NOAUTH = APIERROR - 2 # Current operation not permitted
|
18
|
+
BADVERSION = APIERROR - 3 # Version conflict
|
19
|
+
NOCHILDRENFOREPHEMERALS = APIERROR - 8
|
20
|
+
NODEEXISTS = APIERROR - 10
|
21
|
+
NOTEMPTY = APIERROR - 11
|
22
|
+
SESSIONEXPIRED = APIERROR - 12
|
23
|
+
INVALIDCALLBACK = APIERROR - 13
|
24
|
+
INVALIDACL = APIERROR - 14
|
25
|
+
AUTHFAILED = APIERROR - 15 # client authentication failed
|
26
|
+
end
|
25
27
|
|
26
28
|
|
27
29
|
# these errors are returned rather than the driver level errors
|
@@ -92,7 +94,7 @@ module ZK
|
|
92
94
|
INVALIDCALLBACK => InvalidCallback,
|
93
95
|
INVALIDACL => InvalidACL,
|
94
96
|
AUTHFAILED => AuthFailed,
|
95
|
-
}
|
97
|
+
}.freeze unless defined?(ERROR_MAP)
|
96
98
|
|
97
99
|
# base class of ZK generated errors (not driver-level errors)
|
98
100
|
class ZKError < StandardError; end
|
@@ -120,6 +122,19 @@ module ZK
|
|
120
122
|
|
121
123
|
# raised when someone performs a blocking ZK operation on the event dispatch thread.
|
122
124
|
class EventDispatchThreadException < ZKError; end
|
125
|
+
|
126
|
+
# raised when a chrooted conection is requested but the root doesn't exist
|
127
|
+
class ChrootPathDoesNotExistError < NoNode
|
128
|
+
def initialize(host_string, chroot_path)
|
129
|
+
super("Chrooted connection to #{host_string} at #{chroot_path} requested, but path did not exist")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class ChrootMustStartWithASlashError < ArgumentError
|
134
|
+
def initialize(erroneous_string)
|
135
|
+
super("Chroot strings must start with a '/' you provided: #{erroneous_string.inspect}")
|
136
|
+
end
|
137
|
+
end
|
123
138
|
end
|
124
139
|
end
|
125
140
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module ZK
|
2
|
+
# this is taken from activesupport-3.2.3, and pasted here so that we don't conflict if someone
|
3
|
+
# is using us as part of a rails app
|
4
|
+
#
|
5
|
+
# i've removed the code that includes InstanceMethods (tftfy)
|
6
|
+
# @private
|
7
|
+
module Concern
|
8
|
+
def self.extended(base)
|
9
|
+
base.instance_variable_set("@_dependencies", [])
|
10
|
+
end
|
11
|
+
|
12
|
+
def append_features(base)
|
13
|
+
if base.instance_variable_defined?("@_dependencies")
|
14
|
+
base.instance_variable_get("@_dependencies") << self
|
15
|
+
return false
|
16
|
+
else
|
17
|
+
return false if base < self
|
18
|
+
@_dependencies.each { |dep| base.send(:include, dep) }
|
19
|
+
super
|
20
|
+
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
|
21
|
+
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def included(base = nil, &block)
|
26
|
+
if base.nil?
|
27
|
+
@_included_block = block
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Extensions
|
35
|
+
# some extensions to the ZookeeperCallbacks classes, mainly convenience
|
36
|
+
# interrogators
|
37
|
+
module Callbacks
|
38
|
+
module Callback
|
39
|
+
extend Concern
|
40
|
+
|
41
|
+
# allow access to the connection that fired this callback
|
42
|
+
attr_accessor :zk
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
# allows for easier construction of a user callback block that will be
|
46
|
+
# called with the callback object itself as an argument.
|
47
|
+
#
|
48
|
+
# *args, if given, will be passed on *after* the callback
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
#
|
52
|
+
# WatcherCallback.create do |cb|
|
53
|
+
# puts "watcher callback called with argument: #{cb.inspect}"
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# "watcher callback called with argument: #<ZookeeperCallbacks::WatcherCallback:0x1018a3958 @state=3, @type=1, ...>"
|
57
|
+
#
|
58
|
+
#
|
59
|
+
def create(*args, &block)
|
60
|
+
cb_inst = new { block.call(cb_inst) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end # Callback
|
64
|
+
end # Callbacks
|
65
|
+
end # Extensions
|
66
|
+
end # ZK
|
67
|
+
|
68
|
+
# ZookeeperCallbacks::Callback.extend(ZK::Extensions::Callbacks::Callback)
|
69
|
+
ZookeeperCallbacks::Callback.send(:include, ZK::Extensions::Callbacks::Callback)
|
70
|
+
|
71
|
+
# Include the InterruptedSession module in key ZookeeperExceptions to allow
|
72
|
+
# clients to catch a single error type when waiting on a node (for example)
|
73
|
+
|
74
|
+
[:ConnectionClosed, :NotConnected, :SessionExpired, :SessionMoved, :ConnectionLoss].each do |class_name|
|
75
|
+
ZookeeperExceptions::ZookeeperException.const_get(class_name).tap do |klass|
|
76
|
+
klass.__send__(:include, ZK::Exceptions::InterruptedSession)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
data/lib/{z_k → zk}/find.rb
RENAMED
File without changes
|
data/lib/{z_k → zk}/locker.rb
RENAMED
File without changes
|
data/lib/{z_k → zk}/logging.rb
RENAMED
File without changes
|
@@ -13,7 +13,6 @@ module ZK
|
|
13
13
|
# #handle message
|
14
14
|
# end
|
15
15
|
class MessageQueue
|
16
|
-
|
17
16
|
# @private
|
18
17
|
# :nodoc:
|
19
18
|
attr_accessor :zk
|
@@ -79,15 +78,19 @@ module ZK
|
|
79
78
|
# the message
|
80
79
|
def subscribe(&block)
|
81
80
|
@subscription_block = block
|
82
|
-
@
|
81
|
+
@sub = @zk.register(full_queue_path) do |event, zk|
|
83
82
|
find_and_process_next_available(@zk.children(full_queue_path, :watch => true))
|
84
83
|
end
|
84
|
+
|
85
85
|
find_and_process_next_available(@zk.children(full_queue_path, :watch => true))
|
86
86
|
end
|
87
87
|
|
88
88
|
# stop listening to this queue
|
89
89
|
def unsubscribe
|
90
|
-
@
|
90
|
+
if @sub
|
91
|
+
@sub.unsubscribe
|
92
|
+
@sub = nil
|
93
|
+
end
|
91
94
|
end
|
92
95
|
|
93
96
|
# a list of the message titles in the queue
|
@@ -98,6 +101,7 @@ module ZK
|
|
98
101
|
# highly destructive method!
|
99
102
|
# WARNING! Will delete the queue and all messages in it
|
100
103
|
def destroy!
|
104
|
+
unsubscribe # first thing, make sure we don't get any callbacks related to this
|
101
105
|
children = @zk.children(full_queue_path)
|
102
106
|
locks = []
|
103
107
|
children.each do |path|
|
@@ -120,7 +124,7 @@ module ZK
|
|
120
124
|
messages.each do |message_title|
|
121
125
|
message_path = "#{full_queue_path}/#{message_title}"
|
122
126
|
locker = @zk.locker(message_path)
|
123
|
-
if locker.lock!
|
127
|
+
if locker.lock! # non-blocking lock
|
124
128
|
begin
|
125
129
|
data = @zk.get(message_path).first
|
126
130
|
result = @subscription_block.call(message_title, data)
|
data/lib/{z_k → zk}/mongoid.rb
RENAMED
File without changes
|
data/lib/{z_k → zk}/pool.rb
RENAMED
File without changes
|
data/lib/zk/stat.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module ZK
|
2
|
+
# Included in ZookeeperStat::Stat, extends it with some conveniences for
|
3
|
+
# dealing with Stat objects. Also provides docuemntation here for the meaning
|
4
|
+
# of these values.
|
5
|
+
#
|
6
|
+
# Some of the methods added are to match the names in [the documentation][]
|
7
|
+
#
|
8
|
+
# Some of this may eventually be pushed down to slyphon-zookeeper
|
9
|
+
#
|
10
|
+
# [the documentation]: http://zookeeper.apache.org/doc/r3.3.5/zookeeperProgrammers.html#sc_zkStatStructure
|
11
|
+
module Stat
|
12
|
+
MEMBERS = [:version, :exists, :czxid, :mzxid, :ctime, :mtime, :cversion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid].freeze
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
MEMBERS.all? { |m| self.__send__(m) == other.__send__(m) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns true if the node is ephemeral (will be cleaned up when the
|
19
|
+
# current session expires)
|
20
|
+
def ephemeral?
|
21
|
+
ephemeral_owner && (ephemeral_owner != 0)
|
22
|
+
end
|
23
|
+
|
24
|
+
# The zxid of the change that caused this znode to be created.
|
25
|
+
#
|
26
|
+
# (also: czxid)
|
27
|
+
def created_zxid
|
28
|
+
# @!parse alias czxid created_zxid
|
29
|
+
czxid
|
30
|
+
end
|
31
|
+
|
32
|
+
# The zxid of the change that last modified this znode.
|
33
|
+
#
|
34
|
+
# (also: mzxid)
|
35
|
+
def last_modified_zxid
|
36
|
+
mzxid
|
37
|
+
end
|
38
|
+
|
39
|
+
# The time in milliseconds from epoch when this znode was created
|
40
|
+
#
|
41
|
+
# (also: created\_time)
|
42
|
+
#
|
43
|
+
# @return [Fixnum]
|
44
|
+
# @see #ctime_t
|
45
|
+
def created_time
|
46
|
+
ctime
|
47
|
+
end
|
48
|
+
|
49
|
+
# The time when this znode was created
|
50
|
+
#
|
51
|
+
# @return [Time] creation time of this znode
|
52
|
+
def ctime_t
|
53
|
+
Time.at(ctime * 0.001)
|
54
|
+
end
|
55
|
+
|
56
|
+
# The time in milliseconds from epoch when this znode was last modified.
|
57
|
+
#
|
58
|
+
# (also: mtime)
|
59
|
+
# @return [Fixnum]
|
60
|
+
# @see #mtime_t
|
61
|
+
def last_modified_time
|
62
|
+
mtime
|
63
|
+
end
|
64
|
+
|
65
|
+
# The time when this znode was last modified
|
66
|
+
#
|
67
|
+
# @return [Time] last modification time of this znode
|
68
|
+
# @see #last_modified_time
|
69
|
+
def mtime_t
|
70
|
+
Time.at(mtime * 0.001)
|
71
|
+
end
|
72
|
+
|
73
|
+
# The number of changes to the children of this znode
|
74
|
+
#
|
75
|
+
# (also: #cversion)
|
76
|
+
# @return [Fixnum]
|
77
|
+
def child_list_version
|
78
|
+
cversion
|
79
|
+
end
|
80
|
+
|
81
|
+
# The number of changes to the ACL of this znode.
|
82
|
+
#
|
83
|
+
# (also: #aversion)
|
84
|
+
# @return [Fixnum]
|
85
|
+
def acl_list_version
|
86
|
+
aversion
|
87
|
+
end
|
88
|
+
|
89
|
+
# The number of changes to the data of this znode.
|
90
|
+
#
|
91
|
+
# @return [Fixnum]
|
92
|
+
def version
|
93
|
+
super
|
94
|
+
end
|
95
|
+
|
96
|
+
# The length of the data field of this znode.
|
97
|
+
#
|
98
|
+
# @return [Fixnum]
|
99
|
+
def data_length
|
100
|
+
super
|
101
|
+
end
|
102
|
+
|
103
|
+
# The number of children of this znode.
|
104
|
+
#
|
105
|
+
# @return [Fixnum]
|
106
|
+
def num_children
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end # Stat
|
110
|
+
end # ZK
|
111
|
+
|
112
|
+
class ZookeeperStat::Stat
|
113
|
+
include ZK::Stat
|
114
|
+
end
|
115
|
+
|
@@ -22,6 +22,8 @@ module ZK
|
|
22
22
|
|
23
23
|
@mutex = Mutex.new
|
24
24
|
|
25
|
+
@error_callbacks = []
|
26
|
+
|
25
27
|
start!
|
26
28
|
end
|
27
29
|
|
@@ -35,7 +37,7 @@ module ZK
|
|
35
37
|
callable ||= blk
|
36
38
|
|
37
39
|
# XXX(slyphon): do we care if the threadpool is not running?
|
38
|
-
|
40
|
+
# raise Exceptions::ThreadpoolIsNotRunningException unless running?
|
39
41
|
raise ArgumentError, "Argument to Threadpool#defer must respond_to?(:call)" unless callable.respond_to?(:call)
|
40
42
|
|
41
43
|
@threadqueue << callable
|
@@ -46,6 +48,12 @@ module ZK
|
|
46
48
|
@mutex.synchronize { @running }
|
47
49
|
end
|
48
50
|
|
51
|
+
# returns true if the current thread is one of the threadpool threads
|
52
|
+
def on_threadpool?
|
53
|
+
tp = @mutex.synchronize { @threadpool.dup }
|
54
|
+
tp and tp.respond_to?(:include?) and tp.include?(Thread.current)
|
55
|
+
end
|
56
|
+
|
49
57
|
# starts the threadpool if not already running
|
50
58
|
def start!
|
51
59
|
@mutex.synchronize do
|
@@ -56,6 +64,18 @@ module ZK
|
|
56
64
|
true
|
57
65
|
end
|
58
66
|
|
67
|
+
# register a block to be called back with unhandled exceptions that occur
|
68
|
+
# in the threadpool.
|
69
|
+
#
|
70
|
+
# @note if your exception callback block itself raises an exception, I will
|
71
|
+
# make fun of you.
|
72
|
+
#
|
73
|
+
def on_exception(&blk)
|
74
|
+
@mutex.synchronize do
|
75
|
+
@error_callbacks << blk
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
59
79
|
# join all threads in this threadpool, they will be given a maximum of +timeout+
|
60
80
|
# seconds to exit before they are considered hung and will be ignored (this is an
|
61
81
|
# issue with threads in general: see
|
@@ -90,18 +110,46 @@ module ZK
|
|
90
110
|
end
|
91
111
|
|
92
112
|
private
|
113
|
+
def dispatch_to_error_handler(e)
|
114
|
+
# make a copy that will be free from thread manipulation
|
115
|
+
# and doesn't require holding the lock
|
116
|
+
cbs = @mutex.synchronize { @error_callbacks.dup }
|
117
|
+
|
118
|
+
if cbs.empty?
|
119
|
+
default_exception_handler(e)
|
120
|
+
else
|
121
|
+
while cb = cbs.shift
|
122
|
+
begin
|
123
|
+
cb.call(e)
|
124
|
+
rescue Exception => e
|
125
|
+
msg = [
|
126
|
+
"Exception caught in user supplied on_exception handler.",
|
127
|
+
"Just meditate on the irony of that for a moment. There. Good.",
|
128
|
+
"The callback that errored was: #{cb.inspect}, the exception was",
|
129
|
+
""
|
130
|
+
]
|
131
|
+
|
132
|
+
default_exception_handler(e, msg.join("\n"))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def default_exception_handler(e, msg=nil)
|
139
|
+
msg ||= 'Exception caught in threadpool'
|
140
|
+
logger.error { "#{msg}: #{e.to_std_format}" }
|
141
|
+
end
|
142
|
+
|
93
143
|
def spawn_threadpool #:nodoc:
|
94
144
|
until @threadpool.size >= @size.to_i
|
95
145
|
thread = Thread.new do
|
96
146
|
while @running
|
97
147
|
begin
|
98
148
|
op = @threadqueue.pop
|
99
|
-
# $stderr.puts "thread #{Thread.current.inspect} got #{op.inspect}"
|
100
149
|
break if op == KILL_TOKEN
|
101
150
|
op.call
|
102
151
|
rescue Exception => e
|
103
|
-
|
104
|
-
logger.error { e.to_std_format }
|
152
|
+
dispatch_to_error_handler(e)
|
105
153
|
end
|
106
154
|
end
|
107
155
|
end
|
data/lib/zk/version.rb
ADDED