cancer 0.1.0.a1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|