eventmachine 0.12.8 → 0.12.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +2 -1
  2. data/Rakefile +155 -45
  3. data/eventmachine.gemspec +4 -5
  4. data/ext/binder.cpp +13 -14
  5. data/ext/binder.h +5 -7
  6. data/ext/cmain.cpp +184 -42
  7. data/ext/cplusplus.cpp +20 -20
  8. data/ext/ed.cpp +242 -81
  9. data/ext/ed.h +39 -22
  10. data/ext/em.cpp +127 -108
  11. data/ext/em.h +27 -18
  12. data/ext/emwin.cpp +3 -3
  13. data/ext/eventmachine.h +49 -38
  14. data/ext/eventmachine_cpp.h +4 -4
  15. data/ext/extconf.rb +28 -13
  16. data/ext/fastfilereader/extconf.rb +11 -5
  17. data/ext/project.h +12 -1
  18. data/ext/rubymain.cpp +222 -103
  19. data/ext/ssl.cpp +3 -3
  20. data/ext/ssl.h +2 -2
  21. data/java/src/com/rubyeventmachine/EmReactor.java +396 -249
  22. data/java/src/com/rubyeventmachine/EventableChannel.java +16 -4
  23. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +23 -5
  24. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +181 -61
  25. data/java/src/com/rubyeventmachine/{Application.java → application/Application.java} +25 -31
  26. data/java/src/com/rubyeventmachine/{Connection.java → application/Connection.java} +2 -2
  27. data/java/src/com/rubyeventmachine/{ConnectionFactory.java → application/ConnectionFactory.java} +1 -1
  28. data/java/src/com/rubyeventmachine/{DefaultConnectionFactory.java → application/DefaultConnectionFactory.java} +2 -2
  29. data/java/src/com/rubyeventmachine/{PeriodicTimer.java → application/PeriodicTimer.java} +1 -1
  30. data/java/src/com/rubyeventmachine/{Timer.java → application/Timer.java} +1 -1
  31. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +1 -0
  32. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +4 -2
  33. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +1 -1
  34. data/java/src/com/rubyeventmachine/tests/TestServers.java +1 -0
  35. data/java/src/com/rubyeventmachine/tests/TestTimers.java +1 -0
  36. data/lib/em/connection.rb +71 -12
  37. data/lib/em/deferrable.rb +5 -0
  38. data/lib/em/protocols.rb +1 -0
  39. data/lib/em/protocols/httpclient2.rb +8 -0
  40. data/lib/em/protocols/line_and_text.rb +0 -1
  41. data/lib/em/protocols/linetext2.rb +1 -0
  42. data/lib/em/protocols/object_protocol.rb +8 -2
  43. data/lib/em/protocols/smtpclient.rb +42 -16
  44. data/lib/em/protocols/socks4.rb +66 -0
  45. data/lib/em/queue.rb +1 -1
  46. data/lib/em/timers.rb +2 -1
  47. data/lib/em/version.rb +1 -1
  48. data/lib/eventmachine.rb +125 -169
  49. data/lib/jeventmachine.rb +124 -9
  50. data/tasks/{cpp.rake → cpp.rake_example} +0 -0
  51. data/tests/test_attach.rb +29 -4
  52. data/tests/test_basic.rb +1 -2
  53. data/tests/test_connection_count.rb +10 -20
  54. data/tests/test_epoll.rb +0 -2
  55. data/tests/test_get_sock_opt.rb +30 -0
  56. data/tests/test_httpclient2.rb +3 -3
  57. data/tests/test_inactivity_timeout.rb +21 -1
  58. data/tests/test_ltp.rb +0 -6
  59. data/tests/test_next_tick.rb +0 -2
  60. data/tests/test_pause.rb +70 -0
  61. data/tests/test_pending_connect_timeout.rb +48 -0
  62. data/tests/test_ssl_args.rb +16 -5
  63. data/tests/test_timers.rb +22 -1
  64. metadata +14 -12
  65. data/tasks/project.rake +0 -79
  66. data/tasks/tests.rake +0 -193
@@ -208,7 +208,7 @@ SslContext_t::~SslContext_t()
208
208
  SslBox_t::SslBox_t
209
209
  ******************/
210
210
 
211
- SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const char *binding):
211
+ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding):
212
212
  bIsServer (is_server),
213
213
  bHandshakeCompleted (false),
214
214
  bVerifyPeer (verify_peer),
