eventmachine 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. data/DEFERRABLES +1 -1
  2. data/LIGHTWEIGHT_CONCURRENCY +1 -1
  3. data/PURE_RUBY +1 -1
  4. data/README +1 -1
  5. data/RELEASE_NOTES +1 -1
  6. data/SMTP +1 -1
  7. data/SPAWNED_PROCESSES +1 -1
  8. data/TODO +1 -1
  9. data/ext/binder.cpp +1 -1
  10. data/ext/binder.h +1 -1
  11. data/ext/cmain.cpp +29 -1
  12. data/ext/cplusplus.cpp +1 -1
  13. data/ext/ed.cpp +79 -1
  14. data/ext/ed.h +6 -1
  15. data/ext/em.cpp +343 -27
  16. data/ext/em.h +25 -1
  17. data/ext/emwin.cpp +1 -1
  18. data/ext/emwin.h +1 -1
  19. data/ext/epoll.cpp +1 -1
  20. data/ext/epoll.h +1 -1
  21. data/ext/eventmachine.h +3 -1
  22. data/ext/eventmachine_cpp.h +1 -1
  23. data/ext/extconf.rb +31 -1
  24. data/ext/files.cpp +1 -1
  25. data/ext/files.h +1 -1
  26. data/ext/kb.cpp +4 -1
  27. data/ext/page.cpp +1 -1
  28. data/ext/page.h +1 -1
  29. data/ext/pipe.cpp +4 -1
  30. data/ext/project.h +8 -2
  31. data/ext/rubymain.cpp +73 -2
  32. data/ext/sigs.cpp +1 -1
  33. data/ext/sigs.h +1 -1
  34. data/ext/ssl.cpp +4 -1
  35. data/ext/ssl.h +1 -1
  36. data/lib/em/deferrable.rb +1 -1
  37. data/lib/em/eventable.rb +1 -1
  38. data/lib/em/future.rb +1 -1
  39. data/lib/em/messages.rb +1 -1
  40. data/lib/em/processes.rb +68 -0
  41. data/lib/em/spawnable.rb +1 -1
  42. data/lib/em/streamer.rb +1 -1
  43. data/lib/eventmachine.rb +113 -66
  44. data/lib/eventmachine_version.rb +2 -2
  45. data/lib/evma.rb +1 -1
  46. data/lib/evma/callback.rb +1 -1
  47. data/lib/evma/container.rb +1 -1
  48. data/lib/evma/factory.rb +1 -1
  49. data/lib/evma/protocol.rb +1 -1
  50. data/lib/evma/reactor.rb +1 -1
  51. data/lib/jeventmachine.rb +1 -1
  52. data/lib/pr_eventmachine.rb +35 -8
  53. data/lib/protocols/header_and_content.rb +1 -1
  54. data/lib/protocols/httpcli2.rb +1 -1
  55. data/lib/protocols/httpclient.rb +1 -1
  56. data/lib/protocols/line_and_text.rb +1 -1
  57. data/lib/protocols/linetext2.rb +1 -1
  58. data/lib/protocols/saslauth.rb +59 -1
  59. data/lib/protocols/smtpclient.rb +4 -3
  60. data/lib/protocols/smtpserver.rb +3 -3
  61. data/lib/protocols/stomp.rb +1 -1
  62. data/lib/protocols/tcptest.rb +1 -1
  63. data/tests/test_basic.rb +2 -1
  64. data/tests/test_defer.rb +63 -0
  65. data/tests/test_epoll.rb +10 -5
  66. data/tests/test_errors.rb +13 -3
  67. data/tests/test_eventables.rb +1 -1
  68. data/tests/test_exc.rb +2 -1
  69. data/tests/test_futures.rb +2 -1
  70. data/tests/test_hc.rb +26 -2
  71. data/tests/test_httpclient.rb +2 -1
  72. data/tests/test_httpclient2.rb +2 -1
  73. data/tests/test_kb.rb +2 -1
  74. data/tests/test_ltp.rb +3 -1
  75. data/tests/test_ltp2.rb +2 -1
  76. data/tests/test_next_tick.rb +2 -1
  77. data/tests/test_processes.rb +56 -0
  78. data/tests/test_pure.rb +2 -1
  79. data/tests/test_running.rb +2 -1
  80. data/tests/test_sasl.rb +73 -0
  81. data/tests/test_send_file.rb +3 -1
  82. data/tests/test_servers.rb +4 -1
  83. data/tests/test_smtpclient.rb +2 -1
  84. data/tests/test_smtpserver.rb +3 -2
  85. data/tests/test_spawn.rb +2 -1
  86. data/tests/test_timers.rb +2 -2
  87. data/tests/test_ud.rb +2 -1
  88. data/tests/testem.rb +1 -1
  89. metadata +115 -104
data/DEFERRABLES CHANGED
@@ -1,4 +1,4 @@
1
- $Id: DEFERRABLES 551 2007-09-22 12:57:36Z blackhedd $
1
+ $Id: DEFERRABLES 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  [DOCUMENT UNDER CONSTRUCTION]
4
4
 
