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.
- data/.gitignore +5 -0
- data/.yardopts +5 -1
- data/{docs/GNU → GNU} +0 -0
- data/Gemfile +1 -0
- data/{docs/COPYING → LICENSE} +0 -0
- data/README.md +109 -0
- data/Rakefile +8 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/{ChangeLog → old/ChangeLog} +0 -0
- data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
- data/docs/{EPOLL → old/EPOLL} +0 -0
- data/docs/{INSTALL → old/INSTALL} +0 -0
- data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
- data/docs/{LEGAL → old/LEGAL} +0 -0
- data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
- data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
- data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
- data/docs/{SMTP → old/SMTP} +0 -0
- data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
- data/docs/{TODO → old/TODO} +0 -0
- data/eventmachine.gemspec +4 -1
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
- data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
- data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
- data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
- data/examples/{helper.rb → old/helper.rb} +0 -0
- data/ext/cmain.cpp +3 -3
- data/ext/ed.cpp +90 -15
- data/ext/ed.h +5 -5
- data/ext/em.cpp +47 -55
- data/ext/em.h +12 -2
- data/ext/pipe.cpp +2 -2
- data/ext/project.h +1 -1
- data/ext/rubymain.cpp +48 -3
- data/ext/ssl.cpp +5 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
- data/lib/em/buftok.rb +35 -63
- data/lib/em/callback.rb +43 -11
- data/lib/em/channel.rb +21 -14
- data/lib/em/completion.rb +304 -0
- data/lib/em/connection.rb +339 -209
- data/lib/em/deferrable.rb +4 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +37 -18
- data/lib/em/iterator.rb +42 -42
- data/lib/em/pool.rb +146 -0
- data/lib/em/process_watch.rb +5 -4
- data/lib/em/processes.rb +8 -4
- data/lib/em/protocols/httpclient.rb +22 -11
- data/lib/em/protocols/httpclient2.rb +15 -5
- data/lib/em/protocols/line_protocol.rb +2 -1
- data/lib/em/protocols/memcache.rb +17 -9
- data/lib/em/protocols/object_protocol.rb +2 -1
- data/lib/em/protocols/postgres3.rb +8 -9
- data/lib/em/protocols/smtpclient.rb +19 -11
- data/lib/em/protocols/smtpserver.rb +1 -1
- data/lib/em/protocols/stomp.rb +8 -6
- data/lib/em/protocols/tcptest.rb +3 -2
- data/lib/em/pure_ruby.rb +212 -208
- data/lib/em/queue.rb +22 -13
- data/lib/em/resolver.rb +70 -64
- data/lib/em/spawnable.rb +6 -3
- data/lib/em/streamer.rb +33 -45
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/timers.rb +6 -2
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +538 -602
- data/lib/jeventmachine.rb +22 -1
- data/tasks/package.rake +12 -2
- data/tasks/test.rake +1 -0
- data/tests/em_test_helper.rb +12 -3
- data/tests/test_completion.rb +177 -0
- data/tests/test_epoll.rb +2 -2
- data/tests/test_httpclient.rb +9 -9
- data/tests/test_httpclient2.rb +11 -9
- data/tests/test_ltp.rb +2 -10
- data/tests/test_pool.rb +128 -0
- data/tests/test_processes.rb +20 -2
- data/tests/test_queue.rb +8 -0
- data/tests/test_resolver.rb +1 -1
- data/tests/test_set_sock_opt.rb +37 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_threaded_resource.rb +53 -0
- data/tests/test_unbind_reason.rb +31 -0
- metadata +262 -192
- data/README +0 -81
- 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
|
-
|
145
|
+
void _RunTimers();
|
136
146
|
void _UpdateTime();
|
137
147
|
void _AddNewDescriptors();
|
138
148
|
void _ModifyDescriptors();
|
data/ext/pipe.cpp
CHANGED
@@ -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
|
|
data/ext/project.h
CHANGED
data/ext/rubymain.cpp
CHANGED
@@ -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),
|
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
|
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
|
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);
|
data/ext/ssl.cpp
CHANGED
@@ -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,
|
446
|
-
sendDatagram (sig, ByteBuffer.wrap(data
|
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) {
|
data/lib/em/buftok.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
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
|
-
#
|
34
|
-
#
|
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
|
-
|
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.
|
56
|
-
# makes for easy processing of datagrams using a pattern like:
|
41
|
+
# tokenized entities, provided there were any available to extract.
|
57
42
|
#
|
58
|
-
#
|
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
|
-
#
|
106
|
+
# @return [Boolean]
|
135
107
|
def empty?
|
136
108
|
@input.empty?
|
137
109
|
end
|
data/lib/em/callback.rb
CHANGED
@@ -1,26 +1,58 @@
|
|
1
1
|
module EventMachine
|
2
|
-
# Utility method for coercing arguments to an object that responds to
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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.
|
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
|
data/lib/em/channel.rb
CHANGED
@@ -1,33 +1,38 @@
|
|
1
1
|
module EventMachine
|
2
|
-
# Provides a simple
|
3
|
-
#
|
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
|
-
#
|
7
|
-
#
|
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
|
-
#
|
13
|
+
#
|
15
14
|
class Channel
|
16
|
-
# Create a new channel
|
17
15
|
def initialize
|
18
16
|
@subs = {}
|
19
|
-
@uid
|
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
|
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
|
-
#
|
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
|
-
|
58
|
+
|
59
|
+
# @private
|
60
|
+
def gen_id
|
54
61
|
@uid += 1
|
55
62
|
end
|
56
63
|
end
|
57
|
-
end
|
64
|
+
end
|