eventmachine 1.0.0.beta.3 → 1.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +5 -1
  3. data/{docs/GNU → GNU} +0 -0
  4. data/Gemfile +1 -0
  5. data/{docs/COPYING → LICENSE} +0 -0
  6. data/README.md +109 -0
  7. data/Rakefile +8 -0
  8. data/docs/DocumentationGuidesIndex.md +27 -0
  9. data/docs/GettingStarted.md +521 -0
  10. data/docs/{ChangeLog → old/ChangeLog} +0 -0
  11. data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
  12. data/docs/{EPOLL → old/EPOLL} +0 -0
  13. data/docs/{INSTALL → old/INSTALL} +0 -0
  14. data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
  15. data/docs/{LEGAL → old/LEGAL} +0 -0
  16. data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
  17. data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
  18. data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
  19. data/docs/{SMTP → old/SMTP} +0 -0
  20. data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
  21. data/docs/{TODO → old/TODO} +0 -0
  22. data/eventmachine.gemspec +4 -1
  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/{ex_channel.rb → old/ex_channel.rb} +3 -3
  32. data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
  33. data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
  34. data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
  35. data/examples/{helper.rb → old/helper.rb} +0 -0
  36. data/ext/cmain.cpp +3 -3
  37. data/ext/ed.cpp +90 -15
  38. data/ext/ed.h +5 -5
  39. data/ext/em.cpp +47 -55
  40. data/ext/em.h +12 -2
  41. data/ext/pipe.cpp +2 -2
  42. data/ext/project.h +1 -1
  43. data/ext/rubymain.cpp +48 -3
  44. data/ext/ssl.cpp +5 -0
  45. data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
  46. data/lib/em/buftok.rb +35 -63
  47. data/lib/em/callback.rb +43 -11
  48. data/lib/em/channel.rb +21 -14
  49. data/lib/em/completion.rb +304 -0
  50. data/lib/em/connection.rb +339 -209
  51. data/lib/em/deferrable.rb +4 -0
  52. data/lib/em/deferrable/pool.rb +2 -0
  53. data/lib/em/file_watch.rb +37 -18
  54. data/lib/em/iterator.rb +42 -42
  55. data/lib/em/pool.rb +146 -0
  56. data/lib/em/process_watch.rb +5 -4
  57. data/lib/em/processes.rb +8 -4
  58. data/lib/em/protocols/httpclient.rb +22 -11
  59. data/lib/em/protocols/httpclient2.rb +15 -5
  60. data/lib/em/protocols/line_protocol.rb +2 -1
  61. data/lib/em/protocols/memcache.rb +17 -9
  62. data/lib/em/protocols/object_protocol.rb +2 -1
  63. data/lib/em/protocols/postgres3.rb +8 -9
  64. data/lib/em/protocols/smtpclient.rb +19 -11
  65. data/lib/em/protocols/smtpserver.rb +1 -1
  66. data/lib/em/protocols/stomp.rb +8 -6
  67. data/lib/em/protocols/tcptest.rb +3 -2
  68. data/lib/em/pure_ruby.rb +212 -208
  69. data/lib/em/queue.rb +22 -13
  70. data/lib/em/resolver.rb +70 -64
  71. data/lib/em/spawnable.rb +6 -3
  72. data/lib/em/streamer.rb +33 -45
  73. data/lib/em/threaded_resource.rb +90 -0
  74. data/lib/em/timers.rb +6 -2
  75. data/lib/em/version.rb +1 -1
  76. data/lib/eventmachine.rb +538 -602
  77. data/lib/jeventmachine.rb +22 -1
  78. data/tasks/package.rake +12 -2
  79. data/tasks/test.rake +1 -0
  80. data/tests/em_test_helper.rb +12 -3
  81. data/tests/test_completion.rb +177 -0
  82. data/tests/test_epoll.rb +2 -2
  83. data/tests/test_httpclient.rb +9 -9
  84. data/tests/test_httpclient2.rb +11 -9
  85. data/tests/test_ltp.rb +2 -10
  86. data/tests/test_pool.rb +128 -0
  87. data/tests/test_processes.rb +20 -2
  88. data/tests/test_queue.rb +8 -0
  89. data/tests/test_resolver.rb +1 -1
  90. data/tests/test_set_sock_opt.rb +37 -0
  91. data/tests/test_shutdown_hooks.rb +23 -0
  92. data/tests/test_threaded_resource.rb +53 -0
  93. data/tests/test_unbind_reason.rb +31 -0
  94. metadata +262 -192
  95. data/README +0 -81
  96. data/tasks/doc.rake +0 -30