@@ -433,7 +433,7 @@ ssl_verify_wrapper
433
433
 
434
434
  extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
435
435
  {
436
- const char *binding;
436
+ unsigned long binding;
437
437
  X509 *cert;
438
438
  SSL *ssl;
439
439
  BUF_MEM *buf;
@@ -442,7 +442,7 @@ extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
442
442
 
443
443
  cert = X509_STORE_CTX_get_current_cert(ctx);
444
444
  ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
445
- binding = (const char*) SSL_get_ex_data(ssl, 0);
445
+ binding = (unsigned long) SSL_get_ex_data(ssl, 0);
446
446
 
447
447
  out = BIO_new(BIO_s_mem());
448
448
  PEM_write_bio_X509(out, cert);
data/ext/ssl.h CHANGED
@@ -57,7 +57,7 @@ class SslBox_t
57
57
  class SslBox_t
58
58
  {
59
59
  public:
60
- SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const char *binding);
60
+ SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding);
61
61
  virtual ~SslBox_t();
62
62
 
63
63
  int PutPlaintext (const char*, int);
@@ -75,9 +75,9 @@ class SslBox_t
75
75
  protected:
76
76
  SslContext_t *Context;
77
77
 
78
- bool bVerifyPeer;
79
78
  bool bIsServer;
80
79
  bool bHandshakeCompleted;
80
+ bool bVerifyPeer;
81
81
  SSL *pSSL;
82
82
  BIO *pbioRead;
83
83
  BIO *pbioWrite;
@@ -2,7 +2,7 @@
2
2
  * $Id$
3
3
  *
4
4
  * Author:: Francis Cianfrocca (gmail: blackhedd)
5
- * Homepage:: http://rubyeventmachine.com
5
+ * Homepage:: http://rubyeventmachine.com
6
6
  * Date:: 15 Jul 2007
7
7
  *
8
8
  * See EventMachine and EventMachine::Connection for documentation and
@@ -37,332 +37,425 @@ import java.util.concurrent.atomic.*;
37
37
  import java.security.*;
38
38
 