@@ -1,4 +1,4 @@
1
- $Id: LIGHTWEIGHT_CONCURRENCY 541 2007-09-17 15:08:36Z blackhedd $
1
+ $Id: LIGHTWEIGHT_CONCURRENCY 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use them.
4
4
 
data/PURE_RUBY CHANGED
@@ -1,4 +1,4 @@
1
- $Id: PURE_RUBY 602 2007-12-05 18:45:29Z blackhedd $
1
+ $Id: PURE_RUBY 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  EventMachine is supplied in three alternative versions.
4
4
 
data/README CHANGED
@@ -1,4 +1,4 @@
1
- $Id: README 479 2007-07-27 08:51:26Z blackhedd $
1
+ $Id: README 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  = RUBY/EventMachine
4
4
 
data/RELEASE_NOTES CHANGED
@@ -1,4 +1,4 @@
1
- $Id: RELEASE_NOTES 388 2007-06-25 02:06:05Z blackhedd $
1
+ $Id: RELEASE_NOTES 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  RUBY/EventMachine RELEASE NOTES
4
4
 
data/SMTP CHANGED
@@ -1,4 +1,4 @@
1
- $Id: SMTP 539 2007-09-17 14:42:52Z blackhedd $
1
+ $Id: SMTP 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
 
4
4
  [DOCUMENT UNDER CONSTRUCTION]
data/SPAWNED_PROCESSES CHANGED
@@ -1,4 +1,4 @@
1
- $Id: SPAWNED_PROCESSES 538 2007-09-17 14:38:56Z blackhedd $
1
+ $Id: SPAWNED_PROCESSES 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  [DOCUMENT UNDER CONSTRUCTION]
4
4
 
data/TODO CHANGED
@@ -1,4 +1,4 @@
1
- $Id: TODO 231 2006-08-13 17:15:53Z blackhedd $
1
+ $Id: TODO 668 2008-01-04 23:00:34Z blackhedd $
2
2
 
3
3
  TODO List:
4
4
 
data/ext/binder.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: binder.cpp 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: binder.cpp 668 2008-01-04 23:00:34Z blackhedd $
4
4
 
5
5
  File: binder.cpp
6
6
  Date: 07Apr06
data/ext/binder.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: binder.h 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: binder.h 668 2008-01-04 23:00:34Z blackhedd $
4
4
 
5
5
  File: binder.h
6
6
  Date: 07Apr06
data/ext/cmain.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: cmain.cpp 585 2007-11-27 14:29:34Z blackhedd $
3
+ $Id: cmain.cpp 679 2008-01-19 01:40:06Z blackhedd $
4
4
 
5
5
  File: cmain.cpp
6
6
  Date: 06Apr06
@@ -22,6 +22,7 @@ See the file COPYING for complete licensing information.
22
22
 
23
23
  static EventMachine_t *EventMachine;
24
24
  static int bUseEpoll = 0;
25
+ static int bUseKqueue = 0;
25
26
 
26
27
 
27
28
  /***********************
@@ -38,6 +39,8 @@ extern "C" void evma_initialize_library (void(*cb)(const char*, int, const char*
38
39
  EventMachine = new EventMachine_t (cb);
39
40
  if (bUseEpoll)
40
41
  EventMachine->_UseEpoll();
42
+ if (bUseKqueue)
43
+ EventMachine->_UseKqueue();
41
44
  }
42
45
 
43
46
 
@@ -259,6 +262,22 @@ extern "C" int evma_get_peername (const char *binding, struct sockaddr *sa)
259
262
  return 0;
260
263
  }
261
264
 
265
+ /*****************
266
+ evma_get_sockname
267
+ *****************/
268
+
269
+ extern "C" int evma_get_sockname (const char *binding, struct sockaddr *sa)
270
+ {
271
+ if (!EventMachine)
272
+ throw std::runtime_error ("not initialized");
273
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
274
+ if (ed) {
275
+ return ed->GetSockname (sa) ? 1 : 0;
276
+ }
277
+ else
278
+ return 0;
279
+ }
280
+
262
281
  /***********************
263
282
  evma_get_subprocess_pid
264
283
  ***********************/
@@ -418,6 +437,15 @@ extern "C" void evma__epoll()
418
437
  bUseEpoll = 1;
419
438
  }
420
439
 
440
+ /************
441
+ evma__kqueue
442
+ ************/
443
+
444
+ extern "C" void evma__kqueue()
445
+ {
446
+ bUseKqueue = 1;
447
+ }
448
+
421
449
 
