eventmachine 0.10.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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();