data/ext/em.h CHANGED
@@ -43,6 +43,15 @@ See the file COPYING for complete licensing information.
43
43
  #ifndef RUBY_UBF_IO
44
44
  #define RUBY_UBF_IO RB_UBF_DFL
45
45
  #endif
46
+ #ifndef RSTRING_PTR
47
+ #define RSTRING_PTR(str) RString(str)->ptr
48
+ #endif
49
+ #ifndef RSTRING_LEN
50
+ #define RSTRING_LEN(str) RString(str)->len
51
+ #endif
52
+ #ifndef RSTRING_LENINT
53
+ #define RSTRING_LENINT(str) RSTRING_LEN(str)
54
+ #endif
46
55
  #else
47
56
  #define EmSelect select
48
57
  #endif
@@ -81,6 +90,7 @@ class EventMachine_t
81
90
 
82
91
  void Add (EventableDescriptor*);
83
92
  void Modify (EventableDescriptor*);
93
+ void Deregister (EventableDescriptor*);
84
94
 
85
95
  const unsigned long AttachFD (int, bool);
86
96
  int DetachFD (EventableDescriptor*);
@@ -126,13 +136,13 @@ class EventMachine_t
126
136
  bool UsingEpoll() { return bEpoll; }
127
137
 
128
138
  void QueueHeartbeat(EventableDescriptor*);
129
- void ClearHeartbeat(uint64_t);
139
+ void ClearHeartbeat(uint64_t, EventableDescriptor*);
130
140
 
131
141
  uint64_t GetRealTime();
132
142
 
133
143
  private:
134
144
  bool _RunOnce();
135
- bool _RunTimers();
145
+ void _RunTimers();
136
146
  void _UpdateTime();
137
147
  void _AddNewDescriptors();
138
148
  void _ModifyDescriptors();
@@ -282,7 +282,7 @@ bool PipeDescriptor::SelectForRead()
282
282
  * a pending state, so this is simpler than for the
283
283
  * ConnectionDescriptor object.
284
284
  */
285
- return true;
285
+ return bPaused ? false : true;
286
286
  }
287
287
 
288
288
  /******************************
@@ -295,7 +295,7 @@ bool PipeDescriptor::SelectForWrite()
295
295
  * a pending state, so this is simpler than for the
296
296
  * ConnectionDescriptor object.
297
297
  */
298
- return (GetOutboundDataSize() > 0);
298
+ return (GetOutboundDataSize() > 0) && !bPaused ? true : false;
299
299
  }
300
300
 
301
301
 
@@ -96,7 +96,7 @@ typedef int socklen_t;
96
96
  typedef int pid_t;
97
97
  #endif
98
98
 
99
- #if !defined(_MSC_VER) || _MSC_VER > 1400
99
+ #if !defined(_MSC_VER) || _MSC_VER > 1500
100
100
  #include <stdint.h>
101
101
  #endif
102
102
 
@@ -95,9 +95,13 @@ static inline void event_callback (struct em_event* e)
95
95
  return;
96
96
  }
97
97
  case EM_CONNECTION_ACCEPTED:
