cancer 0.1.0.a1
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.
- data/.autotest +3 -0
- data/.gitignore +5 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/cancer.gemspec +156 -0
- data/documentation/STREAM_INITIATION.markdown +18 -0
- data/examples/example.rb +80 -0
- data/examples/example_2.rb +20 -0
- data/examples/example_em.rb +11 -0
- data/examples/example_em_helper.rb +23 -0
- data/examples/example_im_roster.rb +26 -0
- data/examples/example_xep_0004.rb +124 -0
- data/examples/example_xep_0047.rb +35 -0
- data/examples/example_xep_0050.rb +78 -0
- data/examples/example_xep_0065.rb +66 -0
- data/examples/example_xep_0096.dup.rb +40 -0
- data/examples/example_xep_0096_xep_0047.rb +42 -0
- data/examples/example_xep_0096_xep_0065.rb +40 -0
- data/lib/cancer.rb +122 -0
- data/lib/cancer/adapter.rb +33 -0
- data/lib/cancer/adapters/em.rb +88 -0
- data/lib/cancer/adapters/socket.rb +122 -0
- data/lib/cancer/builder.rb +28 -0
- data/lib/cancer/controller.rb +32 -0
- data/lib/cancer/dependencies.rb +187 -0
- data/lib/cancer/events/abstract_event.rb +10 -0
- data/lib/cancer/events/base_matchers.rb +63 -0
- data/lib/cancer/events/binary_matchers.rb +30 -0
- data/lib/cancer/events/eventable.rb +92 -0
- data/lib/cancer/events/exception_events.rb +28 -0
- data/lib/cancer/events/handler.rb +33 -0
- data/lib/cancer/events/manager.rb +130 -0
- data/lib/cancer/events/named_events.rb +25 -0
- data/lib/cancer/events/proc_matcher.rb +16 -0
- data/lib/cancer/events/xml_events.rb +44 -0
- data/lib/cancer/exceptions.rb +53 -0
- data/lib/cancer/jid.rb +215 -0
- data/lib/cancer/lock.rb +32 -0
- data/lib/cancer/spec.rb +35 -0
- data/lib/cancer/spec/error.rb +18 -0
- data/lib/cancer/spec/extentions.rb +79 -0
- data/lib/cancer/spec/matcher.rb +30 -0
- data/lib/cancer/spec/mock_adapter.rb +139 -0
- data/lib/cancer/spec/mock_stream.rb +15 -0
- data/lib/cancer/spec/transcript.rb +107 -0
- data/lib/cancer/stream.rb +182 -0
- data/lib/cancer/stream/builder.rb +20 -0
- data/lib/cancer/stream/controller.rb +36 -0
- data/lib/cancer/stream/event_helper.rb +12 -0
- data/lib/cancer/stream/xep.rb +52 -0
- data/lib/cancer/stream_parser.rb +144 -0
- data/lib/cancer/support.rb +27 -0
- data/lib/cancer/support/hash.rb +32 -0
- data/lib/cancer/support/string.rb +22 -0
- data/lib/cancer/synchronized_stanza.rb +79 -0
- data/lib/cancer/thread_pool.rb +118 -0
- data/lib/cancer/xep.rb +43 -0
- data/lib/cancer/xeps/core.rb +109 -0
- data/lib/cancer/xeps/core/bind.rb +33 -0
- data/lib/cancer/xeps/core/sasl.rb +113 -0
- data/lib/cancer/xeps/core/session.rb +22 -0
- data/lib/cancer/xeps/core/stream.rb +18 -0
- data/lib/cancer/xeps/core/terminator.rb +21 -0
- data/lib/cancer/xeps/core/tls.rb +34 -0
- data/lib/cancer/xeps/im.rb +323 -0
- data/lib/cancer/xeps/xep_0004_x_data.rb +692 -0
- data/lib/cancer/xeps/xep_0020_feature_neg.rb +35 -0
- data/lib/cancer/xeps/xep_0030_disco.rb +167 -0
- data/lib/cancer/xeps/xep_0047_ibb.rb +322 -0
- data/lib/cancer/xeps/xep_0050_commands.rb +256 -0
- data/lib/cancer/xeps/xep_0065_bytestreams.rb +306 -0
- data/lib/cancer/xeps/xep_0066_oob.rb +69 -0
- data/lib/cancer/xeps/xep_0095_si.rb +211 -0
- data/lib/cancer/xeps/xep_0096_si_filetransfer.rb +173 -0
- data/lib/cancer/xeps/xep_0114_component.rb +73 -0
- data/lib/cancer/xeps/xep_0115_caps.rb +180 -0
- data/lib/cancer/xeps/xep_0138_compress.rb +134 -0
- data/lib/cancer/xeps/xep_0144_rosterx.rb +250 -0
- data/lib/cancer/xeps/xep_0184_receipts.rb +40 -0
- data/lib/cancer/xeps/xep_0199_ping.rb +41 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/stream/stanza_errors_spec.rb +47 -0
- data/spec/stream/stream_errors_spec.rb +38 -0
- data/spec/stream/stream_initialization_spec.rb +160 -0
- data/spec/xep_0050/local_spec.rb +165 -0
- data/spec/xep_0050/remote_spec.rb +44 -0
- metadata +200 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
class Stream
|
4
|
+
module Controller
|
5
|
+
|
6
|
+
def controllers
|
7
|
+
@controllers ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def controllers_by_name
|
11
|
+
@controllers_by_name ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def install_controller(klass)
|
15
|
+
self.controllers.push(klass)
|
16
|
+
self.controllers_by_name[klass.to_s] = klass.new(self)
|
17
|
+
self.controllers_by_name[klass.to_s].register_with(@manager)
|
18
|
+
@manager.resolve_martchers! if @initialized
|
19
|
+
end
|
20
|
+
|
21
|
+
def extend_controller(klass, &proc)
|
22
|
+
object = self.controllers_by_name[klass.to_s]
|
23
|
+
raise "Controller not found #{klass}" if object.nil?
|
24
|
+
metaklass = (class << object ; self ; end)
|
25
|
+
metaklass.instance_eval(&proc)
|
26
|
+
end
|
27
|
+
|
28
|
+
def controller(&proc)
|
29
|
+
controller = Class.new(Cancer::Controller)
|
30
|
+
controller.class_eval(&proc)
|
31
|
+
install_controller controller
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
class Stream
|
4
|
+
module XEP
|
5
|
+
|
6
|
+
def xeps
|
7
|
+
@xeps ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def install_xep(name)
|
11
|
+
if @initialized
|
12
|
+
unless self.xeps.include?(name)
|
13
|
+
xep = Cancer::XEP.const_for(name)
|
14
|
+
xep.strong_dependecies.each do |dep|
|
15
|
+
install_xep(dep)
|
16
|
+
end
|
17
|
+
xep.enhance_stream(self)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
self.xeps.push(name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def enhance_stream_with_xeps!
|
27
|
+
xeps_by_name = self.xeps.inject({}) do |hash, xep_name|
|
28
|
+
hash[xep_name] = Cancer::XEP.const_for(xep_name)
|
29
|
+
hash
|
30
|
+
end
|
31
|
+
|
32
|
+
resolver = Cancer::Dependencies::Resolver.new(:allow_reverse => false)
|
33
|
+
resolver.on_missing_strong_dependency do |xep_name|
|
34
|
+
xep = Cancer::XEP.const_for(xep_name)
|
35
|
+
xeps_by_name[xep_name] = xep
|
36
|
+
resolver.add_node(xep_name, xep)
|
37
|
+
install_xep(xep_name)
|
38
|
+
end
|
39
|
+
xeps_by_name.each do |xep_name, xep|
|
40
|
+
resolver.add_node(xep_name, xep)
|
41
|
+
end
|
42
|
+
orderd_xeps = resolver.sort(*self.xeps)
|
43
|
+
Cancer.logger.debug "XEPS: #{orderd_xeps.inspect}"
|
44
|
+
orderd_xeps.each do |xep_name|
|
45
|
+
xep = xeps_by_name[xep_name]
|
46
|
+
xep.enhance_stream(self)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
class StreamParser < Nokogiri::XML::SAX::Document
|
4
|
+
|
5
|
+
def initialize(stream)
|
6
|
+
@stream = stream
|
7
|
+
@document = nil
|
8
|
+
@current = nil
|
9
|
+
@current_ns = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
###
|
13
|
+
# Called when document starts parsing
|
14
|
+
def start_document
|
15
|
+
end
|
16
|
+
|
17
|
+
###
|
18
|
+
# Called when document ends parsing
|
19
|
+
def end_document
|
20
|
+
end
|
21
|
+
|
22
|
+
###
|
23
|
+
# Called at the beginning of an element
|
24
|
+
# +name+ is the element name
|
25
|
+
# +attrs+ is a list of attributes
|
26
|
+
# +prefix+ is the namespace prefix for the element
|
27
|
+
# +uri+ is the associated namespace URI
|
28
|
+
# +ns+ is a hash of namespace prefix:urls associated with the element
|
29
|
+
def start_element_namespace(name, attrs=nil, prefix=nil, uri=nil, ns=nil)
|
30
|
+
@document = (@current ? @document : Nokogiri::XML::Document.new)
|
31
|
+
@current_ns = (@current ? @current_ns : {})
|
32
|
+
|
33
|
+
element = Nokogiri::XML::Element.new(name, @document)
|
34
|
+
|
35
|
+
if @current
|
36
|
+
@current.add_child element if @current
|
37
|
+
else
|
38
|
+
@document.root = element
|
39
|
+
end
|
40
|
+
|
41
|
+
if ns
|
42
|
+
ns.each do |p, u|
|
43
|
+
@current_ns[u] = element.add_namespace(p, u)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if uri
|
48
|
+
element.namespace = @current_ns[uri] || element.add_namespace(prefix, uri)
|
49
|
+
end
|
50
|
+
|
51
|
+
if attrs
|
52
|
+
attrs.each do |attribute|
|
53
|
+
element[attribute.localname] = attribute.value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if element.named?('stream', STREAM_NS)
|
58
|
+
@stream.received_xml(element)
|
59
|
+
@current = nil
|
60
|
+
else
|
61
|
+
@current = element
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
###
|
66
|
+
# Called at the end of an element
|
67
|
+
# +name+ is the element's name
|
68
|
+
# +prefix+ is the namespace prefix associated with the element
|
69
|
+
# +uri+ is the associated namespace URI
|
70
|
+
def end_element_namespace name, prefix = nil, uri = nil
|
71
|
+
if name == 'stream' and prefix == 'stream' and @current == nil
|
72
|
+
else
|
73
|
+
if Nokogiri::XML::Document === @current.parent or @current.parent.nil?
|
74
|
+
@stream.received_xml(@current)
|
75
|
+
@current = nil
|
76
|
+
else
|
77
|
+
@current = @current.parent
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
###
|
83
|
+
# Characters read between a tag
|
84
|
+
# +string+ contains the character data
|
85
|
+
def characters string
|
86
|
+
@current << Nokogiri::XML::Text.new(string, @document) if @current
|
87
|
+
end
|
88
|
+
|
89
|
+
###
|
90
|
+
# Called when cdata blocks are found
|
91
|
+
# +string+ contains the cdata content
|
92
|
+
def cdata_block string
|
93
|
+
@current << Nokogiri::XML::CDATA.new(@document, string) if @current
|
94
|
+
end
|
95
|
+
|
96
|
+
###
|
97
|
+
# Called on document warnings
|
98
|
+
# +string+ contains the warning
|
99
|
+
def warning string
|
100
|
+
end
|
101
|
+
|
102
|
+
###
|
103
|
+
# Called on document errors
|
104
|
+
# +string+ contains the error
|
105
|
+
def error string
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Nokogiri::XML::Node
|
112
|
+
def named?(name, ns=nil)
|
113
|
+
self.name == name and (ns.nil? or (namespace and namespace.href == ns))
|
114
|
+
end
|
115
|
+
|
116
|
+
def first(xpath, ns={})
|
117
|
+
ns = add_xmpp_base_namespaces_to(ns)
|
118
|
+
element = xpath(xpath, ns).to_a.first
|
119
|
+
if element and block_given?
|
120
|
+
yield(element)
|
121
|
+
else
|
122
|
+
element
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def all(xpath, ns={}, &block)
|
127
|
+
ns = add_xmpp_base_namespaces_to(ns)
|
128
|
+
elements = xpath(xpath, ns)
|
129
|
+
if block_given?
|
130
|
+
elements.each(&block)
|
131
|
+
else
|
132
|
+
elements
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def add_xmpp_base_namespaces_to(ns)
|
139
|
+
{ 'stream' => Cancer::STREAM_NS,
|
140
|
+
'client' => Cancer::CLIENT_NS,
|
141
|
+
'c' => Cancer::CLIENT_NS }.merge(ns)
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
module Support
|
4
|
+
|
5
|
+
def alias_method_chain(target, feature)
|
6
|
+
# Strip out punctuation on predicates or bang methods since
|
7
|
+
# e.g. target?_without_feature is not a valid method name.
|
8
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
9
|
+
yield(aliased_target, punctuation) if block_given?
|
10
|
+
|
11
|
+
with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
|
12
|
+
|
13
|
+
alias_method without_method, target
|
14
|
+
alias_method target, with_method
|
15
|
+
|
16
|
+
case
|
17
|
+
when public_method_defined?(without_method)
|
18
|
+
public target
|
19
|
+
when protected_method_defined?(without_method)
|
20
|
+
protected target
|
21
|
+
when private_method_defined?(without_method)
|
22
|
+
private target
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
module Support
|
4
|
+
module Hash
|
5
|
+
|
6
|
+
def clean!
|
7
|
+
delete_if { |key, value| value.nil? }
|
8
|
+
end
|
9
|
+
|
10
|
+
def clean
|
11
|
+
dup.clean!
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_options
|
15
|
+
self.inject({}) do |options, (key, value)|
|
16
|
+
options[key.to_sym] = value
|
17
|
+
options
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_options!
|
22
|
+
self.keys.each do |key|
|
23
|
+
self[key.to_sym] = self.delete(key)
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
::Hash.send :include, Cancer::Support::Hash
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
module Support
|
4
|
+
module String
|
5
|
+
|
6
|
+
def cmp_ioctet(other)
|
7
|
+
i = 0
|
8
|
+
loop do
|
9
|
+
return 0 if (self.size - i) == 0 and (other.size - i) == 0
|
10
|
+
return -1 if (self.size - i) == 0
|
11
|
+
return 1 if (other.size - i) == 0
|
12
|
+
c = self[i] <=> other[i]
|
13
|
+
return c if c != 0
|
14
|
+
i += 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
::String.send :include, Cancer::Support::String
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
module Cancer
|
3
|
+
module SynchronizedStanza
|
4
|
+
|
5
|
+
def send_and_receive(stanza, *conditions, &proc)
|
6
|
+
lock = nil
|
7
|
+
@mutex.synchronize do
|
8
|
+
conditions.push(proc) if proc
|
9
|
+
lock = Cancer::SynchronizedStanza::Lock.new(*conditions)
|
10
|
+
@receive_locks.push(lock)
|
11
|
+
send(stanza)
|
12
|
+
end
|
13
|
+
r = lock.wait!
|
14
|
+
@receive_locks.delete(lock)
|
15
|
+
if r[:type] == 'error'
|
16
|
+
handle_stanza_errors(r)
|
17
|
+
else
|
18
|
+
return r
|
19
|
+
end
|
20
|
+
ensure
|
21
|
+
@receive_locks.delete(lock)
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_stanza_errors(xml)
|
25
|
+
raise Cancer::StanzaError.build(xml)
|
26
|
+
end
|
27
|
+
|
28
|
+
def handle_synchronized_stanza(xml)
|
29
|
+
@mutex.synchronize do
|
30
|
+
@receive_locks.each do |lock|
|
31
|
+
return true if lock.continue! xml
|
32
|
+
end
|
33
|
+
end
|
34
|
+
(Exception === xml)
|
35
|
+
end
|
36
|
+
|
37
|
+
class Lock
|
38
|
+
def initialize(*conditions)
|
39
|
+
@mutex = Mutex.new
|
40
|
+
@condition = ConditionVariable.new
|
41
|
+
@conditions = conditions
|
42
|
+
@conditions.collect! do |condition|
|
43
|
+
case condition
|
44
|
+
when String then lambda { |xml| !!xml.first(condition) }
|
45
|
+
when Array then lambda { |xml| !!xml.first(*condition) }
|
46
|
+
when Proc then condition
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@conditions.push(lambda { |xml| true })
|
50
|
+
end
|
51
|
+
|
52
|
+
def wait!
|
53
|
+
raise @exception if @exception
|
54
|
+
return @xml if @xml
|
55
|
+
@mutex.synchronize do
|
56
|
+
@condition.wait(@mutex)
|
57
|
+
raise @exception if @exception
|
58
|
+
return @xml
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def continue!(xml)
|
63
|
+
@mutex.synchronize do
|
64
|
+
if Exception === xml
|
65
|
+
@exception = xml
|
66
|
+
@condition.signal
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
unless @conditions.any? { |condition| FalseClass === condition.call(xml) }
|
70
|
+
@xml = xml
|
71
|
+
@condition.signal
|
72
|
+
return true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
require 'thread'
|
3
|
+
require 'monitor'
|
4
|
+
|
5
|
+
module Cancer
|
6
|
+
class ThreadPool < Queue
|
7
|
+
|
8
|
+
# create a new pool and start it as well.
|
9
|
+
def self.start!(size=10, &proc)
|
10
|
+
new(size, &proc).start!
|
11
|
+
end
|
12
|
+
|
13
|
+
# Build a new thread pool.
|
14
|
+
# @param [Fixnum] size the size of the pool
|
15
|
+
# @yield [job] The code to run in for each job on the queue
|
16
|
+
# @yieldparam [Object] job the job which needs to be processed.
|
17
|
+
def initialize(size=10, &proc)
|
18
|
+
extend MonitorMixin
|
19
|
+
@proc = proc
|
20
|
+
@empty_cond = new_cond
|
21
|
+
@pool = []
|
22
|
+
@defered_pool = []
|
23
|
+
@size = size
|
24
|
+
@num_working = 0
|
25
|
+
@exception_handler_proc = nil
|
26
|
+
super()
|
27
|
+
end
|
28
|
+
|
29
|
+
# set a handler for exceptions raised inside the pool threads
|
30
|
+
# @yield [exception] handle exceptions in here
|
31
|
+
# @yieldparam [Exception] exceptions the exception that was raised in side a pool thread.
|
32
|
+
def on_exception(&proc)
|
33
|
+
@exception_handler_proc = proc
|
34
|
+
end
|
35
|
+
|
36
|
+
# push a new job onto the queue.
|
37
|
+
def push(*args)
|
38
|
+
super(args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def rescue_any_exceptions
|
42
|
+
defult_handler = lambda do |e|
|
43
|
+
Cancer.logger.fatal e
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
yield
|
48
|
+
rescue Exception => e
|
49
|
+
begin
|
50
|
+
(@exception_handler_proc || defult_handler).call(e)
|
51
|
+
rescue Exception => e
|
52
|
+
defult_handler.call(e)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# create the threads and start processing jobs.
|
58
|
+
def start!
|
59
|
+
@size.times do
|
60
|
+
@pool.push(Thread.new(self) do |queue|
|
61
|
+
Thread.current.abort_on_exception = true
|
62
|
+
|
63
|
+
loop do
|
64
|
+
rescue_any_exceptions do
|
65
|
+
job = queue.pop
|
66
|
+
queue.synchronize { @num_working += 1 }
|
67
|
+
@proc.call(*job)
|
68
|
+
end
|
69
|
+
queue.synchronize do
|
70
|
+
@num_working -= 1
|
71
|
+
@empty_cond.signal
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end)
|
76
|
+
end
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# wait until the queue is empty.
|
81
|
+
def wait!
|
82
|
+
synchronize do
|
83
|
+
@empty_cond.wait_until do
|
84
|
+
((empty?) and
|
85
|
+
(@num_working == 0) and
|
86
|
+
(@defered_pool.empty?))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# wait until the queue is empty and then stop the pool.
|
92
|
+
def stop!
|
93
|
+
wait!
|
94
|
+
@pool.each { |thread| thread.kill }
|
95
|
+
@pool = []
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
# start a thread outside the thread pool.
|
100
|
+
# @param args the arguments to pass to the thread
|
101
|
+
# @yield [*args] the block to execute inside the new thread.
|
102
|
+
def defer(*args, &proc)
|
103
|
+
@defered_pool.push(Thread.new(self, proc, args) do |queue, proc, args|
|
104
|
+
Thread.current.abort_on_exception = true
|
105
|
+
|
106
|
+
rescue_any_exceptions do
|
107
|
+
proc.call(*args)
|
108
|
+
end
|
109
|
+
queue.synchronize do
|
110
|
+
@defered_pool.delete(Thread.current)
|
111
|
+
@empty_cond.signal
|
112
|
+
end
|
113
|
+
|
114
|
+
end)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|