422
450
  /**********************
423
451
  evma_set_rlimit_nofile
data/ext/cplusplus.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: cplusplus.cpp 505 2007-08-26 20:44:57Z blackhedd $
3
+ $Id: cplusplus.cpp 668 2008-01-04 23:00:34Z blackhedd $
4
4
 
5
5
  File: cplusplus.cpp
6
6
  Date: 27Jul07
data/ext/ed.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.cpp 572 2007-11-15 23:13:45Z blackhedd $
3
+ $Id: ed.cpp 687 2008-05-15 14:28:32Z francis $
4
4
 
5
5
  File: ed.cpp
6
6
  Date: 06Apr06
@@ -187,6 +187,9 @@ ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
187
187
  #ifdef HAVE_EPOLL
188
188
  EpollEvent.events = EPOLLOUT;
189
189
  #endif
190
+ #ifdef HAVE_KQUEUE
191
+ MyEventMachine->ArmKqueueWriter (this);
192
+ #endif
190
193
  }
191
194
 
192
195
 
@@ -321,6 +324,9 @@ int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
321
324
  assert (MyEventMachine);
322
325
  MyEventMachine->Modify (this);
323
326
  #endif
327
+ #ifdef HAVE_KQUEUE
328
+ MyEventMachine->ArmKqueueWriter (this);
329
+ #endif
324
330
  return length;
325
331
  }
326
332
 
@@ -531,6 +537,12 @@ void ConnectionDescriptor::Write()
531
537
  // The callback may have scheduled outbound data.
532
538
  EpollEvent.events = EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0);
533
539
  #endif
540
+ #ifdef HAVE_KQUEUE
541
+ MyEventMachine->ArmKqueueReader (this);
542
+ // The callback may have scheduled outbound data.
543
+ if (SelectForWrite())
544
+ MyEventMachine->ArmKqueueWriter (this);
545
+ #endif
534
546
  }
535
547
  else
536
548
  ScheduleClose (false);
@@ -614,6 +626,10 @@ void ConnectionDescriptor::_WriteOutboundData()
614
626
  assert (MyEventMachine);
615
627
  MyEventMachine->Modify (this);
616
628
  #endif
629
+ #ifdef HAVE_KQUEUE
630
+ if (SelectForWrite())
631
+ MyEventMachine->ArmKqueueWriter (this);
632
+ #endif
617
633
  }
618
634
  else {
619
635
  #ifdef OS_UNIX
@@ -797,6 +813,9 @@ LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em):
797
813
  #ifdef HAVE_EPOLL
798
814
  EpollEvent.events = EPOLLIN;
799
815
  #endif
816
+ #ifdef HAVE_KQUEUE
817
+ MyEventMachine->ArmKqueueReader (this);
818
+ #endif
800
819
  }
801
820
 
802
821
 
@@ -834,6 +853,9 @@ AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
834
853
  #ifdef HAVE_EPOLL
835
854
  EpollEvent.events = EPOLLIN;
836
855
  #endif
856
+ #ifdef HAVE_KQUEUE
857
+ MyEventMachine->ArmKqueueReader (this);
858
+ #endif
837
859
  }
838
860
 
839
861
 
@@ -922,6 +944,11 @@ void AcceptorDescriptor::Read()
922
944
  #endif
923
945
  assert (MyEventMachine);
924
946
  MyEventMachine->Add (cd);
947
+ #ifdef HAVE_KQUEUE
948
+ if (cd->SelectForWrite())
949
+ MyEventMachine->ArmKqueueWriter (cd);
950
+ MyEventMachine->ArmKqueueReader (cd);
951
+ #endif
925
952
  }
926
953
 
927
954
  }
@@ -948,6 +975,22 @@ void AcceptorDescriptor::Heartbeat()
948
975
  }
949
976
 
950
977
 
978
+ /*******************************
979
+ AcceptorDescriptor::GetSockname
980
+ *******************************/
981
+
982
+ bool AcceptorDescriptor::GetSockname (struct sockaddr *s)
983
+ {
984
+ bool ok = false;
985
+ if (s) {
986
+ socklen_t len = sizeof(*s);
987
+ int gp = getsockname (GetSocket(), s, &len);
988
+ if (gp == 0)
989
+ ok = true;
990
+ }
991
+ return ok;
992
+ }
993
+
951
994
 
952
995
 
953
996
  /**************************************
@@ -986,6 +1029,9 @@ DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em):
986
1029
  #ifdef HAVE_EPOLL
987
1030
  EpollEvent.events = EPOLLIN;
988
1031
  #endif
1032
+ #ifdef HAVE_KQUEUE
1033
+ MyEventMachine->ArmKqueueReader (this);
1034
+ #endif
989
1035
  }
990
1036
 
991
1037
 
@@ -1273,6 +1319,22 @@ bool ConnectionDescriptor::GetPeername (struct sockaddr *s)
1273
1319
  return ok;
1274
1320
  }
1275
1321
 
1322
+ /*********************************
1323
+ ConnectionDescriptor::GetSockname
1324
+ *********************************/
1325
+
1326
+ bool ConnectionDescriptor::GetSockname (struct sockaddr *s)
1327
+ {
1328
+ bool ok = false;
1329
+ if (s) {
1330
+ socklen_t len = sizeof(*s);
1331
+ int gp = getsockname (GetSocket(), s, &len);
1332
+ if (gp == 0)
1333
+ ok = true;
1334
+ }
1335
+ return ok;
1336
+ }
1337
+
1276
1338
 
