sonixlabs-eventmachine-java 1.0.0.rc.4-java

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.
Files changed (162) hide show
  1. data/.gitignore +22 -0
  2. data/.yardopts +7 -0
  3. data/GNU +281 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +60 -0
  6. data/README.md +109 -0
  7. data/Rakefile +20 -0
  8. data/docs/DocumentationGuidesIndex.md +27 -0
  9. data/docs/GettingStarted.md +521 -0
  10. data/docs/old/ChangeLog +211 -0
  11. data/docs/old/DEFERRABLES +246 -0
  12. data/docs/old/EPOLL +141 -0
  13. data/docs/old/INSTALL +13 -0
  14. data/docs/old/KEYBOARD +42 -0
  15. data/docs/old/LEGAL +25 -0
  16. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  17. data/docs/old/PURE_RUBY +75 -0
  18. data/docs/old/RELEASE_NOTES +94 -0
  19. data/docs/old/SMTP +4 -0
  20. data/docs/old/SPAWNED_PROCESSES +148 -0
  21. data/docs/old/TODO +8 -0
  22. data/eventmachine.gemspec +34 -0
  23. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  24. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  25. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  26. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  27. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  28. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  29. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  30. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  31. data/examples/old/ex_channel.rb +43 -0
  32. data/examples/old/ex_queue.rb +2 -0
  33. data/examples/old/ex_tick_loop_array.rb +15 -0
  34. data/examples/old/ex_tick_loop_counter.rb +32 -0
  35. data/examples/old/helper.rb +2 -0
  36. data/ext/binder.cpp +124 -0
  37. data/ext/binder.h +46 -0
  38. data/ext/cmain.cpp +876 -0
  39. data/ext/ed.cpp +1973 -0
  40. data/ext/ed.h +422 -0
  41. data/ext/em.cpp +2353 -0
  42. data/ext/em.h +239 -0
  43. data/ext/eventmachine.h +127 -0
  44. data/ext/extconf.rb +176 -0
  45. data/ext/fastfilereader/extconf.rb +103 -0
  46. data/ext/fastfilereader/mapper.cpp +214 -0
  47. data/ext/fastfilereader/mapper.h +59 -0
  48. data/ext/fastfilereader/rubymain.cpp +127 -0
  49. data/ext/kb.cpp +79 -0
  50. data/ext/page.cpp +107 -0
  51. data/ext/page.h +51 -0
  52. data/ext/pipe.cpp +347 -0
  53. data/ext/project.h +156 -0
  54. data/ext/rubymain.cpp +1297 -0
  55. data/ext/ssl.cpp +468 -0
  56. data/ext/ssl.h +94 -0
  57. data/java/.classpath +8 -0
  58. data/java/.project +17 -0
  59. data/java/src/com/rubyeventmachine/EmReactor.java +588 -0
  60. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  61. data/java/src/com/rubyeventmachine/EventableChannel.java +70 -0
  62. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +195 -0
  63. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +364 -0
  64. data/lib/em/buftok.rb +110 -0
  65. data/lib/em/callback.rb +58 -0
  66. data/lib/em/channel.rb +64 -0
  67. data/lib/em/completion.rb +304 -0
  68. data/lib/em/connection.rb +712 -0
  69. data/lib/em/deferrable.rb +210 -0
  70. data/lib/em/deferrable/pool.rb +2 -0
  71. data/lib/em/file_watch.rb +73 -0
  72. data/lib/em/future.rb +61 -0
  73. data/lib/em/iterator.rb +270 -0
  74. data/lib/em/messages.rb +66 -0
  75. data/lib/em/pool.rb +151 -0
  76. data/lib/em/process_watch.rb +45 -0
  77. data/lib/em/processes.rb +123 -0
  78. data/lib/em/protocols.rb +36 -0
  79. data/lib/em/protocols/header_and_content.rb +138 -0
  80. data/lib/em/protocols/httpclient.rb +279 -0
  81. data/lib/em/protocols/httpclient2.rb +600 -0
  82. data/lib/em/protocols/line_and_text.rb +125 -0
  83. data/lib/em/protocols/line_protocol.rb +29 -0
  84. data/lib/em/protocols/linetext2.rb +161 -0
  85. data/lib/em/protocols/memcache.rb +331 -0
  86. data/lib/em/protocols/object_protocol.rb +46 -0
  87. data/lib/em/protocols/postgres3.rb +246 -0
  88. data/lib/em/protocols/saslauth.rb +175 -0
  89. data/lib/em/protocols/smtpclient.rb +365 -0
  90. data/lib/em/protocols/smtpserver.rb +640 -0
  91. data/lib/em/protocols/socks4.rb +66 -0
  92. data/lib/em/protocols/stomp.rb +202 -0
  93. data/lib/em/protocols/tcptest.rb +54 -0
  94. data/lib/em/pure_ruby.rb +1017 -0
  95. data/lib/em/queue.rb +71 -0
  96. data/lib/em/resolver.rb +192 -0
  97. data/lib/em/spawnable.rb +84 -0
  98. data/lib/em/streamer.rb +118 -0
  99. data/lib/em/threaded_resource.rb +90 -0
  100. data/lib/em/tick_loop.rb +85 -0
  101. data/lib/em/timers.rb +61 -0
  102. data/lib/em/version.rb +3 -0
  103. data/lib/eventmachine.rb +1532 -0
  104. data/lib/jeventmachine.rb +284 -0
  105. data/lib/sonixlabs-eventmachine-java.rb +1 -0
  106. data/rakelib/cpp.rake_example +77 -0
  107. data/rakelib/package.rake +98 -0
  108. data/rakelib/test.rake +8 -0
  109. data/tests/client.crt +31 -0
  110. data/tests/client.key +51 -0
  111. data/tests/em_test_helper.rb +64 -0
  112. data/tests/test_attach.rb +126 -0
  113. data/tests/test_basic.rb +294 -0
  114. data/tests/test_channel.rb +62 -0
  115. data/tests/test_completion.rb +177 -0
  116. data/tests/test_connection_count.rb +33 -0
  117. data/tests/test_defer.rb +18 -0
  118. data/tests/test_deferrable.rb +35 -0
  119. data/tests/test_epoll.rb +130 -0
  120. data/tests/test_error_handler.rb +38 -0
  121. data/tests/test_exc.rb +28 -0
  122. data/tests/test_file_watch.rb +65 -0
  123. data/tests/test_futures.rb +170 -0
  124. data/tests/test_get_sock_opt.rb +37 -0
  125. data/tests/test_handler_check.rb +35 -0
  126. data/tests/test_hc.rb +155 -0
  127. data/tests/test_httpclient.rb +190 -0
  128. data/tests/test_httpclient2.rb +128 -0
  129. data/tests/test_idle_connection.rb +23 -0
  130. data/tests/test_inactivity_timeout.rb +54 -0
  131. data/tests/test_kb.rb +34 -0
  132. data/tests/test_ltp.rb +138 -0
  133. data/tests/test_ltp2.rb +288 -0
  134. data/tests/test_next_tick.rb +104 -0
  135. data/tests/test_object_protocol.rb +36 -0
  136. data/tests/test_pause.rb +78 -0
  137. data/tests/test_pending_connect_timeout.rb +52 -0
  138. data/tests/test_pool.rb +194 -0
  139. data/tests/test_process_watch.rb +48 -0
  140. data/tests/test_processes.rb +128 -0
  141. data/tests/test_proxy_connection.rb +180 -0
  142. data/tests/test_pure.rb +88 -0
  143. data/tests/test_queue.rb +50 -0
  144. data/tests/test_resolver.rb +55 -0
  145. data/tests/test_running.rb +14 -0
  146. data/tests/test_sasl.rb +47 -0
  147. data/tests/test_send_file.rb +217 -0
  148. data/tests/test_servers.rb +33 -0
  149. data/tests/test_set_sock_opt.rb +37 -0
  150. data/tests/test_shutdown_hooks.rb +23 -0
  151. data/tests/test_smtpclient.rb +55 -0
  152. data/tests/test_smtpserver.rb +57 -0
  153. data/tests/test_spawn.rb +293 -0
  154. data/tests/test_ssl_args.rb +78 -0
  155. data/tests/test_ssl_methods.rb +48 -0
  156. data/tests/test_ssl_verify.rb +82 -0
  157. data/tests/test_threaded_resource.rb +53 -0
  158. data/tests/test_tick_loop.rb +59 -0
  159. data/tests/test_timers.rb +123 -0
  160. data/tests/test_ud.rb +8 -0
  161. data/tests/test_unbind_reason.rb +48 -0
  162. metadata +301 -0