98
+ {
99
+ rb_funcall (EmModule, Intern_event_callback, 3, ULONG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
100
+ return;
101
+ }
98
102
  case EM_CONNECTION_UNBOUND:
99
103
  {
100
- rb_funcall (EmModule, Intern_event_callback, 3, ULONG2NUM(signature), INT2FIX(event), data_str ? rb_str_new(data_str,data_num) : ULONG2NUM(data_num));
104
+ rb_funcall (EmModule, Intern_event_callback, 3, ULONG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
101
105
  return;
102
106
  }
103
107
  case EM_CONNECTION_COMPLETED:
@@ -426,8 +430,9 @@ t_set_comm_inactivity_timeout
426
430
  static VALUE t_set_comm_inactivity_timeout (VALUE self, VALUE signature, VALUE timeout)
427
431
  {
428
432
  float ti = RFLOAT_VALUE(timeout);
429
- if (evma_set_comm_inactivity_timeout (NUM2ULONG (signature), ti));
433
+ if (evma_set_comm_inactivity_timeout(NUM2ULONG(signature), ti)) {
430
434
  return Qtrue;
435
+ }
431
436
  return Qfalse;
432
437
  }
433
438
 
@@ -447,8 +452,9 @@ t_set_pending_connect_timeout
447
452
  static VALUE t_set_pending_connect_timeout (VALUE self, VALUE signature, VALUE timeout)
448
453
  {
449
454
  float ti = RFLOAT_VALUE(timeout);
450
- if (evma_set_pending_connect_timeout (NUM2ULONG (signature), ti));
455
+ if (evma_set_pending_connect_timeout(NUM2ULONG(signature), ti)) {
451
456
  return Qtrue;
457
+ }
452
458
  return Qfalse;
453
459
  }
454
460
 
@@ -577,6 +583,44 @@ static VALUE t_get_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optna
577
583
  return rb_str_new(buf, len);
578
584
  }
579
585
 
586
+ /**************
587
+ t_set_sock_opt
588
+ **************/
589
+
590
+ static VALUE t_set_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optname, VALUE optval)
591
+ {
592
+ int fd = evma_get_file_descriptor (NUM2ULONG (signature));
593
+ int level = NUM2INT(lev), option = NUM2INT(optname);
594
+ int i;
595
+ void *v;
596
+ socklen_t len;
597
+
598
+ switch (TYPE(optval)) {
599
+ case T_FIXNUM:
600
+ i = FIX2INT(optval);
601
+ goto numval;
602
+ case T_FALSE:
603
+ i = 0;
604
+ goto numval;
605
+ case T_TRUE:
606
+ i = 1;
607
+ numval:
608
+ v = (void*)&i; len = sizeof(i);
609
+ break;
610
+ default:
611
+ StringValue(optval);
612
+ v = RSTRING_PTR(optval);
613
+ len = RSTRING_LENINT(optval);
614
+ break;
615
+ }
616
+
617
+
618
+ if (setsockopt(fd, level, option, v, len) < 0)
619
+ rb_sys_fail("setsockopt");
620
+
621
+ return INT2FIX(0);
622
+ }
623
+
580
624
  /********************
581
625
  t_is_notify_readable
582
626
  ********************/
@@ -1127,6 +1171,7 @@ extern "C" void Init_rubyeventmachine()
1127
1171
  rb_define_module_function (EmModule, "attach_fd", (VALUE (*)(...))t_attach_fd, 2);
1128
1172
  rb_define_module_function (EmModule, "detach_fd", (VALUE (*)(...))t_detach_fd, 1);
1129
1173
  rb_define_module_function (EmModule, "get_sock_opt", (VALUE (*)(...))t_get_sock_opt, 3);
1174
+ rb_define_module_function (EmModule, "set_sock_opt", (VALUE (*)(...))t_set_sock_opt, 4);
1130
1175
  rb_define_module_function (EmModule, "set_notify_readable", (VALUE (*)(...))t_set_notify_readable, 2);