1277
1339
  /**********************************************
1278
1340
  ConnectionDescriptor::GetCommInactivityTimeout
@@ -1333,6 +1395,22 @@ bool DatagramDescriptor::GetPeername (struct sockaddr *s)
1333
1395
  return ok;
1334
1396
  }
1335
1397
 
1398
+ /*******************************
1399
+ DatagramDescriptor::GetSockname
1400
+ *******************************/
1401
+
1402
+ bool DatagramDescriptor::GetSockname (struct sockaddr *s)
1403
+ {
1404
+ bool ok = false;
1405
+ if (s) {
1406
+ socklen_t len = sizeof(*s);
1407
+ int gp = getsockname (GetSocket(), s, &len);
1408
+ if (gp == 0)
1409
+ ok = true;
1410
+ }
1411
+ return ok;
1412
+ }
1413
+
1336
1414
 
1337
1415
 
1338
1416
  /********************************************
data/ext/ed.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.h 585 2007-11-27 14:29:34Z blackhedd $
3
+ $Id: ed.h 687 2008-05-15 14:28:32Z francis $
4
4
 
5
5
  File: ed.h
6
6
  Date: 06Apr06
@@ -62,6 +62,7 @@ class EventableDescriptor: public Bindable_t
62
62
  void SetEventCallback (void (*cb)(const char*, int, const char*, int));
63
63
 
64
64
  virtual bool GetPeername (struct sockaddr*) {return false;}
65
+ virtual bool GetSockname (struct sockaddr*) {return false;}
65
66
  virtual bool GetSubprocessPid (pid_t*) {return false;}
66
67
 
67
68
  virtual void StartTls() {}
@@ -154,6 +155,7 @@ class ConnectionDescriptor: public EventableDescriptor
154
155
  void SetServerMode() {bIsServer = true;}
155
156
 
156
157
  virtual bool GetPeername (struct sockaddr*);
158
+ virtual bool GetSockname (struct sockaddr*);
157
159
 
158
160
  virtual int GetCommInactivityTimeout (int *value);
159
161
  virtual int SetCommInactivityTimeout (int *value);
@@ -220,6 +222,7 @@ class DatagramDescriptor: public EventableDescriptor
220
222
  virtual int GetOutboundDataSize() {return OutboundDataSize;}
221
223
 
222
224
  virtual bool GetPeername (struct sockaddr*);
225
+ virtual bool GetSockname (struct sockaddr*);
223
226
 
224
227
  virtual int GetCommInactivityTimeout (int *value);
225
228
  virtual int SetCommInactivityTimeout (int *value);
@@ -264,6 +267,8 @@ class AcceptorDescriptor: public EventableDescriptor
264
267
  virtual bool SelectForRead() {return true;}
265
268
  virtual bool SelectForWrite() {return false;}
266
269
 
270
+ virtual bool GetSockname (struct sockaddr*);
271
+
267
272
  static void StopAcceptor (const char *binding);
268
273
  };
269
274
 
data/ext/em.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: em.cpp 587 2007-12-01 13:50:32Z blackhedd $
3
+ $Id: em.cpp 686 2008-05-14 21:21:10Z francis $
4
4
 
5
5
  File: em.cpp
6
6
  Date: 06Apr06
@@ -40,6 +40,11 @@ unsigned gLastTickCount;
40
40
  static int MaxOutstandingTimers = 1000;
41
41
 
42
42
 
43
+ /* Internal helper to convert strings to internet addresses. IPv6-aware.
44
+ * Not reentrant or threadsafe, optimized for speed.
45
+ */
46
+ static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size);
47
+
43
48
 
44
49
  /***************************************
45
50
  STATIC EventMachine_t::SetMaxTimerCount
@@ -71,6 +76,7 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const c
71
76
  LoopBreakerReader (-1),
72
77
  LoopBreakerWriter (-1),
73
78
  bEpoll (false),
79
+ bKqueue (false),
74
80
  epfd (-1)
75
81
  {
76
82
  // Default time-slice is just smaller than one hundred mills.
@@ -115,6 +121,8 @@ EventMachine_t::~EventMachine_t()
115
121
 
116
122
  if (epfd != -1)
117
123
  close (epfd);
124
+ if (kqfd != -1)
125
+ close (kqfd);
118
126
  }
119
127
 
120
128
 
@@ -136,6 +144,21 @@ void EventMachine_t::_UseEpoll()
136
144
  #endif
137
145
  }
138
146
 
147
+ /**************************
148
+ EventMachine_t::_UseKqueue
149
+ **************************/
150
+
151
+ void EventMachine_t::_UseKqueue()
152
+ {
153
+ /* Temporary.
154
+ * See comments under _UseEpoll.
155
+ */
156
+
157
+ #ifdef HAVE_KQUEUE
158
+ bKqueue = true;
159
+ #endif
160
+ }
161
+
139
162
 