@@ -0,0 +1,110 @@
1
+ # BufferedTokenizer takes a delimiter upon instantiation, or acts line-based
2
+ # by default. It allows input to be spoon-fed from some outside source which
3
+ # receives arbitrary length datagrams which may-or-may-not contain the token
4
+ # by which entities are delimited.
5
+ #
6
+ # By default, new BufferedTokenizers will operate on lines delimited by "\n" by default
7
+ # or allow you to specify any delimiter token you so choose, which will then
8
+ # be used by String#split to tokenize the input data
9
+ #
10
+ # @example Using BufferedTokernizer to parse lines out of incoming data
11
+ #
12
+ # module LineBufferedConnection
13
+ # def receive_data(data)
14
+ # (@buffer ||= BufferedTokenizer.new).extract(data).each do |line|
15
+ # receive_line(line)
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ # @author Tony Arcieri
21
+ # @author Martin Emde
22
+ class BufferedTokenizer
23
+ # @param [String] delimiter
24
+ # @param [Integer] size_limit
25
+ def initialize(delimiter = "\n", size_limit = nil)
26
+ @delimiter = delimiter
27
+ @size_limit = size_limit
28
+
29
+ # The input buffer is stored as an array. This is by far the most efficient
30
+ # approach given language constraints (in C a linked list would be a more
31
+ # appropriate data structure). Segments of input data are stored in a list
32
+ # which is only joined when a token is reached, substantially reducing the
33
+ # number of objects required for the operation.
34
+ @input = []
35
+
36
+ # Size of the input buffer
37
+ @input_size = 0
38
+ end
39
+
40
+ # Extract takes an arbitrary string of input data and returns an array of
41
+ # tokenized entities, provided there were any available to extract.
42
+ #
43
+ # @example
44
+ #
45
+ # tokenizer.extract(data).
46
+ # map { |entity| Decode(entity) }.each { ... }
47
+ #
48
+ # @param [String] data
49
+ def extract(data)
50
+ # Extract token-delimited entities from the input string with the split command.
51
+ # There's a bit of craftiness here with the -1 parameter. Normally split would
52
+ # behave no differently regardless of if the token lies at the very end of the
53
+ # input buffer or not (i.e. a literal edge case) Specifying -1 forces split to
54
+ # return "" in this case, meaning that the last entry in the list represents a
55
+ # new segment of data where the token has not been encountered
56
+ entities = data.split @delimiter, -1
57
+
58
+ # Check to see if the buffer has exceeded capacity, if we're imposing a limit
59
+ if @size_limit
60
+ raise 'input buffer full' if @input_size + entities.first.size > @size_limit
61
+ @input_size += entities.first.size
62
+ end
63
+
64
+ # Move the first entry in the resulting array into the input buffer. It represents
65
+ # the last segment of a token-delimited entity unless it's the only entry in the list.
66
+ @input << entities.shift
67
+
68
+ # If the resulting array from the split is empty, the token was not encountered
69
+ # (not even at the end of the buffer). Since we've encountered no token-delimited
70
+ # entities this go-around, return an empty array.
71
+ return [] if entities.empty?
72
+
73
+ # At this point, we've hit a token, or potentially multiple tokens. Now we can bring
74
+ # together all the data we've buffered from earlier calls without hitting a token,
75
+ # and add it to our list of discovered entities.
76
+ entities.unshift @input.join
77
+
78
+ # Now that we've hit a token, joined the input buffer and added it to the entities
79
+ # list, we can go ahead and clear the input buffer. All of the segments that were
80
+ # stored before the join can now be garbage collected.
81
+ @input.clear
82
+
83
+ # The last entity in the list is not token delimited, however, thanks to the -1
84
+ # passed to split. It represents the beginning of a new list of as-yet-untokenized
85
+ # data, so we add it to the start of the list.
86
+ @input << entities.pop
87
+
88
+ # Set the new input buffer size, provided we're keeping track
89
+ @input_size = @input.first.size if @size_limit
90
+
91
+ # Now we're left with the list of extracted token-delimited entities we wanted
92
+ # in the first place. Hooray!
93
+ entities
94
+ end
95
+
96
+ # Flush the contents of the input buffer, i.e. return the input buffer even though
97
+ # a token has not yet been encountered.
98
+ #
99
+ # @return [String]
100
+ def flush
101
+ buffer = @input.join
102
+ @input.clear
103
+ buffer
104
+ end
105
+
106
+ # @return [Boolean]
107
+ def empty?
108
+ @input.empty?
109
+ end
110
+ end
@@ -0,0 +1,58 @@
1
+ module EventMachine
2
+ # Utility method for coercing arguments to an object that responds to :call.
3
+ # Accepts an object and a method name to send to, or a block, or an object
4
+ # that responds to :call.
5
+ #
6
+ # @example EventMachine.Callback used with a block. Returns that block.
7
+ #
8
+ # cb = EventMachine.Callback do |msg|
9
+ # puts(msg)
10
+ # end
11
+ # # returned object is a callable
12
+ # cb.call('hello world')
13
+ #
14
+ #
15
+ # @example EventMachine.Callback used with an object (to be more specific, class object) and a method name, returns an object that responds to #call
16
+ #
17
+ # cb = EventMachine.Callback(Object, :puts)
18
+ # # returned object is a callable that delegates to Kernel#puts (in this case Object.puts)
19
+ # cb.call('hello world')
20
+ #
21
+ #
22
+ # @example EventMachine.Callback used with an object that responds to #call. Returns the argument.
23
+ #
24
+ # cb = EventMachine.Callback(proc{ |msg| puts(msg) })
25
+ # # returned object is a callable
26
+ # cb.call('hello world')
27
+ #
28
+ #
29
+ # @overload Callback(object, method)
30
+ # Wraps `method` invocation on `object` into an object that responds to #call that proxies all the arguments to that method
31
+ # @param [Object] Object to invoke method on
32
+ # @param [Symbol] Method name
33
+ # @return [<#call>] An object that responds to #call that takes any number of arguments and invokes method on object with those arguments
34
+ #
35
+ # @overload Callback(object)
36
+ # Returns callable object as is, without any coercion
37
+ # @param [<#call>] An object that responds to #call
38
+ # @return [<#call>] Its argument
39
+ #
40
+ # @overload Callback(&block)
41
+ # Returns block passed to it without any coercion
42
+ # @return [<#call>] Block passed to this method
43
+ #
44
+ # @raise [ArgumentError] When argument doesn't respond to #call, method name is missing or when invoked without arguments and block isn't given
45
+ #
46
+ # @return [<#call>]
47
+ def self.Callback(object = nil, method = nil, &blk)
48
+ if object && method
49
+ lambda { |*args| object.__send__ method, *args }
50
+ else
51
+ if object.respond_to? :call
52
+ object
53
+ else
54
+ blk || raise(ArgumentError)
55
+ end # if
56
+ end # if
57
+ end # self.Callback
58
+ end # EventMachine
@@ -0,0 +1,64 @@
1
+ module EventMachine
2
+ # Provides a simple thread-safe way to transfer data between (typically) long running
3
+ # tasks in {EventMachine.defer} and event loop thread.
4
+ #
5
+ # @example
6
+ #
7
+ # channel = EventMachine::Channel.new
8
+ # sid = channel.subscribe { |msg| p [:got, msg] }
9
+ #
10
+ # channel.push('hello world')
11
+ # channel.unsubscribe(sid)
12
+ #
13
+ #
14
+ class Channel
15
+ def initialize
16
+ @subs = {}
17
+ @uid = 0
18
+ end
19
+
20
+ # Takes any arguments suitable for EM::Callback() and returns a subscriber
21
+ # id for use when unsubscribing.
22
+ #
23
+ # @return [Integer] Subscribe identifier
24
+ # @see #unsubscribe
25
+ def subscribe(*a, &b)
26
+ name = gen_id
27
+ EM.schedule { @subs[name] = EM::Callback(*a, &b) }
28
+
29
+ name
30
+ end
31
+
32
+ # Removes subscriber from the list.
33
+ #
34
+ # @param [Integer] Subscriber identifier
35
+ # @see #subscribe
36
+ def unsubscribe(name)
37
+ EM.schedule { @subs.delete name }
38
+ end
39
+
40
+ # Add items to the channel, which are pushed out to all subscribers.
41
+ def push(*items)
42
+ items = items.dup
43
+ EM.schedule { items.each { |i| @subs.values.each { |s| s.call i } } }
44
+ end
45
+ alias << push
46
+
47
+ # Fetches one message from the channel.
48
+ def pop(*a, &b)
49
+ EM.schedule {
50
+ name = subscribe do |*args|
51
+ unsubscribe(name)
52
+ EM::Callback(*a, &b).call(*args)
53
+ end
54
+ }
55
+ end
56
+
57
+ private
58
+
59
+ # @private
60
+ def gen_id
61
+ @uid += 1
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,304 @@
1
+ # = EM::Completion
2
+ #
3
+ # A completion is a callback container for various states of completion. In
4
+ # it's most basic form it has a start state and a finish state.
5
+ #
6
+ # This implementation includes some hold-back from the EM::Deferrable
7
+ # interface in order to be compatible - but it has a much cleaner
8
+ # implementation.
9
+ #
10
+ # In general it is preferred that this implementation be used as a state
11
+ # callback container than EM::DefaultDeferrable or other classes including
12
+ # EM::Deferrable. This is because it is generally more sane to keep this level
13
+ # of state in a dedicated state-back container. This generally leads to more
14
+ # malleable interfaces and software designs, as well as eradicating nasty bugs
15
+ # that result from abstraction leakage.
16
+ #
17
+ # == Basic Usage
18
+ #
19
+ # As already mentioned, the basic usage of a Completion is simply for its two
20
+ # final states, :succeeded and :failed.
21
+ #
22
+ # An asynchronous operation will complete at some future point in time, and
23
+ # users often want to react to this event. API authors will want to expose
24
+ # some common interface to react to these events.
25
+ #
26
+ # In the following example, the user wants to know when a short lived
27
+ # connection has completed its exchange with the remote server. The simple
28
+ # protocol just waits for an ack to its message.
29
+ #
30
+ # class Protocol < EM::Connection
31
+ # include EM::P::LineText2
32
+ #
33
+ # def initialize(message, completion)
34
+ # @message, @completion = message, completion
35
+ # @completion.completion { close_connection }
36
+ # @completion.timeout(1, :timeout)
37
+ # end
38
+ #
39
+ # def post_init
40
+ # send_data(@message)
41
+ # end
42
+ #
43
+ # def receive_line(line)
44
+ # case line
45
+ # when /ACK/i
46
+ # @completion.succeed line
47
+ # when /ERR/i
48
+ # @completion.fail :error, line
49
+ # else
50
+ # @completion.fail :unknown, line
51
+ # end
52
+ # end
53
+ #
54
+ # def unbind
55
+ # @completion.fail :disconnected unless @completion.completed?
56
+ # end
57
+ # end
58
+ #
59
+ # class API
60
+ # attr_reader :host, :port
61
+ #
62
+ # def initialize(host = 'example.org', port = 8000)
63
+ # @host, @port = host, port
64
+ # end
65
+ #
66
+ # def request(message)
67
+ # completion = EM::Deferrable::Completion.new
68
+ # EM.connect(host, port, Protocol, message, completion)
69
+ # completion
70
+ # end
71
+ # end
72
+ #
73
+ # api = API.new
74
+ # completion = api.request('stuff')
75
+ # completion.callback do |line|
76
+ # puts "API responded with: #{line}"
77
+ # end
78
+ # completion.errback do |type, line|
79
+ # case type
80
+ # when :error
81
+ # puts "API error: #{line}"
82
+ # when :unknown
83
+ # puts "API returned unknown response: #{line}"
84
+ # when :disconnected
85
+ # puts "API server disconnected prematurely"
86
+ # when :timeout
87
+ # puts "API server did not respond in a timely fashion"
88
+ # end
89
+ # end
90
+ #
91
+ # == Advanced Usage
92
+ #
93
+ # This completion implementation also supports more state callbacks and
94
+ # arbitrary states (unlike the original Deferrable API). This allows for basic
95
+ # stateful process encapsulation. One might use this to setup state callbacks
96
+ # for various states in an exchange like in the basic usage example, except
97
+ # where the applicaiton could be made to react to "connected" and
98
+ # "disconnected" states additionally.
99
+ #
100
+ # class Protocol < EM::Connection
101
+ # def initialize(completion)
102
+ # @response = []
103
+ # @completion = completion
104
+ # @completion.stateback(:disconnected) do
105
+ # @completion.succeed @response.join
106
+ # end
107
+ # end
108
+ #
109
+ # def connection_completed
110
+ # @host, @port = Socket.unpack_sockaddr_in get_peername
111
+ # @completion.change_state(:connected, @host, @port)
112
+ # send_data("GET http://example.org/ HTTP/1.0\r\n\r\n")
113
+ # end
114
+ #
115
+ # def receive_data(data)
116
+ # @response << data
117
+ # end
118
+ #
119
+ # def unbind
120
+ # @completion.change_state(:disconnected, @host, @port)
121
+ # end
122
+ # end
123
+ #
124
+ # completion = EM::Deferrable::Completion.new
125
+ # completion.stateback(:connected) do |host, port|
126
+ # puts "Connected to #{host}:#{port}"
127
+ # end
128
+ # completion.stateback(:disconnected) do |host, port|
129
+ # puts "Disconnected from #{host}:#{port}"
130
+ # end
131
+ # completion.callback do |response|
132
+ # puts response
133
+ # end
134
+ #
135
+ # EM.connect('example.org', 80, Protocol, completion)
136
+ #
137
+ # == Timeout
138
+ #
139
+ # The Completion also has a timeout. The timeout is global and is not aware of
140
+ # states apart from completion states. The timeout is only engaged if #timeout
141
+ # is called, and it will call fail if it is reached.
142
+ #
143
+ # == Completion states
144
+ #
145
+ # By default there are two completion states, :succeeded and :failed. These
146
+ # states can be modified by subclassing and overrding the #completion_states
147
+ # method. Completion states are special, in that callbacks for all completion
148
+ # states are explcitly cleared when a completion state is entered. This
149
+ # prevents errors that could arise from accidental unterminated timeouts, and
150
+ # other such user errors.
151
+ #
152
+ # == Other notes
153
+ #
154
+ # Several APIs have been carried over from EM::Deferrable for compatibility
155
+ # reasons during a transitionary period. Specifically cancel_errback and
156
+ # cancel_callback are implemented, but their usage is to be strongly
157
+ # discouraged. Due to the already complex nature of reaction systems, dynamic
158
+ # callback deletion only makes the problem much worse. It is always better to
159
+ # add correct conditionals to the callback code, or use more states, than to
160
+ # address such implementaiton issues with conditional callbacks.
161
+
162
+ module EventMachine
163
+
164
+ class Completion
165
+ # This is totally not used (re-implemented), it's here in case people check
166
+ # for kind_of?
167
+ include EventMachine::Deferrable
168
+
169
+ attr_reader :state, :value
170
+
171
+ def initialize
172
+ @state = :unknown
173
+ @callbacks = Hash.new { |h,k| h[k] = [] }
174
+ @value = []
175
+ @timeout_timer = nil
176
+ end
177
+
178
+ # Enter the :succeeded state, setting the result value if given.
179
+ def succeed(*args)
180
+ change_state(:succeeded, *args)
181
+ end
182
+ # The old EM method:
183
+ alias set_deferred_success succeed
184
+
185
+ # Enter the :failed state, setting the result value if given.
186
+ def fail(*args)
187
+ change_state(:failed, *args)
188
+ end
189
+ # The old EM method:
190
+ alias set_deferred_failure fail
191
+
192
+ # Statebacks are called when you enter (or are in) the named state.
193
+ def stateback(state, *a, &b)
194
+ # The following is quite unfortunate special casing for :completed
195
+ # statebacks, but it's a necessary evil for latent completion
196
+ # definitions.
197
+
198
+ if :completed == state || !completed? || @state == state
199
+ @callbacks[state] << EM::Callback(*a, &b)
200
+ end
201
+ execute_callbacks
202
+ self
203
+ end
204
+
205
+ # Callbacks are called when you enter (or are in) a :succeeded state.
206
+ def callback(*a, &b)
207
+ stateback(:succeeded, *a, &b)
208
+ end
209
+
210
+ # Errbacks are called when you enter (or are in) a :failed state.
211
+ def errback(*a, &b)
212
+ stateback(:failed, *a, &b)
213
+ end
214
+
215
+ # Completions are called when you enter (or are in) either a :failed or a
216
+ # :succeeded state. They are stored as a special (reserved) state called
217
+ # :completed.
218
+ def completion(*a, &b)
219
+ stateback(:completed, *a, &b)
220
+ end
221
+
222
+ # Enter a new state, setting the result value if given. If the state is one
223
+ # of :succeeded or :failed, then :completed callbacks will also be called.
224
+ def change_state(state, *args)
225
+ @value = args
226
+ @state = state
227
+
228
+ EM.schedule { execute_callbacks }
229
+ end
230
+
231
+ # The old EM method:
232
+ alias set_deferred_status change_state
233
+
234
+ # Indicates that we've reached some kind of completion state, by default
235
+ # this is :succeeded or :failed. Due to these semantics, the :completed
236
+ # state is reserved for internal use.
237
+ def completed?
238
+ completion_states.any? { |s| state == s }
239
+ end
240
+
241
+ # Completion states simply returns a list of completion states, by default
242
+ # this is :succeeded and :failed.
243
+ def completion_states
244
+ [:succeeded, :failed]
245
+ end
246
+
247
+ # Schedule a time which if passes before we enter a completion state, this
248
+ # deferrable will be failed with the given arguments.
249
+ def timeout(time, *args)
250
+ cancel_timeout
251
+ @timeout_timer = EM::Timer.new(time) do
252
+ fail(*args) unless completed?
253
+ end
254
+ end
255
+
256
+ # Disable the timeout
257
+ def cancel_timeout
258
+ if @timeout_timer
259
+ @timeout_timer.cancel
260
+ @timeout_timer = nil
261
+ end
262
+ end
263
+
264
+ # Remove an errback. N.B. Some errbacks cannot be deleted. Usage is NOT
265
+ # recommended, this is an anti-pattern.
266
+ def cancel_errback(*a, &b)
267
+ @callbacks[:failed].delete(EM::Callback(*a, &b))
268
+ end
269
+
270
+ # Remove a callback. N.B. Some callbacks cannot be deleted. Usage is NOT
271
+ # recommended, this is an anti-pattern.
272
+ def cancel_callback(*a, &b)
273
+ @callbacks[:succeeded].delete(EM::Callback(*a, &b))
274
+ end
275
+
276
+ private
277
+ # Execute all callbacks for the current state. If in a completed state, then
278
+ # call any statebacks associated with the completed state.
279
+ def execute_callbacks
280
+ execute_state_callbacks(state)
281
+ if completed?
282
+ execute_state_callbacks(:completed)
283
+ clear_dead_callbacks
284
+ cancel_timeout
285
+ end
286
+ end
287
+
288
+ # Iterate all callbacks for a given state, and remove then call them.
289
+ def execute_state_callbacks(state)
290
+ while callback = @callbacks[state].shift
291
+ callback.call(*value)
292
+ end
293
+ end
294
+
295
+ # If we enter a completion state, clear other completion states after all
296
+ # callback chains are completed. This means that operation specific
297
+ # callbacks can't be dual-called, which is most common user error.
298
+ def clear_dead_callbacks
299
+ completion_states.each do |state|
300
+ @callbacks[state].clear
301
+ end
302
+ end
303
+ end
304
+ end