1131
1176
  rb_define_module_function (EmModule, "set_notify_writable", (VALUE (*)(...))t_set_notify_writable, 2);
1132
1177
  rb_define_module_function (EmModule, "is_notify_readable", (VALUE (*)(...))t_is_notify_readable, 1);
@@ -159,11 +159,14 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str
159
159
  e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
160
160
  else
161
161
  e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
162
+ if (e <= 0) ERR_print_errors_fp(stderr);
162
163
  assert (e > 0);
164
+
163
165
  if (certchainfile.length() > 0)
164
166
  e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
165
167
  else
166
168
  e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
169
+ if (e <= 0) ERR_print_errors_fp(stderr);
167
170
  assert (e > 0);
168
171
  }
169
172
 
@@ -177,10 +180,12 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str
177
180
  int e;
178
181
  if (privkeyfile.length() > 0) {
179
182
  e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
183
+ if (e <= 0) ERR_print_errors_fp(stderr);
180
184
  assert (e > 0);
181
185
  }
182
186
  if (certchainfile.length() > 0) {
183
187
  e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
188
+ if (e <= 0) ERR_print_errors_fp(stderr);
184
189
  assert (e > 0);
185
190
  }
186
191
  }
@@ -442,8 +442,8 @@ public class EmReactor {
442
442
  Connections.get(sig).setCommInactivityTimeout (mills);
443
443
  }
444
444
 
445
- public void sendDatagram (long sig, String data, int length, String recipAddress, int recipPort) {
446
- sendDatagram (sig, ByteBuffer.wrap(data.getBytes()), recipAddress, recipPort);
445
+ public void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort) {
446
+ sendDatagram (sig, ByteBuffer.wrap(data), recipAddress, recipPort);
447
447
  }
448
448
 