140
163
  /****************************
141
164
  EventMachine_t::ScheduleHalt
@@ -337,6 +360,23 @@ void EventMachine_t::Run()
337
360
  }
338
361
  #endif
339
362
 
363
+ #ifdef HAVE_KQUEUE
364
+ if (bKqueue) {
365
+ kqfd = kqueue();
366
+ if (kqfd == -1) {
367
+ char buf[200];
368
+ snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
369
+ throw std::runtime_error (buf);
370
+ }
371
+ // cloexec not needed. By definition, kqueues are not carried across forks.
372
+
373
+ assert (LoopBreakerReader >= 0);
374
+ LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
375
+ assert (ld);
376
+ Add (ld);
377
+ }
378
+ #endif
379
+
340
380
  while (true) {
341
381
  gCurrentLoopTime = time(NULL);
342
382
  if (!_RunTimers())
@@ -369,6 +409,8 @@ bool EventMachine_t::_RunOnce()
369
409
  {
370
410
  if (bEpoll)
371
411
  return _RunEpollOnce();
412
+ else if (bKqueue)
413
+ return _RunKqueueOnce();
372
414
  else
373
415
  return _RunSelectOnce();
374
416
  }
@@ -384,7 +426,7 @@ bool EventMachine_t::_RunEpollOnce()
384
426
  #ifdef HAVE_EPOLL
385
427
  assert (epfd != -1);
386
428
  struct epoll_event ev [MaxEpollDescriptors];
387
- int s = epoll_wait (epfd, ev, MaxEpollDescriptors, 10);
429
+ int s = epoll_wait (epfd, ev, MaxEpollDescriptors, 50);
388
430
  if (s > 0) {
389
431
  for (int i=0; i < s; i++) {
390
432
  EventableDescriptor *ed = (EventableDescriptor*) ev[i].data.ptr;
@@ -413,20 +455,30 @@ bool EventMachine_t::_RunEpollOnce()
413
455
  // vector::pop_back works in constant time.
414
456
  // TODO, rip this out and only delete the descriptors we know have died,
415
457
  // rather than traversing the whole list.
458
+ // Modified 05Jan08 per suggestions by Chris Heath. It's possible that
459
+ // an EventableDescriptor will have a descriptor value of -1. That will
460
+ // happen if EventableDescriptor::Close was called on it. In that case,
461
+ // don't call epoll_ctl to remove the socket's filters from the epoll set.
462
+ // According to the epoll docs, this happens automatically when the
463
+ // descriptor is closed anyway. This is different from the case where
464
+ // the socket has already been closed but the descriptor in the ED object
465
+ // hasn't yet been set to INVALID_SOCKET.
416
466
  int i, j;
417
467
  int nSockets = Descriptors.size();
418
468
  for (i=0, j=0; i < nSockets; i++) {
419
469
  EventableDescriptor *ed = Descriptors[i];
420
470
  assert (ed);
421
471
  if (ed->ShouldDelete()) {
422
- assert (bEpoll); // wouldn't be in this method otherwise.
423
- assert (epfd != -1);
424
- int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
425
- // ENOENT is not an error because the socket may be already closed when we get here.
426
- if (e && (e != ENOENT)) {
427
- char buf [200];
428
- snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
429
- throw std::runtime_error (buf);
472
+ if (ed->GetSocket() != INVALID_SOCKET) {
473
+ assert (bEpoll); // wouldn't be in this method otherwise.
474
+ assert (epfd != -1);
475
+ int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
476
+ // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
477
+ if (e && (errno != ENOENT) && (errno != EBADF)) {
478
+ char buf [200];
479
+ snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
480
+ throw std::runtime_error (buf);
481
+ }
430
482
  }
431
483
 
432
484
  ModifiedDescriptors.erase (ed);
@@ -469,6 +521,82 @@ bool EventMachine_t::_RunEpollOnce()
469
521
  }
470
522
 
471
523
 
524
+ /******************************
525
+ EventMachine_t::_RunKqueueOnce
526
+ ******************************/
527
+
528
+ bool EventMachine_t::_RunKqueueOnce()
529
+ {
530
+ #ifdef HAVE_KQUEUE
531
+ assert (kqfd != -1);
532
+ const int maxKevents = 2000;
533
+ struct kevent Karray [maxKevents];
534
+ struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region
535
+
536
+ int k = kevent (kqfd, NULL, 0, Karray, maxKevents, &ts);
537
+ struct kevent *ke = Karray;
538
+ while (k > 0) {
539
+ EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
540
+ assert (ed);
541
+
542
+ if (ke->filter == EVFILT_READ)
543
+ ed->Read();
544
+ else if (ke->filter == EVFILT_WRITE)
545
+ ed->Write();
546
+ else
547
+ cerr << "Discarding unknown kqueue event " << ke->filter << endl;
548
+
549
+ --k;
550
+ ++ke;
551
+ }
552
+
553
+ { // cleanup dying sockets
554
+ // vector::pop_back works in constant time.
555
+ // TODO, rip this out and only delete the descriptors we know have died,
556
+ // rather than traversing the whole list.
557
+ // In kqueue, closing a descriptor automatically removes its event filters.
558
+
559
+ int i, j;
560
+ int nSockets = Descriptors.size();
561
+ for (i=0, j=0; i < nSockets; i++) {
562
+ EventableDescriptor *ed = Descriptors[i];
563
+ assert (ed);
564
+ if (ed->ShouldDelete()) {
565
+ ModifiedDescriptors.erase (ed);
566
+ delete ed;
567
+ }
568
+ else
569
+ Descriptors [j++] = ed;
570
+ }
571
+ while ((size_t)j < Descriptors.size())
572
+ Descriptors.pop_back();
573
+
574
+ }
575
+
576
+ { // dispatch heartbeats
577
+ if (gCurrentLoopTime >= NextHeartbeatTime) {
578
+ NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
579
+
580
+ for (int i=0; i < Descriptors.size(); i++) {
581
+ EventableDescriptor *ed = Descriptors[i];
582
+ assert (ed);
583
+ ed->Heartbeat();
584
+ }
585
+ }
586
+ }
587
+
588
+
589
+ // TODO, replace this with rb_thread_blocking_region for 1.9 builds.
590
+ timeval tv = {0,0};
591
+ EmSelect (0, NULL, NULL, NULL, &tv);
592
+
593
+ return true;
594
+ #else
595
+ throw std::runtime_error ("kqueue is not implemented on this platform");
596
+ #endif
597
+ }
598
+
599
+
472
600
  /*********************************
473
601
  EventMachine_t::_ModifyEpollEvent
474
602
  *********************************/