39
39
  public class EmReactor {
40
-
41
-
42
40
  public final int EM_TIMER_FIRED = 100;
43
41
  public final int EM_CONNECTION_READ = 101;
44
42
  public final int EM_CONNECTION_UNBOUND = 102;
45
43
  public final int EM_CONNECTION_ACCEPTED = 103;
46
44
  public final int EM_CONNECTION_COMPLETED = 104;
47
45
  public final int EM_LOOPBREAK_SIGNAL = 105;
48
-
46
+ public final int EM_CONNECTION_NOTIFY_READABLE = 106;
47
+ public final int EM_CONNECTION_NOTIFY_WRITABLE = 107;
48
+ public final int EM_SSL_HANDSHAKE_COMPLETED = 108;
49
+ public final int EM_SSL_VERIFY = 109;
50
+ public final int EM_PROXY_TARGET_UNBOUND = 110;
51
+
49
52
  private Selector mySelector;
50
- private TreeMap<Long, String> Timers;
51
- private TreeMap<String, EventableChannel> Connections;
52
- private TreeMap<String, ServerSocketChannel> Acceptors;
53
+ private TreeMap<Long, ArrayList<Long>> Timers;
54
+ private HashMap<Long, EventableChannel> Connections;
55
+ private HashMap<Long, ServerSocketChannel> Acceptors;
56
+ private ArrayList<Long> NewConnections;
57
+ private ArrayList<Long> UnboundConnections;
58
+ private ArrayList<EventableSocketChannel> DetachedConnections;
53
59
 
54
60
  private boolean bRunReactor;
55
61
  private long BindingIndex;
56
- private ByteBuffer EmptyByteBuffer;
57
62
  private AtomicBoolean loopBreaker;
58
63
  private ByteBuffer myReadBuffer;
59
64
  private int timerQuantum;
60
-
65
+
61
66
  public EmReactor() {
62
- Timers = new TreeMap<Long, String>();
63
- Connections = new TreeMap<String, EventableChannel>();
64
- Acceptors = new TreeMap<String, ServerSocketChannel>();
65
-
66
- BindingIndex = 100000;
67
- EmptyByteBuffer = ByteBuffer.allocate(0);
67
+ Timers = new TreeMap<Long, ArrayList<Long>>();
68
+ Connections = new HashMap<Long, EventableChannel>();
69
+ Acceptors = new HashMap<Long, ServerSocketChannel>();
70
+ NewConnections = new ArrayList<Long>();
71
+ UnboundConnections = new ArrayList<Long>();
72
+ DetachedConnections = new ArrayList<EventableSocketChannel>();
73
+
74
+ BindingIndex = 0;
68
75
  loopBreaker = new AtomicBoolean();
69
76
  loopBreaker.set(false);
70
77
  myReadBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
71
78
  timerQuantum = 98;
72
79
  }
73
-
80
+
74
81
  /**
75
- * Intended to be overridden in languages (like Ruby) that can't handle ByteBuffer. This is a stub.
76
- * Obsolete now that I figured out how to make Ruby deal with ByteBuffers.
77
- * @param sig
78
- * @param eventType
79
- * @param data
82
+ * This is a no-op stub, intended to be overridden in user code.
80
83
  */
81
- /*
82
- public void stringEventCallback (String sig, int eventType, String data) {
83
- System.out.println ("Default event callback: " + sig + " " + eventType + " " + data);
84
- }
85
- */
86
-
87
- /**
88
- * This is a no-op stub, intended to be overridden in user code.
89
- * @param sig
90
- * @param eventType
91
- * @param data
92
- */
93
- public void eventCallback (String sig, int eventType, ByteBuffer data) {
94
- System.out.println ("Default callback: "+sig+" "+eventType+" "+data);
95
- //stringEventCallback (sig, eventType, new String (data.array(), data.position(), data.limit()));
96
-
84
+ public void eventCallback (long sig, int eventType, ByteBuffer data, long data2) {
85
+ System.out.println ("Default callback: "+sig+" "+eventType+" "+data+" "+data2);
97
86
  }
98
-
99
- public void run() throws IOException {
100
- mySelector = Selector.open();
101
- bRunReactor = true;
102
-
103
- //int n = 0;
104
- for (;;) {
105
- //System.out.println ("loop "+ (n++));
106
- if (!bRunReactor) break;
107
- runLoopbreaks();
108
- if (!bRunReactor) break;
109
- runTimers();
110
- if (!bRunReactor) break;
111
- mySelector.select(timerQuantum);
112
-
113
- Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
114
- while (it.hasNext()) {
115
- SelectionKey k = it.next();
116
- it.remove();
117
-
118
- try {
119
- if (k.isAcceptable()) {
120
- ServerSocketChannel ss = (ServerSocketChannel) k.channel();
121
- SocketChannel sn;
122
- while ((sn = ss.accept()) != null) {
123
- sn.configureBlocking(false);
124
- String b = createBinding();
125
- EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
126
- Connections.put(b, ec);
127
- eventCallback ((String)k.attachment(), EM_CONNECTION_ACCEPTED, ByteBuffer.wrap(b.getBytes()));
128
- }
129
- }
130
-
131
- if (k.isReadable()) {
132
- EventableChannel ec = (EventableChannel)k.attachment();
133
- myReadBuffer.clear();
134
- ec.readInboundData (myReadBuffer);
135
- myReadBuffer.flip();
136
- String b = ec.getBinding();
137
- if (myReadBuffer.limit() > 0) {
138
- eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
139
- }
140
- else {
141
- eventCallback (b, EM_CONNECTION_UNBOUND, EmptyByteBuffer);
142
- Connections.remove(b);
143
- k.channel().close();
144
- }
145
- /*
146
- System.out.println ("READABLE");
147
- SocketChannel sn = (SocketChannel) k.channel();
148
- //ByteBuffer bb = ByteBuffer.allocate(16 * 1024);
149
- // Obviously not thread-safe, since we're using the same buffer for every connection.
150
- // This should minimize the production of garbage, though.
151
- // TODO, we need somehow to make a call to the EventableChannel, so we can pass the
152
- // inbound data through an SSLEngine. Hope that won't break the strategy of using one
153
- // global read-buffer.
154
- myReadBuffer.clear();
155
- int r = sn.read(myReadBuffer);
156
- if (r > 0) {
157
- myReadBuffer.flip();
158
- //bb = ((EventableChannel)k.attachment()).dispatchInboundData (bb);
159
- eventCallback (((EventableChannel)k.attachment()).getBinding(), EM_CONNECTION_READ, myReadBuffer);
160
- }
161
- else {
162
- // TODO. Figure out if a socket that selects readable can ever return 0 bytes
163
- // without it being indicative of an error condition. If Java is like C, the answer is no.
164
- String b = ((EventableChannel)k.attachment()).getBinding();
165
- eventCallback (b, EM_CONNECTION_UNBOUND, EmptyByteBuffer);
166
- Connections.remove(b);
167
- sn.close();
168
- }
169
- */
170
- }
171
-
172
-
173
- if (k.isWritable()) {
174
- EventableChannel ec = (EventableChannel)k.attachment();
175
- if (!ec.writeOutboundData()) {
176
- eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, EmptyByteBuffer);
177
- Connections.remove (ec.getBinding());
178
- k.channel().close();
179
- }
180
- }
181
-
182
- if (k.isConnectable()) {
183
- EventableSocketChannel ec = (EventableSocketChannel)k.attachment();
184
- if (ec.finishConnecting()) {
185
- eventCallback (ec.getBinding(), EM_CONNECTION_COMPLETED, EmptyByteBuffer);
186
- }
187
- else {
188
- Connections.remove (ec.getBinding());
189
- k.channel().close();
190
- eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, EmptyByteBuffer);
191
- }
192
- }
193
- }
194
- catch (CancelledKeyException e) {
195
- // No-op. We can come here if a read-handler closes a socket before we fall through
196
- // to call isWritable.
197
- }
198
-
199
- }
200
- }
201
-
202
- close();
87
+ public void eventCallback (long sig, int eventType, ByteBuffer data) {
88
+ eventCallback (sig, eventType, data, 0);
203
89
  }
204
-
205
- void close() throws IOException {
206
- mySelector.close();
90
+
91
+
92
+ public void run() {
93
+ try {
94
+ mySelector = Selector.open();
95
+ bRunReactor = true;
96
+ } catch (IOException e) {
97
+ throw new RuntimeException ("Could not open selector", e);
98
+ }
99
+
100
+ while (bRunReactor) {
101
+ runLoopbreaks();
102
+ if (!bRunReactor) break;
103
+
104
+ runTimers();
105
+ if (!bRunReactor) break;
106
+
107
+ removeUnboundConnections();
108
+ checkIO();
109
+ addNewConnections();
110
+ processIO();
111
+ }
112
+
113
+ close();
114
+ }
115
+
116
+ void addNewConnections() {
117
+ ListIterator<EventableSocketChannel> iter = DetachedConnections.listIterator(0);
118
+ while (iter.hasNext()) {
119
+ EventableSocketChannel ec = iter.next();
120
+ ec.cleanup();
121
+ }
122
+ DetachedConnections.clear();
123
+
124
+ ListIterator<Long> iter2 = NewConnections.listIterator(0);
125
+ while (iter2.hasNext()) {
126
+ long b = iter2.next();
127
+
128
+ EventableChannel ec = Connections.get(b);
129
+ if (ec != null) {
130
+ try {
131
+ ec.register();
132
+ } catch (ClosedChannelException e) {
133
+ UnboundConnections.add (ec.getBinding());
134
+ }
135
+ }
136
+ }
137
+ NewConnections.clear();
138
+ }
139
+
140
+ void removeUnboundConnections() {
141
+ ListIterator<Long> iter = UnboundConnections.listIterator(0);
142
+ while (iter.hasNext()) {
143
+ long b = iter.next();
144
+
145
+ EventableChannel ec = Connections.remove(b);
146
+ if (ec != null) {
147
+ eventCallback (b, EM_CONNECTION_UNBOUND, null);
148
+ ec.close();
149
+
150
+ EventableSocketChannel sc = (EventableSocketChannel) ec;
151
+ if (sc != null && sc.isAttached())
152
+ DetachedConnections.add (sc);
153
+ }
154
+ }
155
+ UnboundConnections.clear();
156
+ }
157
+
158
+ void checkIO() {
159
+ long timeout;
160
+
161
+ if (NewConnections.size() > 0) {
162
+ timeout = -1;
163
+ } else if (!Timers.isEmpty()) {
164
+ long now = new Date().getTime();
165
+ long k = Timers.firstKey();
166
+ long diff = k-now;
167
+
168
+ if (diff <= 0)
169
+ timeout = -1; // don't wait, just poll once
170
+ else
171
+ timeout = diff;
172
+ } else {
173
+ timeout = 0; // wait indefinitely
174
+ }
175
+
176
+ try {
177
+ if (timeout == -1)
178
+ mySelector.selectNow();
179
+ else
180
+ mySelector.select(timeout);
181
+ } catch (IOException e) {
182
+ e.printStackTrace();
183
+ }
184
+ }
185
+
186
+ void processIO() {
187
+ Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
188
+ while (it.hasNext()) {
189
+ SelectionKey k = it.next();
190
+ it.remove();
191
+
192
+ if (k.isConnectable())
193
+ isConnectable(k);
194
+
195
+ else if (k.isAcceptable())
196
+ isAcceptable(k);
197
+
198
+ else {
199
+ if (k.isWritable())
200
+ isWritable(k);
201
+
202
+ if (k.isReadable())
203
+ isReadable(k);
204
+ }
205
+ }
206
+ }
207
+
208
+ void isAcceptable (SelectionKey k) {
209
+ ServerSocketChannel ss = (ServerSocketChannel) k.channel();
210
+ SocketChannel sn;
211
+ long b;
212
+
213
+ for (int n = 0; n < 10; n++) {
214
+ try {
215
+ sn = ss.accept();
216
+ if (sn == null)
217
+ break;
218
+ } catch (IOException e) {
219
+ e.printStackTrace();
220
+ k.cancel();
221
+
222
+ ServerSocketChannel server = Acceptors.remove(k.attachment());
223
+ if (server != null)
224
+ try{ server.close(); } catch (IOException ex) {};
225
+ break;
226
+ }
227
+
228
+ try {
229
+ sn.configureBlocking(false);
230
+ } catch (IOException e) {
231
+ e.printStackTrace();
232
+ continue;
233
+ }
234
+
235
+ b = createBinding();
236
+ EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
237
+ Connections.put (b, ec);
238
+ NewConnections.add (b);
239
+
240
+ eventCallback (((Long)k.attachment()).longValue(), EM_CONNECTION_ACCEPTED, null, b);
241
+ }
242
+ }
243
+
244
+ void isReadable (SelectionKey k) {
245
+ EventableChannel ec = (EventableChannel) k.attachment();
246
+ long b = ec.getBinding();
247
+
248
+ if (ec.isWatchOnly()) {
249
+ if (ec.isNotifyReadable())
250
+ eventCallback (b, EM_CONNECTION_NOTIFY_READABLE, null);
251
+ } else {
252
+ myReadBuffer.clear();
253
+
254
+ try {
255
+ ec.readInboundData (myReadBuffer);
256
+ myReadBuffer.flip();
257
+ if (myReadBuffer.limit() > 0)
258
+ eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
259
+ } catch (IOException e) {
260
+ UnboundConnections.add (b);
261
+ }
262
+ }
263
+ }
264
+
265
+ void isWritable (SelectionKey k) {
266
+ EventableChannel ec = (EventableChannel) k.attachment();
267
+ long b = ec.getBinding();
268
+
269
+ if (ec.isWatchOnly()) {
270
+ if (ec.isNotifyWritable())
271
+ eventCallback (b, EM_CONNECTION_NOTIFY_WRITABLE, null);
272
+ }
273
+ else {
274
+ try {
275
+ if (!ec.writeOutboundData())
276
+ UnboundConnections.add (b);
277
+ } catch (IOException e) {
278
+ UnboundConnections.add (b);
279
+ }
280
+ }
281
+ }
282
+
283
+ void isConnectable (SelectionKey k) {
284
+ EventableSocketChannel ec = (EventableSocketChannel) k.attachment();
285
+ long b = ec.getBinding();
286
+
287
+ try {
288
+ if (ec.finishConnecting())
289
+ eventCallback (b, EM_CONNECTION_COMPLETED, null);
290
+ else
291
+ UnboundConnections.add (b);
292
+ } catch (IOException e) {
293
+ UnboundConnections.add (b);
294
+ }
295
+ }
296
+
297
+ void close() {
298
+ try {
299
+ if (mySelector != null)
300
+ mySelector.close();
301
+ } catch (IOException e) {}
207
302
  mySelector = null;
208
303
 
209
304
  // run down open connections and sockets.
210
305
  Iterator<ServerSocketChannel> i = Acceptors.values().iterator();
211
306
  while (i.hasNext()) {
212
- i.next().close();
307
+ try {
308
+ i.next().close();
309
+ } catch (IOException e) {}
213
310
  }
214
-
311
+
312
+ // 29Sep09: We create an ArrayList of the existing connections, then iterate over
313
+ // that to call unbind on them. This is because an unbind can trigger a reconnect,
314
+ // which will add to the Connections HashMap, causing a ConcurrentModificationException.
315
+ // XXX: The correct behavior here would be to latch the various reactor methods to return
316
+ // immediately if the reactor is shutting down.
317
+ ArrayList<EventableChannel> conns = new ArrayList<EventableChannel>();
215
318
  Iterator<EventableChannel> i2 = Connections.values().iterator();
216
- while (i2.hasNext())
217
- i2.next().close();
319
+ while (i2.hasNext()) {
320
+ EventableChannel ec = i2.next();
321
+ if (ec != null) {
322
+ conns.add (ec);
323
+ }
324
+ }
325
+ Connections.clear();
326
+
327
+ ListIterator<EventableChannel> i3 = conns.listIterator(0);
328
+ while (i3.hasNext()) {
329
+ EventableChannel ec = i3.next();
330
+ eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, null);
331
+ ec.close();
332
+
333
+ EventableSocketChannel sc = (EventableSocketChannel) ec;
334
+ if (sc != null && sc.isAttached())
335
+ DetachedConnections.add (sc);
336
+ }
337
+
338
+ ListIterator<EventableSocketChannel> i4 = DetachedConnections.listIterator(0);
339
+ while (i4.hasNext()) {
340
+ EventableSocketChannel ec = i4.next();
341
+ ec.cleanup();
342
+ }
343
+ DetachedConnections.clear();
218
344
  }
219
-
345
+
220
346
  void runLoopbreaks() {
221
347
  if (loopBreaker.getAndSet(false)) {
222
- eventCallback ("", EM_LOOPBREAK_SIGNAL, EmptyByteBuffer);
348
+ eventCallback (0, EM_LOOPBREAK_SIGNAL, null);
223
349
  }
224
350
  }
225
-
351
+
226
352
  public void stop() {
227
353
  bRunReactor = false;
228
354
  signalLoopbreak();
229
355
  }
230
-
356
+
231
357
  void runTimers() {
232
358
  long now = new Date().getTime();
233
359
  while (!Timers.isEmpty()) {
234
360
  long k = Timers.firstKey();
235
- //System.out.println (k - now);
236
361
  if (k > now)
237
362
  break;
238
- String s = Timers.remove(k);
239
- eventCallback ("", EM_TIMER_FIRED, ByteBuffer.wrap(s.getBytes()));
363
+
364
+ ArrayList<Long> callbacks = Timers.get(k);
365
+ Timers.remove(k);
366
+
367
+ // Fire all timers at this timestamp
368
+ ListIterator<Long> iter = callbacks.listIterator(0);
369
+ while (iter.hasNext()) {
370
+ eventCallback (0, EM_TIMER_FIRED, null, iter.next().longValue());
371
+ }
240
372
  }
241
373
  }
242
-
243
- public String installOneshotTimer (int milliseconds) {
244
- BindingIndex++;
245
- String s = createBinding();
246
- Timers.put(new Date().getTime() + milliseconds, s);
374
+
375
+ public long installOneshotTimer (int milliseconds) {
376
+ long s = createBinding();
377
+ long deadline = new Date().getTime() + milliseconds;
378
+
379
+ if (Timers.containsKey(deadline)) {
380
+ Timers.get(deadline).add(s);
381
+ } else {
382
+ ArrayList<Long> callbacks = new ArrayList<Long>();
383
+ callbacks.add(s);
384
+ Timers.put(deadline, callbacks);
385
+ }
386
+
247
387
  return s;
248
388
  }
249
-
250
- public String startTcpServer (SocketAddress sa) throws EmReactorException {
389
+
390
+ public long startTcpServer (SocketAddress sa) throws EmReactorException {
251
391
  try {
252
392
  ServerSocketChannel server = ServerSocketChannel.open();
253
393
  server.configureBlocking(false);
254
394
  server.socket().bind (sa);
255
- String s = createBinding();
395
+ long s = createBinding();
256
396
  Acceptors.put(s, server);
257
397
  server.register(mySelector, SelectionKey.OP_ACCEPT, s);
258
398
  return s;
259
399
  } catch (IOException e) {
260
- // TODO, should parameterize this exception better.
261
- throw new EmReactorException ("unable to open socket acceptor");
400
+ throw new EmReactorException ("unable to open socket acceptor: " + e.toString());
262
401
  }
263
402
  }
264
-
265
- public String startTcpServer (String address, int port) throws EmReactorException {
403
+
404
+ public long startTcpServer (String address, int port) throws EmReactorException {
266
405
  return startTcpServer (new InetSocketAddress (address, port));
267
- /*
268
- ServerSocketChannel server = ServerSocketChannel.open();
269
- server.configureBlocking(false);
270
- server.socket().bind(new java.net.InetSocketAddress(address, port));
271
- String s = createBinding();
272
- Acceptors.put(s, server);
273
- server.register(mySelector, SelectionKey.OP_ACCEPT, s);
274
- return s;
275
- */
276
406
  }
277
407
 
278
- public void stopTcpServer (String signature) throws IOException {
408
+ public void stopTcpServer (long signature) throws IOException {
279
409
  ServerSocketChannel server = Acceptors.remove(signature);
280
410
  if (server != null)
281
411
  server.close();
282
412
  else
283
413
  throw new RuntimeException ("failed to close unknown acceptor");
284
414
  }
285
-
286
415
 
287
- public String openUdpSocket (String address, int port) throws IOException {
288
- return openUdpSocket (new InetSocketAddress (address, port));
289
- }
290
- /**
291
- *
292
- * @param address
293
- * @param port
294
- * @return
295
- * @throws IOException
296
- */
297
- public String openUdpSocket (InetSocketAddress address) throws IOException {
416
+ public long openUdpSocket (InetSocketAddress address) throws IOException {
298
417
  // TODO, don't throw an exception out of here.
299
418
  DatagramChannel dg = DatagramChannel.open();
300
419
  dg.configureBlocking(false);
301
420
  dg.socket().bind(address);
302
- String b = createBinding();
421
+ long b = createBinding();
303
422
  EventableChannel ec = new EventableDatagramChannel (dg, b, mySelector);
304
423
  dg.register(mySelector, SelectionKey.OP_READ, ec);
305
424
  Connections.put(b, ec);
306
425
  return b;
307
426
  }
308
-
309
- public void sendData (String sig, ByteBuffer bb) throws IOException {
310
- (Connections.get(sig)).scheduleOutboundData( bb );
427
+
428
+ public long openUdpSocket (String address, int port) throws IOException {
429
+ return openUdpSocket (new InetSocketAddress (address, port));
430
+ }
431
+
432
+ public void sendData (long sig, ByteBuffer bb) throws IOException {
433
+ Connections.get(sig).scheduleOutboundData( bb );
311
434
  }
312
- public void sendData (String sig, byte[] data) throws IOException {
435
+
436
+ public void sendData (long sig, byte[] data) throws IOException {
313
437
  sendData (sig, ByteBuffer.wrap(data));
314
- //(Connections.get(sig)).scheduleOutboundData( ByteBuffer.wrap(data.getBytes()));
315
438
  }
316
- public void setCommInactivityTimeout (String sig, long mills) {
317
- (Connections.get(sig)).setCommInactivityTimeout (mills);
439
+
440
+ public void setCommInactivityTimeout (long sig, long mills) {
441
+ Connections.get(sig).setCommInactivityTimeout (mills);
318
442
  }
319
-
320
- /**
321
- *
322
- * @param sig
323
- * @param data
324
- * @param length
325
- * @param recipAddress
326
- * @param recipPort
327
- */
328
- public void sendDatagram (String sig, String data, int length, String recipAddress, int recipPort) {
443
+
444
+ public void sendDatagram (long sig, String data, int length, String recipAddress, int recipPort) {
329
445
  sendDatagram (sig, ByteBuffer.wrap(data.getBytes()), recipAddress, recipPort);
330
446
  }
331
-
332
- /**
333
- *
334
- * @param sig
335
- * @param bb
336
- * @param recipAddress
337
- * @param recipPort
338
- */
339
- public void sendDatagram (String sig, ByteBuffer bb, String recipAddress, int recipPort) {
447
+
448
+ public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) {
340
449
  (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
341
450
  }
342
451
 
343
- /**
344
- *
345
- * @param address
346
- * @param port
347
- * @return
348
- * @throws ClosedChannelException
349
- */
350
- public String connectTcpServer (String address, int port) throws ClosedChannelException {
452
+ public long connectTcpServer (String address, int port) {
351
453
  return connectTcpServer(null, 0, address, port);
352
454
  }
353
455
 
354
- /**
355
- *
356
- * @param bindAddr
357
- * @param bindPort
358
- * @param address
359
- * @param port
360
- * @return
361
- * @throws ClosedChannelException
362
- */
363
- public String connectTcpServer (String bindAddr, int bindPort, String address, int port) throws ClosedChannelException {
364
- String b = createBinding();
365
-
456
+ public long connectTcpServer (String bindAddr, int bindPort, String address, int port) {
457
+ long b = createBinding();
458
+
366
459
  try {
367
460
  SocketChannel sc = SocketChannel.open();
368
461
  sc.configureBlocking(false);
@@ -385,39 +478,93 @@ public class EmReactor {
385
478
  // CONNECTION_COMPLETED on the next pass through the loop, because writable will
386
479
  // fire.
387
480
  throw new RuntimeException ("immediate-connect unimplemented");
388
- }
481
+ }
389
482
  else {
390
- Connections.put (b, ec);
391
483
  ec.setConnectPending();
484
+ Connections.put (b, ec);
485
+ NewConnections.add (b);
392
486
  }
393
487
  } catch (IOException e) {
394
488
  // Can theoretically come here if a connect failure can be determined immediately.
395
489
  // I don't know how to make that happen for testing purposes.
396
- throw new RuntimeException ("immediate-connect unimplemented");
490
+ throw new RuntimeException ("immediate-connect unimplemented: " + e.toString());
397
491
  }
398
492
  return b;
399
493
  }
400
494
 
401
- public void closeConnection (String sig, boolean afterWriting) throws ClosedChannelException {
402
- Connections.get(sig).scheduleClose (afterWriting);
495
+ public void closeConnection (long sig, boolean afterWriting) {
496
+ EventableChannel ec = Connections.get(sig);
497
+ if (ec != null)
498
+ if (ec.scheduleClose (afterWriting))
499
+ UnboundConnections.add (sig);
403
500
  }
404
501
 
405
- String createBinding() {
406
- return new String ("BND_" + (++BindingIndex));
502
+ long createBinding() {
503
+ return ++BindingIndex;
407
504
  }
408
-
505
+
409
506
  public void signalLoopbreak() {
410
507
  loopBreaker.set(true);
411
- mySelector.wakeup();
508
+ if (mySelector != null)
509
+ mySelector.wakeup();
412
510
  }
413
-
414
- public void startTls (String sig) throws NoSuchAlgorithmException, KeyManagementException {
511
+
512
+ public void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException {
415
513
  Connections.get(sig).startTls();
416
514
  }
417
-
515
+
418
516
  public void setTimerQuantum (int mills) {
419
517
  if (mills < 5 || mills > 2500)
420
518
  throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
421
519
  timerQuantum = mills;
422
520
  }
521
+
522
+ public Object[] getPeerName (long sig) {
523
+ return Connections.get(sig).getPeerName();
524
+ }
525
+
526
+ public long attachChannel (SocketChannel sc, boolean watch_mode) {
527
+ long b = createBinding();
528
+
529
+ EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
530
+
531
+ ec.setAttached();
532
+ if (watch_mode)
533
+ ec.setWatchOnly();
534
+
535
+ Connections.put (b, ec);
536
+ NewConnections.add (b);
537
+
538
+ return b;
539
+ }
540
+
541
+ public SocketChannel detachChannel (long sig) {
542
+ EventableSocketChannel ec = (EventableSocketChannel) Connections.get (sig);
543
+ if (ec != null) {
544
+ UnboundConnections.add (sig);
545
+ return ec.getChannel();
546
+ } else {
547
+ return null;
548
+ }
549
+ }
550
+
551
+ public void setNotifyReadable (long sig, boolean mode) {
552
+ ((EventableSocketChannel) Connections.get(sig)).setNotifyReadable(mode);
553
+ }
554
+
555
+ public void setNotifyWritable (long sig, boolean mode) {
556
+ ((EventableSocketChannel) Connections.get(sig)).setNotifyWritable(mode);
557
+ }
558
+
559
+ public boolean isNotifyReadable (long sig) {
560
+ return Connections.get(sig).isNotifyReadable();
561
+ }
562
+
563
+ public boolean isNotifyWritable (long sig) {
564
+ return Connections.get(sig).isNotifyWritable();
565
+ }
566
+
567
+ public int getConnectionCount() {
568
+ return Connections.size() + Acceptors.size();
569
+ }
423
570
  }