449
449
  public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) {
@@ -1,43 +1,29 @@
1
- # BufferedTokenizer - Statefully split input data by a specifiable token
2
- #
3
- # Authors:: Tony Arcieri, Martin Emde
4
- #
5
- #----------------------------------------------------------------------------
6
- #
7
- # Copyright (C) 2006-07 by Tony Arcieri and Martin Emde
8
- #
9
- # Distributed under the Ruby license (http://www.ruby-lang.org/en/LICENSE.txt)
10
- #
11
- #---------------------------------------------------------------------------
12
- #
13
-
14
- # (C)2006 Tony Arcieri, Martin Emde
15
- # Distributed under the Ruby license (http://www.ruby-lang.org/en/LICENSE.txt)
16
-
17
1
  # BufferedTokenizer takes a delimiter upon instantiation, or acts line-based
18
2
  # by default. It allows input to be spoon-fed from some outside source which
19
3
  # receives arbitrary length datagrams which may-or-may-not contain the token
20
4
  # by which entities are delimited.
21
5
  #
22
- # Commonly used to parse lines out of incoming data:
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
23
9
  #
24
- # module LineBufferedConnection
25
- # def receive_data(data)
26
- # (@buffer ||= BufferedTokenizer.new).extract(data).each do |line|
27
- # receive_line(line)
28
- # end
29
- # end
30
- # end
31
-
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
32
22
  class BufferedTokenizer
33
- # New BufferedTokenizers will operate on lines delimited by "\n" by default
34
- # or allow you to specify any delimiter token you so choose, which will then
35
- # be used by String#split to tokenize the input data
23
+ # @param [String] delimiter
24
+ # @param [Integer] size_limit
36
25
  def initialize(delimiter = "\n", size_limit = nil)
37
- # Store the specified delimiter
38
- @delimiter = delimiter
39
-
40
- # Store the specified size limitation
26
+ @delimiter = delimiter
41
27
  @size_limit = size_limit
42
28
 
43
29
  # The input buffer is stored as an array. This is by far the most efficient
@@ -52,14 +38,18 @@ class BufferedTokenizer
52
38
  end
53
39
 
54
40
  # Extract takes an arbitrary string of input data and returns an array of
55
- # tokenized entities, provided there were any available to extract. This
56
- # makes for easy processing of datagrams using a pattern like:
41
+ # tokenized entities, provided there were any available to extract.
57
42
  #
58
- # tokenizer.extract(data).map { |entity| Decode(entity) }.each do ...
43
+ # @example
44
+ #
45
+ # tokenizer.extract(data).
46
+ # map { |entity| Decode(entity) }.each { ... }
47
+ #
48
+ # @param [String] data
59
49
  def extract(data)
60
50
  # Extract token-delimited entities from the input string with the split command.
61
51
  # There's a bit of craftiness here with the -1 parameter. Normally split would
62
- # behave no differently regardless of if the token lies at the very end of the
52
+ # behave no differently regardless of if the token lies at the very end of the
63
53
  # input buffer or not (i.e. a literal edge case) Specifying -1 forces split to
64
54
  # return "" in this case, meaning that the last entry in the list represents a
65
55
  # new segment of data where the token has not been encountered
@@ -70,7 +60,7 @@ class BufferedTokenizer
70
60
  raise 'input buffer full' if @input_size + entities.first.size > @size_limit
71
61
  @input_size += entities.first.size
72
62
  end
73
-
63
+
74
64
  # Move the first entry in the resulting array into the input buffer. It represents
75
65
  # the last segment of a token-delimited entity unless it's the only entry in the list.
76
66
  @input << entities.shift
@@ -85,36 +75,16 @@ class BufferedTokenizer
85
75
  # and add it to our list of discovered entities.
86
76
  entities.unshift @input.join
87
77
 
88
- =begin
89
- # Note added by FC, 10Jul07. This paragraph contains a regression. It breaks
90
- # empty tokens. Think of the empty line that delimits an HTTP header. It will have
91
- # two "\n" delimiters in a row, and this code mishandles the resulting empty token.
92
- # It someone figures out how to fix the problem, we can re-enable this code branch.
93
- # Multi-character token support.
94
- # Split any tokens that were incomplete on the last iteration buf complete now.
95
- entities.map! do |e|
96
- e.split @delimiter, -1
97
- end
98
- # Flatten the resulting array. This has the side effect of removing the empty
99
- # entry at the end that was produced by passing -1 to split. Add it again if
100
- # necessary.
101
- if (entities[-1] == [])
102
- entities.flatten! << []
103
- else
104
- entities.flatten!
105
- end
106
- =end
107
-
108
78
  # Now that we've hit a token, joined the input buffer and added it to the entities
109
79
  # list, we can go ahead and clear the input buffer. All of the segments that were
110
80
  # stored before the join can now be garbage collected.
111
81
  @input.clear
112
-
82
+
113
83
  # The last entity in the list is not token delimited, however, thanks to the -1
114
- # passed to split. It represents the beginning of a new list of as-yet-untokenized
84
+ # passed to split. It represents the beginning of a new list of as-yet-untokenized
115
85
  # data, so we add it to the start of the list.
116
86
  @input << entities.pop
117
-
87
+
118
88
  # Set the new input buffer size, provided we're keeping track
119
89
  @input_size = @input.first.size if @size_limit
120
90
 
@@ -122,16 +92,18 @@ class BufferedTokenizer
122
92
  # in the first place. Hooray!
123
93
  entities
124
94
  end
125
-
95
+
126
96
  # Flush the contents of the input buffer, i.e. return the input buffer even though
127
- # a token has not yet been encountered
97
+ # a token has not yet been encountered.
98
+ #
99
+ # @return [String]
128
100
  def flush
129
101
  buffer = @input.join
130
102
  @input.clear
131
103
  buffer
132
104
  end
133
105
 
134
- # Is the buffer empty?
106
+ # @return [Boolean]
135
107
  def empty?
136
108
  @input.empty?
137
109
  end
@@ -1,26 +1,58 @@
1
1
  module EventMachine
2
- # Utility method for coercing arguments to an object that responds to #call
2
+ # Utility method for coercing arguments to an object that responds to :call.
3
3
  # Accepts an object and a method name to send to, or a block, or an object
4
- # that responds to call.
4
+ # that responds to :call.
5
5
  #
6
- # cb = EM.Callback{ |msg| puts(msg) }
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
7
12
  # cb.call('hello world')
8
13
  #
9
- # cb = EM.Callback(Object, :puts)
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)
10
19
  # cb.call('hello world')