@@ -490,6 +618,48 @@ void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
490
618
  }
491
619
 
492
620
 
621
+
622
+ /**************************
623
+ SelectData_t::SelectData_t
624
+ **************************/
625
+
626
+ SelectData_t::SelectData_t()
627
+ {
628
+ maxsocket = 0;
629
+ FD_ZERO (&fdreads);
630
+ FD_ZERO (&fdwrites);
631
+ }
632
+
633
+
634
+ /*****************
635
+ _SelectDataSelect
636
+ *****************/
637
+
638
+ static VALUE _SelectDataSelect (void *v)
639
+ {
640
+ SelectData_t *sd = (SelectData_t*)v;
641
+ sd->nSockets = select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), NULL, &(sd->tv));
642
+ return Qnil;
643
+ }
644
+
645
+ /*********************
646
+ SelectData_t::_Select
647
+ *********************/
648
+
649
+ int SelectData_t::_Select()
650
+ {
651
+ #ifdef HAVE_TBR
652
+ rb_thread_blocking_region (_SelectDataSelect, (void*)this, RB_UBF_DFL, 0);
653
+ return nSockets;
654
+ #endif
655
+
656
+ #ifndef HAVE_TBR
657
+ return rb_thread_select (maxsocket+1, &fdreads, &fdwrites, NULL, &tv);
658
+ #endif
659
+ }
660
+
661
+
662
+
493
663
  /******************************
494
664
  EventMachine_t::_RunSelectOnce
495
665
  ******************************/
@@ -525,20 +695,23 @@ bool EventMachine_t::_RunSelectOnce()
525
695
  }
526
696
  */
527
697
 
698
+ SelectData_t SelectData;
699
+ /*
528
700
  fd_set fdreads, fdwrites;
529
701
  FD_ZERO (&fdreads);
530
702
  FD_ZERO (&fdwrites);
531
703
 
532
704
  int maxsocket = 0;
705
+ */
533
706
 
534
707
  // Always read the loop-breaker reader.
535
708
  // Changed 23Aug06, provisionally implemented for Windows with a UDP socket
536
709
  // running on localhost with a randomly-chosen port. (*Puke*)
537
710
  // Windows has a version of the Unix pipe() library function, but it doesn't
538
711
  // give you back descriptors that are selectable.
539
- FD_SET (LoopBreakerReader, &fdreads);
540
- if (maxsocket < LoopBreakerReader)
541
- maxsocket = LoopBreakerReader;
712
+ FD_SET (LoopBreakerReader, &(SelectData.fdreads));
713
+ if (SelectData.maxsocket < LoopBreakerReader)
714
+ SelectData.maxsocket = LoopBreakerReader;
542
715
 
543
716
  // prepare the sockets for reading and writing
544
717
  size_t i;
@@ -549,19 +722,23 @@ bool EventMachine_t::_RunSelectOnce()
549
722
  assert (sd != INVALID_SOCKET);
550
723
 
551
724
  if (ed->SelectForRead())
552
- FD_SET (sd, &fdreads);
725
+ FD_SET (sd, &(SelectData.fdreads));
553
726
  if (ed->SelectForWrite())
554
- FD_SET (sd, &fdwrites);
727
+ FD_SET (sd, &(SelectData.fdwrites));
555
728
 
