wj_eventmachine 1.3.0.dev.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +179 -0
- data/GNU +281 -0
- data/LICENSE +60 -0
- data/README.md +110 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +520 -0
- data/docs/old/ChangeLog +211 -0
- data/docs/old/DEFERRABLES +246 -0
- data/docs/old/EPOLL +141 -0
- data/docs/old/INSTALL +13 -0
- data/docs/old/KEYBOARD +42 -0
- data/docs/old/LEGAL +25 -0
- data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
- data/docs/old/PURE_RUBY +75 -0
- data/docs/old/RELEASE_NOTES +94 -0
- data/docs/old/SMTP +4 -0
- data/docs/old/SPAWNED_PROCESSES +148 -0
- data/docs/old/TODO +8 -0
- 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/old/ex_channel.rb +43 -0
- data/examples/old/ex_queue.rb +2 -0
- data/examples/old/ex_tick_loop_array.rb +15 -0
- data/examples/old/ex_tick_loop_counter.rb +32 -0
- data/examples/old/helper.rb +2 -0
- data/ext/binder.cpp +124 -0
- data/ext/binder.h +52 -0
- data/ext/cmain.cpp +1046 -0
- data/ext/ed.cpp +2238 -0
- data/ext/ed.h +460 -0
- data/ext/em.cpp +2378 -0
- data/ext/em.h +266 -0
- data/ext/eventmachine.h +152 -0
- data/ext/extconf.rb +285 -0
- data/ext/fastfilereader/extconf.rb +120 -0
- data/ext/fastfilereader/mapper.cpp +214 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +126 -0
- data/ext/kb.cpp +79 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +354 -0
- data/ext/project.h +174 -0
- data/ext/rubymain.cpp +1610 -0
- data/ext/ssl.cpp +627 -0
- data/ext/ssl.h +103 -0
- data/ext/wait_for_single_fd.h +36 -0
- data/java/.classpath +8 -0
- data/java/.project +17 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
- data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
- data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
- data/lib/em/buftok.rb +59 -0
- data/lib/em/callback.rb +58 -0
- data/lib/em/channel.rb +69 -0
- data/lib/em/completion.rb +307 -0
- data/lib/em/connection.rb +776 -0
- data/lib/em/deferrable.rb +210 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +73 -0
- data/lib/em/future.rb +61 -0
- data/lib/em/io_streamer.rb +68 -0
- data/lib/em/iterator.rb +252 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/pool.rb +151 -0
- data/lib/em/process_watch.rb +45 -0
- data/lib/em/processes.rb +123 -0
- data/lib/em/protocols.rb +37 -0
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +303 -0
- data/lib/em/protocols/httpclient2.rb +602 -0
- data/lib/em/protocols/line_and_text.rb +125 -0
- data/lib/em/protocols/line_protocol.rb +33 -0
- data/lib/em/protocols/linetext2.rb +179 -0
- data/lib/em/protocols/memcache.rb +331 -0
- data/lib/em/protocols/object_protocol.rb +46 -0
- data/lib/em/protocols/postgres3.rb +246 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +394 -0
- data/lib/em/protocols/smtpserver.rb +666 -0
- data/lib/em/protocols/socks4.rb +66 -0
- data/lib/em/protocols/stomp.rb +205 -0
- data/lib/em/protocols/tcptest.rb +54 -0
- data/lib/em/pure_ruby.rb +1299 -0
- data/lib/em/queue.rb +80 -0
- data/lib/em/resolver.rb +232 -0
- data/lib/em/spawnable.rb +84 -0
- data/lib/em/streamer.rb +118 -0
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +61 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1602 -0
- data/lib/jeventmachine.rb +318 -0
- data/rakelib/package.rake +120 -0
- data/rakelib/test.rake +6 -0
- data/rakelib/test_pure.rake +11 -0
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/dhparam.pem +13 -0
- data/tests/em_ssl_handlers.rb +153 -0
- data/tests/em_test_helper.rb +198 -0
- data/tests/jruby/test_jeventmachine.rb +38 -0
- data/tests/test_attach.rb +199 -0
- data/tests/test_basic.rb +321 -0
- data/tests/test_channel.rb +75 -0
- data/tests/test_completion.rb +178 -0
- data/tests/test_connection_count.rb +83 -0
- data/tests/test_connection_write.rb +35 -0
- data/tests/test_defer.rb +35 -0
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_epoll.rb +141 -0
- data/tests/test_error_handler.rb +38 -0
- data/tests/test_exc.rb +37 -0
- data/tests/test_file_watch.rb +86 -0
- data/tests/test_fork.rb +75 -0
- data/tests/test_futures.rb +170 -0
- data/tests/test_handler_check.rb +35 -0
- data/tests/test_hc.rb +155 -0
- data/tests/test_httpclient.rb +238 -0
- data/tests/test_httpclient2.rb +132 -0
- data/tests/test_idle_connection.rb +31 -0
- data/tests/test_inactivity_timeout.rb +102 -0
- data/tests/test_io_streamer.rb +47 -0
- data/tests/test_ipv4.rb +96 -0
- data/tests/test_ipv6.rb +107 -0
- data/tests/test_iterator.rb +122 -0
- data/tests/test_kb.rb +28 -0
- data/tests/test_keepalive.rb +113 -0
- data/tests/test_line_protocol.rb +33 -0
- data/tests/test_ltp.rb +155 -0
- data/tests/test_ltp2.rb +332 -0
- data/tests/test_many_fds.rb +21 -0
- data/tests/test_next_tick.rb +104 -0
- data/tests/test_object_protocol.rb +36 -0
- data/tests/test_pause.rb +109 -0
- data/tests/test_pending_connect_timeout.rb +52 -0
- data/tests/test_pool.rb +196 -0
- data/tests/test_process_watch.rb +50 -0
- data/tests/test_processes.rb +128 -0
- data/tests/test_proxy_connection.rb +180 -0
- data/tests/test_pure.rb +156 -0
- data/tests/test_queue.rb +64 -0
- data/tests/test_resolver.rb +129 -0
- data/tests/test_running.rb +14 -0
- data/tests/test_sasl.rb +46 -0
- data/tests/test_send_file.rb +217 -0
- data/tests/test_servers.rb +32 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_smtpclient.rb +75 -0
- data/tests/test_smtpserver.rb +90 -0
- data/tests/test_sock_opt.rb +53 -0
- data/tests/test_spawn.rb +290 -0
- data/tests/test_ssl_args.rb +41 -0
- data/tests/test_ssl_dhparam.rb +57 -0
- data/tests/test_ssl_ecdh_curve.rb +57 -0
- data/tests/test_ssl_extensions.rb +24 -0
- data/tests/test_ssl_methods.rb +31 -0
- data/tests/test_ssl_protocols.rb +190 -0
- data/tests/test_ssl_verify.rb +52 -0
- data/tests/test_stomp.rb +38 -0
- data/tests/test_system.rb +46 -0
- data/tests/test_threaded_resource.rb +68 -0
- data/tests/test_tick_loop.rb +58 -0
- data/tests/test_timers.rb +150 -0
- data/tests/test_ud.rb +8 -0
- data/tests/test_unbind_reason.rb +40 -0
- metadata +384 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
package com.rubyeventmachine;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import java.net.InetSocketAddress;
|
|
5
|
+
import java.net.SocketAddress;
|
|
6
|
+
import java.nio.ByteBuffer;
|
|
7
|
+
import java.nio.channels.SocketChannel;
|
|
8
|
+
import java.security.KeyManagementException;
|
|
9
|
+
import java.security.NoSuchAlgorithmException;
|
|
10
|
+
|
|
11
|
+
public class NullEmReactor implements EmReactorInterface
|
|
12
|
+
{
|
|
13
|
+
public void eventCallback(long sig, int eventType, ByteBuffer data, long data2)
|
|
14
|
+
{
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public void eventCallback(long sig, int eventType, ByteBuffer data)
|
|
19
|
+
{
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public void run()
|
|
24
|
+
{
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public void stop()
|
|
29
|
+
{
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public long installOneshotTimer(long milliseconds)
|
|
34
|
+
{
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public long startTcpServer(SocketAddress sa) throws EmReactorException
|
|
39
|
+
{
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public long startTcpServer(String address, int port) throws EmReactorException
|
|
44
|
+
{
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public void stopTcpServer(long signature) throws IOException
|
|
49
|
+
{
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public long openUdpSocket(InetSocketAddress address) throws IOException
|
|
54
|
+
{
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public long openUdpSocket(String address, int port) throws IOException
|
|
59
|
+
{
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public void sendData(long sig, ByteBuffer bb) throws IOException
|
|
64
|
+
{
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public void sendData(long sig, byte[] data) throws IOException
|
|
69
|
+
{
|
|
70
|
+
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public void setCommInactivityTimeout(long sig, long mills)
|
|
74
|
+
{
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public void sendDatagram(long sig, byte[] data, int length, String recipAddress, int recipPort)
|
|
79
|
+
{
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public void sendDatagram(long sig, ByteBuffer bb, String recipAddress, int recipPort)
|
|
84
|
+
{
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public long connectTcpServer(String address, int port)
|
|
89
|
+
{
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public long connectTcpServer(String bindAddr, int bindPort, String address, int port)
|
|
94
|
+
{
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public void closeConnection(long sig, boolean afterWriting)
|
|
99
|
+
{
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public void signalLoopbreak()
|
|
104
|
+
{
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public void startTls(long sig) throws NoSuchAlgorithmException, KeyManagementException
|
|
109
|
+
{
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public void setTimerQuantum(int mills)
|
|
114
|
+
{
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public Object[] getPeerName(long sig)
|
|
119
|
+
{
|
|
120
|
+
return new Object[0];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public long attachChannel(SocketChannel sc, boolean watch_mode)
|
|
124
|
+
{
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public SocketChannel detachChannel(long sig)
|
|
129
|
+
{
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public void setNotifyReadable(long sig, boolean mode)
|
|
134
|
+
{
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public void setNotifyWritable(long sig, boolean mode)
|
|
139
|
+
{
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public boolean isNotifyReadable(long sig)
|
|
144
|
+
{
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public boolean isNotifyWritable(long sig)
|
|
149
|
+
{
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public int getConnectionCount()
|
|
154
|
+
{
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
package com.rubyeventmachine;
|
|
2
|
+
|
|
3
|
+
import java.io.IOException;
|
|
4
|
+
import java.nio.ByteBuffer;
|
|
5
|
+
import java.nio.channels.ClosedChannelException;
|
|
6
|
+
|
|
7
|
+
public class NullEventableChannel implements EventableChannel
|
|
8
|
+
{
|
|
9
|
+
public void scheduleOutboundData(ByteBuffer bb)
|
|
10
|
+
{
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public void scheduleOutboundDatagram(ByteBuffer bb, String recipAddress, int recipPort)
|
|
14
|
+
{
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public boolean scheduleClose(boolean afterWriting)
|
|
18
|
+
{
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public void startTls()
|
|
23
|
+
{
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public long getBinding()
|
|
27
|
+
{
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public void readInboundData(ByteBuffer dst) throws IOException
|
|
32
|
+
{
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public void register() throws ClosedChannelException
|
|
36
|
+
{
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public void close()
|
|
40
|
+
{
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public boolean writeOutboundData() throws IOException
|
|
44
|
+
{
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public void setCommInactivityTimeout(long seconds)
|
|
49
|
+
{
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public Object[] getPeerName()
|
|
53
|
+
{
|
|
54
|
+
return new Object[0];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public Object[] getSockName()
|
|
58
|
+
{
|
|
59
|
+
return new Object[0];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public boolean isWatchOnly()
|
|
63
|
+
{
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public boolean isNotifyReadable()
|
|
68
|
+
{
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public boolean isNotifyWritable()
|
|
73
|
+
{
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public long getOutboundDataSize ()
|
|
78
|
+
{
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
data/lib/em/buftok.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
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. In this respect it's ideally paired with
|
|
5
|
+
# something like EventMachine (http://rubyeventmachine.com/).
|
|
6
|
+
class BufferedTokenizer
|
|
7
|
+
# New BufferedTokenizers will operate on lines delimited by a delimiter,
|
|
8
|
+
# which is by default the global input delimiter $/ ("\n").
|
|
9
|
+
#
|
|
10
|
+
# The input buffer is stored as an array. This is by far the most efficient
|
|
11
|
+
# approach given language constraints (in C a linked list would be a more
|
|
12
|
+
# appropriate data structure). Segments of input data are stored in a list
|
|
13
|
+
# which is only joined when a token is reached, substantially reducing the
|
|
14
|
+
# number of objects required for the operation.
|
|
15
|
+
def initialize(delimiter = $/)
|
|
16
|
+
@delimiter = delimiter
|
|
17
|
+
@input = []
|
|
18
|
+
@tail = ''
|
|
19
|
+
@trim = @delimiter.length - 1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Extract takes an arbitrary string of input data and returns an array of
|
|
23
|
+
# tokenized entities, provided there were any available to extract. This
|
|
24
|
+
# makes for easy processing of datagrams using a pattern like:
|
|
25
|
+
#
|
|
26
|
+
# tokenizer.extract(data).map { |entity| Decode(entity) }.each do ...
|
|
27
|
+
#
|
|
28
|
+
# Using -1 makes split to return "" if the token is at the end of
|
|
29
|
+
# the string, meaning the last element is the start of the next chunk.
|
|
30
|
+
def extract(data)
|
|
31
|
+
if @trim > 0
|
|
32
|
+
tail_end = @tail.slice!(-@trim, @trim) # returns nil if string is too short
|
|
33
|
+
data = tail_end + data if tail_end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@input << @tail
|
|
37
|
+
entities = data.split(@delimiter, -1)
|
|
38
|
+
@tail = entities.shift
|
|
39
|
+
|
|
40
|
+
unless entities.empty?
|
|
41
|
+
@input << @tail
|
|
42
|
+
entities.unshift @input.join
|
|
43
|
+
@input.clear
|
|
44
|
+
@tail = entities.pop
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
entities
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Flush the contents of the input buffer, i.e. return the input buffer even though
|
|
51
|
+
# a token has not yet been encountered
|
|
52
|
+
def flush
|
|
53
|
+
@input << @tail
|
|
54
|
+
buffer = @input.join
|
|
55
|
+
@input.clear
|
|
56
|
+
@tail = "" # @tail.clear is slightly faster, but not supported on 1.8.7
|
|
57
|
+
buffer
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/em/callback.rb
ADDED
|
@@ -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
|
data/lib/em/channel.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
# Return the number of current subscribers.
|
|
21
|
+
def num_subscribers
|
|
22
|
+
return @subs.size
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Takes any arguments suitable for EM::Callback() and returns a subscriber
|
|
26
|
+
# id for use when unsubscribing.
|
|
27
|
+
#
|
|
28
|
+
# @return [Integer] Subscribe identifier
|
|
29
|
+
# @see #unsubscribe
|
|
30
|
+
def subscribe(*a, &b)
|
|
31
|
+
name = gen_id
|
|
32
|
+
EM.schedule { @subs[name] = EM::Callback(*a, &b) }
|
|
33
|
+
|
|
34
|
+
name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Removes subscriber from the list.
|
|
38
|
+
#
|
|
39
|
+
# @param [Integer] Subscriber identifier
|
|
40
|
+
# @see #subscribe
|
|
41
|
+
def unsubscribe(name)
|
|
42
|
+
EM.schedule { @subs.delete name }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Add items to the channel, which are pushed out to all subscribers.
|
|
46
|
+
def push(*items)
|
|
47
|
+
items = items.dup
|
|
48
|
+
EM.schedule { items.each { |i| @subs.values.each { |s| s.call i } } }
|
|
49
|
+
end
|
|
50
|
+
alias << push
|
|
51
|
+
|
|
52
|
+
# Fetches one message from the channel.
|
|
53
|
+
def pop(*a, &b)
|
|
54
|
+
EM.schedule {
|
|
55
|
+
name = subscribe do |*args|
|
|
56
|
+
unsubscribe(name)
|
|
57
|
+
EM::Callback(*a, &b).call(*args)
|
|
58
|
+
end
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# @private
|
|
65
|
+
def gen_id
|
|
66
|
+
@uid += 1
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
module EventMachine
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# An EM::Completion instance is a callback container for various states of
|
|
5
|
+
# completion. In its most basic form it has a start state and a finish state.
|
|
6
|
+
#
|
|
7
|
+
# This implementation includes some hold-back from the EM::Deferrable
|
|
8
|
+
# interface in order to be compatible - but it has a much cleaner
|
|
9
|
+
# implementation.
|
|
10
|
+
#
|
|
11
|
+
# In general it is preferred that this implementation be used as a state
|
|
12
|
+
# callback container than EM::DefaultDeferrable or other classes including
|
|
13
|
+
# EM::Deferrable. This is because it is generally more sane to keep this level
|
|
14
|
+
# of state in a dedicated state-back container. This generally leads to more
|
|
15
|
+
# malleable interfaces and software designs, as well as eradicating nasty bugs
|
|
16
|
+
# that result from abstraction leakage.
|
|
17
|
+
#
|
|
18
|
+
# == Basic Usage
|
|
19
|
+
#
|
|
20
|
+
# As already mentioned, the basic usage of a Completion is simply for its two
|
|
21
|
+
# final states, :succeeded and :failed.
|
|
22
|
+
#
|
|
23
|
+
# An asynchronous operation will complete at some future point in time, and
|
|
24
|
+
# users often want to react to this event. API authors will want to expose
|
|
25
|
+
# some common interface to react to these events.
|
|
26
|
+
#
|
|
27
|
+
# In the following example, the user wants to know when a short lived
|
|
28
|
+
# connection has completed its exchange with the remote server. The simple
|
|
29
|
+
# protocol just waits for an ack to its message.
|
|
30
|
+
#
|
|
31
|
+
# ```ruby
|
|
32
|
+
# class Protocol < EM::Connection
|
|
33
|
+
# include EM::P::LineText2
|
|
34
|
+
#
|
|
35
|
+
# def initialize(message, completion)
|
|
36
|
+
# @message, @completion = message, completion
|
|
37
|
+
# @completion.completion { close_connection }
|
|
38
|
+
# @completion.timeout(1, :timeout)
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# def post_init
|
|
42
|
+
# send_data(@message)
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# def receive_line(line)
|
|
46
|
+
# case line
|
|
47
|
+
# when /ACK/i
|
|
48
|
+
# @completion.succeed line
|
|
49
|
+
# when /ERR/i
|
|
50
|
+
# @completion.fail :error, line
|
|
51
|
+
# else
|
|
52
|
+
# @completion.fail :unknown, line
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# def unbind
|
|
57
|
+
# @completion.fail :disconnected unless @completion.completed?
|
|
58
|
+
# end
|
|
59
|
+
# end
|
|
60
|
+
#
|
|
61
|
+
# class API
|
|
62
|
+
# attr_reader :host, :port
|
|
63
|
+
#
|
|
64
|
+
# def initialize(host = 'example.org', port = 8000)
|
|
65
|
+
# @host, @port = host, port
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# def request(message)
|
|
69
|
+
# completion = EM::Deferrable::Completion.new
|
|
70
|
+
# EM.connect(host, port, Protocol, message, completion)
|
|
71
|
+
# completion
|
|
72
|
+
# end
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# api = API.new
|
|
76
|
+
# completion = api.request('stuff')
|
|
77
|
+
# completion.callback do |line|
|
|
78
|
+
# puts "API responded with: #{line}"
|
|
79
|
+
# end
|
|
80
|
+
# completion.errback do |type, line|
|
|
81
|
+
# case type
|
|
82
|
+
# when :error
|
|
83
|
+
# puts "API error: #{line}"
|
|
84
|
+
# when :unknown
|
|
85
|
+
# puts "API returned unknown response: #{line}"
|
|
86
|
+
# when :disconnected
|
|
87
|
+
# puts "API server disconnected prematurely"
|
|
88
|
+
# when :timeout
|
|
89
|
+
# puts "API server did not respond in a timely fashion"
|
|
90
|
+
# end
|
|
91
|
+
# end
|
|
92
|
+
# ```
|
|
93
|
+
#
|
|
94
|
+
# == Advanced Usage
|
|
95
|
+
#
|
|
96
|
+
# This completion implementation also supports more state callbacks and
|
|
97
|
+
# arbitrary states (unlike the original Deferrable API). This allows for basic
|
|
98
|
+
# stateful process encapsulation. One might use this to setup state callbacks
|
|
99
|
+
# for various states in an exchange like in the basic usage example, except
|
|
100
|
+
# where the applicaiton could be made to react to "connected" and
|
|
101
|
+
# "disconnected" states additionally.
|
|
102
|
+
#
|
|
103
|
+
# ```ruby
|
|
104
|
+
# class Protocol < EM::Connection
|
|
105
|
+
# def initialize(completion)
|
|
106
|
+
# @response = []
|
|
107
|
+
# @completion = completion
|
|
108
|
+
# @completion.stateback(:disconnected) do
|
|
109
|
+
# @completion.succeed @response.join
|
|
110
|
+
# end
|
|
111
|
+
# end
|
|
112
|
+
#
|
|
113
|
+
# def connection_completed
|
|
114
|
+
# @host, @port = Socket.unpack_sockaddr_in get_peername
|
|
115
|
+
# @completion.change_state(:connected, @host, @port)
|
|
116
|
+
# send_data("GET http://example.org/ HTTP/1.0\r\n\r\n")
|
|
117
|
+
# end
|
|
118
|
+
#
|
|
119
|
+
# def receive_data(data)
|
|
120
|
+
# @response << data
|
|
121
|
+
# end
|
|
122
|
+
#
|
|
123
|
+
# def unbind
|
|
124
|
+
# @completion.change_state(:disconnected, @host, @port)
|
|
125
|
+
# end
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# completion = EM::Deferrable::Completion.new
|
|
129
|
+
# completion.stateback(:connected) do |host, port|
|
|
130
|
+
# puts "Connected to #{host}:#{port}"
|
|
131
|
+
# end
|
|
132
|
+
# completion.stateback(:disconnected) do |host, port|
|
|
133
|
+
# puts "Disconnected from #{host}:#{port}"
|
|
134
|
+
# end
|
|
135
|
+
# completion.callback do |response|
|
|
136
|
+
# puts response
|
|
137
|
+
# end
|
|
138
|
+
#
|
|
139
|
+
# EM.connect('example.org', 80, Protocol, completion)
|
|
140
|
+
# ```
|
|
141
|
+
#
|
|
142
|
+
# == Timeout
|
|
143
|
+
#
|
|
144
|
+
# The Completion also has a timeout. The timeout is global and is not aware of
|
|
145
|
+
# states apart from completion states. The timeout is only engaged if #timeout
|
|
146
|
+
# is called, and it will call fail if it is reached.
|
|
147
|
+
#
|
|
148
|
+
# == Completion states
|
|
149
|
+
#
|
|
150
|
+
# By default there are two completion states, :succeeded and :failed. These
|
|
151
|
+
# states can be modified by subclassing and overrding the #completion_states
|
|
152
|
+
# method. Completion states are special, in that callbacks for all completion
|
|
153
|
+
# states are explcitly cleared when a completion state is entered. This
|
|
154
|
+
# prevents errors that could arise from accidental unterminated timeouts, and
|
|
155
|
+
# other such user errors.
|
|
156
|
+
#
|
|
157
|
+
# == Other notes
|
|
158
|
+
#
|
|
159
|
+
# Several APIs have been carried over from EM::Deferrable for compatibility
|
|
160
|
+
# reasons during a transitionary period. Specifically cancel_errback and
|
|
161
|
+
# cancel_callback are implemented, but their usage is to be strongly
|
|
162
|
+
# discouraged. Due to the already complex nature of reaction systems, dynamic
|
|
163
|
+
# callback deletion only makes the problem much worse. It is always better to
|
|
164
|
+
# add correct conditionals to the callback code, or use more states, than to
|
|
165
|
+
# address such implementaiton issues with conditional callbacks.
|
|
166
|
+
|
|
167
|
+
class Completion
|
|
168
|
+
# This is totally not used (re-implemented), it's here in case people check
|
|
169
|
+
# for kind_of?
|
|
170
|
+
include EventMachine::Deferrable
|
|
171
|
+
|
|
172
|
+
attr_reader :state, :value
|
|
173
|
+
|
|
174
|
+
def initialize
|
|
175
|
+
@state = :unknown
|
|
176
|
+
@callbacks = Hash.new { |h,k| h[k] = [] }
|
|
177
|
+
@value = []
|
|
178
|
+
@timeout_timer = nil
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Enter the :succeeded state, setting the result value if given.
|
|
182
|
+
def succeed(*args)
|
|
183
|
+
change_state(:succeeded, *args)
|
|
184
|
+
end
|
|
185
|
+
# The old EM method:
|
|
186
|
+
alias set_deferred_success succeed
|
|
187
|
+
|
|
188
|
+
# Enter the :failed state, setting the result value if given.
|
|
189
|
+
def fail(*args)
|
|
190
|
+
change_state(:failed, *args)
|
|
191
|
+
end
|
|
192
|
+
# The old EM method:
|
|
193
|
+
alias set_deferred_failure fail
|
|
194
|
+
|
|
195
|
+
# Statebacks are called when you enter (or are in) the named state.
|
|
196
|
+
def stateback(state, *a, &b)
|
|
197
|
+
# The following is quite unfortunate special casing for :completed
|
|
198
|
+
# statebacks, but it's a necessary evil for latent completion
|
|
199
|
+
# definitions.
|
|
200
|
+
|
|
201
|
+
if :completed == state || !completed? || @state == state
|
|
202
|
+
@callbacks[state] << EM::Callback(*a, &b)
|
|
203
|
+
end
|
|
204
|
+
execute_callbacks
|
|
205
|
+
self
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Callbacks are called when you enter (or are in) a :succeeded state.
|
|
209
|
+
def callback(*a, &b)
|
|
210
|
+
stateback(:succeeded, *a, &b)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Errbacks are called when you enter (or are in) a :failed state.
|
|
214
|
+
def errback(*a, &b)
|
|
215
|
+
stateback(:failed, *a, &b)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Completions are called when you enter (or are in) either a :failed or a
|
|
219
|
+
# :succeeded state. They are stored as a special (reserved) state called
|
|
220
|
+
# :completed.
|
|
221
|
+
def completion(*a, &b)
|
|
222
|
+
stateback(:completed, *a, &b)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Enter a new state, setting the result value if given. If the state is one
|
|
226
|
+
# of :succeeded or :failed, then :completed callbacks will also be called.
|
|
227
|
+
def change_state(state, *args)
|
|
228
|
+
@value = args
|
|
229
|
+
@state = state
|
|
230
|
+
|
|
231
|
+
EM.schedule { execute_callbacks }
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# The old EM method:
|
|
235
|
+
alias set_deferred_status change_state
|
|
236
|
+
|
|
237
|
+
# Indicates that we've reached some kind of completion state, by default
|
|
238
|
+
# this is :succeeded or :failed. Due to these semantics, the :completed
|
|
239
|
+
# state is reserved for internal use.
|
|
240
|
+
def completed?
|
|
241
|
+
completion_states.any? { |s| state == s }
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Completion states simply returns a list of completion states, by default
|
|
245
|
+
# this is :succeeded and :failed.
|
|
246
|
+
def completion_states
|
|
247
|
+
[:succeeded, :failed]
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Schedule a time which if passes before we enter a completion state, this
|
|
251
|
+
# deferrable will be failed with the given arguments.
|
|
252
|
+
def timeout(time, *args)
|
|
253
|
+
cancel_timeout
|
|
254
|
+
@timeout_timer = EM::Timer.new(time) do
|
|
255
|
+
fail(*args) unless completed?
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Disable the timeout
|
|
260
|
+
def cancel_timeout
|
|
261
|
+
if @timeout_timer
|
|
262
|
+
@timeout_timer.cancel
|
|
263
|
+
@timeout_timer = nil
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Remove an errback. N.B. Some errbacks cannot be deleted. Usage is NOT
|
|
268
|
+
# recommended, this is an anti-pattern.
|
|
269
|
+
def cancel_errback(*a, &b)
|
|
270
|
+
@callbacks[:failed].delete(EM::Callback(*a, &b))
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Remove a callback. N.B. Some callbacks cannot be deleted. Usage is NOT
|
|
274
|
+
# recommended, this is an anti-pattern.
|
|
275
|
+
def cancel_callback(*a, &b)
|
|
276
|
+
@callbacks[:succeeded].delete(EM::Callback(*a, &b))
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
private
|
|
280
|
+
# Execute all callbacks for the current state. If in a completed state, then
|
|
281
|
+
# call any statebacks associated with the completed state.
|
|
282
|
+
def execute_callbacks
|
|
283
|
+
execute_state_callbacks(state)
|
|
284
|
+
if completed?
|
|
285
|
+
execute_state_callbacks(:completed)
|
|
286
|
+
clear_dead_callbacks
|
|
287
|
+
cancel_timeout
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Iterate all callbacks for a given state, and remove then call them.
|
|
292
|
+
def execute_state_callbacks(state)
|
|
293
|
+
while callback = @callbacks[state].shift
|
|
294
|
+
callback.call(*value)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# If we enter a completion state, clear other completion states after all
|
|
299
|
+
# callback chains are completed. This means that operation specific
|
|
300
|
+
# callbacks can't be dual-called, which is most common user error.
|
|
301
|
+
def clear_dead_callbacks
|
|
302
|
+
completion_states.each do |state|
|
|
303
|
+
@callbacks[state].clear
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|