11
20
  #
12
- # cb = EM.Callback(proc{ |msg| puts(msg) })
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
13
26
  # cb.call('hello world')
14
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>]
15
47
  def self.Callback(object = nil, method = nil, &blk)
16
48
  if object && method
17
- lambda { |*args| object.send method, *args }
49
+ lambda { |*args| object.__send__ method, *args }
18
50
  else
19
51
  if object.respond_to? :call
20
52
  object
21
- else
53
+ else
22
54
  blk || raise(ArgumentError)
23
- end
24
- end
25
- end
26
- end
55
+ end # if
56
+ end # if
57
+ end # self.Callback
58
+ end # EventMachine
@@ -1,33 +1,38 @@
1
1
  module EventMachine
2
- # Provides a simple interface to push items to a number of subscribers. The
3
- # channel will schedule all operations on the main reactor thread for thread
4
- # safe reactor operations.
2
+ # Provides a simple thread-safe way to transfer data between (typically) long running
3
+ # tasks in {EventMachine.defer} and event loop thread.
5
4
  #
6
- # This provides a convenient way for connections to consume messages from
7
- # long running code in defer, without threading issues.
5
+ # @example
6
+ #
7
+ # channel = EventMachine::Channel.new
8
+ # sid = channel.subscribe { |msg| p [:got, msg] }
8
9
  #
9
- # channel = EM::Channel.new
10
- # sid = channel.subscribe{ |msg| p [:got, msg] }
11
10
  # channel.push('hello world')
12
11
  # channel.unsubscribe(sid)
13
12
  #
14
- # See examples/ex_channel.rb for a detailed example.
13
+ #
15
14
  class Channel
16
- # Create a new channel
17
15
  def initialize
18
16
  @subs = {}
19
- @uid = 0
17
+ @uid = 0
20
18
  end
21
19
 
22
20
  # Takes any arguments suitable for EM::Callback() and returns a subscriber
23
21
  # id for use when unsubscribing.
22
+ #
23
+ # @return [Integer] Subscribe identifier
24
+ # @see #unsubscribe
24
25
  def subscribe(*a, &b)
25
26
  name = gen_id
26
27
  EM.schedule { @subs[name] = EM::Callback(*a, &b) }
28
+
27
29
  name
28
30
  end
29
31
 
30
- # Removes this subscriber from the list.
32
+ # Removes subscriber from the list.
33
+ #
34
+ # @param [Integer] Subscriber identifier
35
+ # @see #subscribe
31
36
  def unsubscribe(name)
32
37
  EM.schedule { @subs.delete name }
33
38
  end
@@ -39,7 +44,7 @@ module EventMachine
39
44
  end
40
45
  alias << push
41
46
 
42
- # Receive exactly one message from the channel.
47
+ # Fetches one message from the channel.
43
48
  def pop(*a, &b)
44
49
  EM.schedule {
45
50
  name = subscribe do |*args|
@@ -50,8 +55,10 @@ module EventMachine
50
55
  end
51
56
 
52
57
  private
53
- def gen_id # :nodoc:
58
+
59
+ # @private
60
+ def gen_id
54
61
  @uid += 1
55
62
  end
56
63
  end
57
- end
64
+ end