556
- if (maxsocket < sd)
557
- maxsocket = sd;
729
+ if (SelectData.maxsocket < sd)
730
+ SelectData.maxsocket = sd;
558
731
  }
559
732
 
560
733
 
561
734
  { // read and write the sockets
562
735
  //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
563
- timeval tv = Quantum;
564
- int s = EmSelect (maxsocket+1, &fdreads, &fdwrites, NULL, &tv);
736
+ //timeval tv = Quantum;
737
+ SelectData.tv = Quantum;
738
+ int s = SelectData._Select();
739
+ //rb_thread_blocking_region(xxx,(void*)&SelectData,RB_UBF_DFL,0);
740
+ //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
741
+ //int s = SelectData.nSockets;
565
742
  if (s > 0) {
566
743
  /* Changed 01Jun07. We used to handle the Loop-breaker right here.
567
744
  * Now we do it AFTER all the regular descriptors. There's an
@@ -579,13 +756,13 @@ bool EventMachine_t::_RunSelectOnce()
579
756
  int sd = ed->GetSocket();
580
757
  assert (sd != INVALID_SOCKET);
581
758
 
582
- if (FD_ISSET (sd, &fdwrites))
759
+ if (FD_ISSET (sd, &(SelectData.fdwrites)))
583
760
  ed->Write();
584
- if (FD_ISSET (sd, &fdreads))
761
+ if (FD_ISSET (sd, &(SelectData.fdreads)))
585
762
  ed->Read();
586
763
  }
587
764
 
588
- if (FD_ISSET (LoopBreakerReader, &fdreads))
765
+ if (FD_ISSET (LoopBreakerReader, &(SelectData.fdreads)))
589
766
  _ReadLoopBreaker();
590
767
  }
591
768
  else if (s < 0) {
@@ -758,6 +935,16 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
758
935
  if (!server || !*server || !port)
759
936
  return NULL;
760
937
 
938
+ int family, bind_size;
939
+ struct sockaddr *bind_as = name2address (server, port, &family, &bind_size);
940
+ if (!bind_as)
941
+ return NULL;
942
+
943
+ int sd = socket (family, SOCK_STREAM, 0);
944
+ if (sd == INVALID_SOCKET)
945
+ return NULL;
946
+
947
+ /*
761
948
  sockaddr_in pin;
762
949
  unsigned long HostAddr;
763
950
 
@@ -783,6 +970,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
783
970
  int sd = socket (AF_INET, SOCK_STREAM, 0);
784
971
  if (sd == INVALID_SOCKET)
785
972
  return NULL;
973
+ */
786
974
 
787
975
  // From here on, ALL error returns must close the socket.
788
976
  // Set the new socket nonblocking.
@@ -790,11 +978,15 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
790
978
  closesocket (sd);
791
979
  return NULL;
792
980
  }
981
+ // Disable slow-start (Nagle algorithm).
982
+ int one = 1;
983
+ setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
793
984
 
794
985
  const char *out = NULL;
795
986
 
796
987
  #ifdef OS_UNIX
797
- if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
988
+ //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
989
+ if (connect (sd, bind_as, bind_size) == 0) {
798
990
  // This is a connect success, which Linux appears
799
991
  // never to give when the socket is nonblocking,
800
992
  // even if the connection is intramachine or to
@@ -866,7 +1058,8 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
866
1058
  #endif
867
1059
 
868
1060
  #ifdef OS_WIN32
869
- if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
1061
+ //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
1062
+ if (connect (sd, bind_as, bind_size) == 0) {
870
1063
  // This is a connect success, which Windows appears
871
1064
  // never to give when the socket is nonblocking,
872
1065
  // even if the connection is intramachine or to
@@ -971,6 +1164,71 @@ const char *EventMachine_t::ConnectToUnixServer (const char *server)
971
1164
  }
972
1165
 
973
1166
 
1167
+ /************
1168
+ name2address
1169
+ ************/
1170
+
1171
+ struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size)
1172
+ {
1173
+ // THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed.
1174
+ // Check the more-common cases first.
1175
+ // Return NULL if no resolution.
1176
+
1177
+ static struct sockaddr_in in4;
1178
+ static struct sockaddr_in6 in6;
1179
+ struct hostent *hp;
1180
+
1181
+ if (!server || !*server)
1182
+ server = "0.0.0.0";
1183
+
1184
+ memset (&in4, 0, sizeof(in4));
1185
+ if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) {
1186
+ if (family)
1187
+ *family = AF_INET;
1188
+ if (bind_size)
1189
+ *bind_size = sizeof(in4);
1190
+ in4.sin_family = AF_INET;
1191
+ in4.sin_port = htons (port);
1192
+ return (struct sockaddr*)&in4;
1193
+ }
1194
+
1195
+ #ifdef OS_UNIX
1196
+ memset (&in6, 0, sizeof(in6));
1197
+ if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) {
1198
+ if (family)
1199
+ *family = AF_INET6;
1200
+ if (bind_size)
1201
+ *bind_size = sizeof(in6);
1202
+ in6.sin6_family = AF_INET6;
1203
+ in6.sin6_port = htons (port);
1204
+ return (struct sockaddr*)&in6;
1205
+ }
1206
+ #endif
1207
+
1208
+ #ifdef OS_WIN32
1209
+ // TODO, must complete this branch. Windows doesn't have inet_pton.
1210
+ // A possible approach is to make a getaddrinfo call with the supplied
1211
+ // server address, constraining the hints to ipv6 and seeing if we
1212
+ // get any addresses.
1213
+ // For the time being, Ipv6 addresses aren't supported on Windows.
1214
+ #endif
1215
+
1216
+ hp = gethostbyname ((char*)server); // Windows requires the cast.
1217
+ if (hp) {
1218
+ in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
1219
+ if (family)
1220
+ *family = AF_INET;
1221
+ if (bind_size)
1222
+ *bind_size = sizeof(in4);
1223
+ in4.sin_family = AF_INET;
1224
+ in4.sin_port = htons (port);
1225
+ return (struct sockaddr*)&in4;
1226
+ }
1227
+
1228
+ return NULL;
1229
+ }
1230
+
1231
+
974
1232
  /*******************************
975
1233
  EventMachine_t::CreateTcpServer
976
1234
  *******************************/
@@ -983,15 +1241,22 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
983
1241
  * to indicate accepted connections.
984
1242
  */
985
1243
 
1244
+
1245
+ int family, bind_size;
1246
+ struct sockaddr *bind_here = name2address (server, port, &family, &bind_size);
1247
+ if (!bind_here)
1248
+ return NULL;
1249
+
986
1250
  const char *output_binding = NULL;
987
1251
 
988
- struct sockaddr_in sin;
1252
+ //struct sockaddr_in sin;
989
1253
 
990
- int sd_accept = socket (AF_INET, SOCK_STREAM, 0);
1254
+ int sd_accept = socket (family, SOCK_STREAM, 0);
991
1255
  if (sd_accept == INVALID_SOCKET) {
992
1256
  goto fail;
993
1257
  }
994
1258
 
1259
+ /*
995
1260
  memset (&sin, 0, sizeof(sin));
996
1261
  sin.sin_family = AF_INET;
997
1262
  sin.sin_addr.s_addr = INADDR_ANY;
@@ -1008,6 +1273,7 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
1008
1273
  sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
1009
1274
  }
1010
1275
  }
1276
+ */
1011
1277
 
1012
1278
  { // set reuseaddr to improve performance on restarts.
1013
1279
  int oval = 1;
@@ -1027,7 +1293,8 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
1027
1293
  }
1028
1294
 
1029
1295
 
1030
- if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
1296
+ //if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
1297
+ if (bind (sd_accept, bind_here, bind_size)) {
1031
1298
  //__warning ("binding failed");
1032
1299
  goto fail;
1033
1300
  }
@@ -1139,6 +1406,42 @@ void EventMachine_t::Add (EventableDescriptor *ed)
1139
1406
  }
1140
1407
 
1141
1408
 
1409
+ /*******************************
1410
+ EventMachine_t::ArmKqueueWriter
1411
+ *******************************/
1412
+
1413
+ void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
1414
+ {
1415
+ #ifdef HAVE_KQUEUE
1416
+ if (bKqueue) {
1417
+ if (!ed)
1418
+ throw std::runtime_error ("added bad descriptor");
1419
+ struct kevent k;
1420
+ EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, ed);
1421
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
1422
+ assert (t == 0);
1423
+ }
1424
+ #endif
1425
+ }
1426
+
1427
+ /*******************************
1428
+ EventMachine_t::ArmKqueueReader
1429
+ *******************************/
1430
+
1431
+ void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
1432
+ {
1433
+ #ifdef HAVE_KQUEUE
1434
+ if (bKqueue) {
1435
+ if (!ed)
1436
+ throw std::runtime_error ("added bad descriptor");
1437
+ struct kevent k;
1438
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
1439
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
1440
+ assert (t == 0);
1441
+ }
1442
+ #endif
1443
+ }
1444
+
1142
1445
  /**********************************
1143
1446
  EventMachine_t::_AddNewDescriptors
1144
1447
  **********************************/
@@ -1173,6 +1476,19 @@ void EventMachine_t::_AddNewDescriptors()
1173
1476
  }
1174
1477
  #endif
1175
1478
 
1479
+ #if HAVE_KQUEUE
1480
+ /*
1481
+ if (bKqueue) {
1482
+ // INCOMPLETE. Some descriptors don't want to be readable.
1483
+ assert (kqfd != -1);
1484
+ struct kevent k;
1485
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
1486
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
1487
+ assert (t == 0);
1488
+ }
1489
+ */
1490
+ #endif
1491
+
1176
1492
  Descriptors.push_back (ed);
1177
1493
  }
1178
1494
  NewDescriptors.clear();