eventmachine 0.12.6 → 0.12.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/{docs/README → README} +21 -13
  2. data/Rakefile +14 -4
  3. data/docs/DEFERRABLES +0 -5
  4. data/docs/INSTALL +2 -4
  5. data/docs/LEGAL +1 -1
  6. data/docs/LIGHTWEIGHT_CONCURRENCY +0 -2
  7. data/docs/PURE_RUBY +0 -2
  8. data/docs/RELEASE_NOTES +0 -2
  9. data/docs/SMTP +0 -7
  10. data/docs/SPAWNED_PROCESSES +0 -4
  11. data/docs/TODO +0 -2
  12. data/eventmachine.gemspec +17 -8
  13. data/examples/ex_channel.rb +43 -0
  14. data/examples/ex_queue.rb +2 -0
  15. data/examples/helper.rb +2 -0
  16. data/ext/cmain.cpp +119 -20
  17. data/ext/cplusplus.cpp +15 -6
  18. data/ext/ed.cpp +303 -93
  19. data/ext/ed.h +49 -22
  20. data/ext/em.cpp +368 -42
  21. data/ext/em.h +43 -6
  22. data/ext/eventmachine.h +21 -8
  23. data/ext/eventmachine_cpp.h +1 -0
  24. data/ext/extconf.rb +4 -0
  25. data/ext/kb.cpp +1 -2
  26. data/ext/pipe.cpp +1 -3
  27. data/ext/project.h +21 -0
  28. data/ext/rubymain.cpp +232 -32
  29. data/ext/ssl.cpp +38 -1
  30. data/ext/ssl.h +5 -1
  31. data/java/src/com/rubyeventmachine/Application.java +7 -3
  32. data/java/src/com/rubyeventmachine/EmReactor.java +16 -1
  33. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +25 -3
  34. data/lib/{protocols → em}/buftok.rb +16 -5
  35. data/lib/em/callback.rb +26 -0
  36. data/lib/em/channel.rb +57 -0
  37. data/lib/em/connection.rb +505 -0
  38. data/lib/em/deferrable.rb +144 -165
  39. data/lib/em/file_watch.rb +54 -0
  40. data/lib/em/future.rb +24 -25
  41. data/lib/em/messages.rb +1 -1
  42. data/lib/em/process_watch.rb +44 -0
  43. data/lib/em/processes.rb +58 -52
  44. data/lib/em/protocols.rb +35 -0
  45. data/lib/em/protocols/header_and_content.rb +138 -0
  46. data/lib/em/protocols/httpclient.rb +263 -0
  47. data/lib/em/protocols/httpclient2.rb +582 -0
  48. data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
  49. data/lib/em/protocols/linetext2.rb +160 -0
  50. data/lib/{protocols → em/protocols}/memcache.rb +37 -7
  51. data/lib/em/protocols/object_protocol.rb +39 -0
  52. data/lib/em/protocols/postgres3.rb +247 -0
  53. data/lib/em/protocols/saslauth.rb +175 -0
  54. data/lib/em/protocols/smtpclient.rb +331 -0
  55. data/lib/em/protocols/smtpserver.rb +547 -0
  56. data/lib/em/protocols/stomp.rb +200 -0
  57. data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
  58. data/lib/em/queue.rb +61 -0
  59. data/lib/em/spawnable.rb +53 -56
  60. data/lib/em/streamer.rb +92 -74
  61. data/lib/em/timers.rb +55 -0
  62. data/lib/em/version.rb +3 -0
  63. data/lib/eventmachine.rb +1008 -1298
  64. data/lib/evma.rb +1 -1
  65. data/lib/jeventmachine.rb +106 -101
  66. data/lib/pr_eventmachine.rb +47 -36
  67. data/tasks/project.rake +2 -1
  68. data/tests/client.crt +31 -0
  69. data/tests/client.key +51 -0
  70. data/tests/test_attach.rb +18 -0
  71. data/tests/test_basic.rb +108 -54
  72. data/tests/test_channel.rb +63 -0
  73. data/tests/test_connection_count.rb +2 -2
  74. data/tests/test_epoll.rb +109 -110
  75. data/tests/test_errors.rb +36 -36
  76. data/tests/test_exc.rb +22 -25
  77. data/tests/test_file_watch.rb +49 -0
  78. data/tests/test_futures.rb +77 -93
  79. data/tests/test_hc.rb +2 -2
  80. data/tests/test_httpclient.rb +55 -52
  81. data/tests/test_httpclient2.rb +110 -112
  82. data/tests/test_inactivity_timeout.rb +30 -0
  83. data/tests/test_kb.rb +8 -9
  84. data/tests/test_ltp2.rb +274 -277
  85. data/tests/test_next_tick.rb +91 -65
  86. data/tests/test_object_protocol.rb +37 -0
  87. data/tests/test_process_watch.rb +48 -0
  88. data/tests/test_processes.rb +56 -23
  89. data/tests/test_proxy_connection.rb +92 -0
  90. data/tests/test_pure.rb +1 -5
  91. data/tests/test_queue.rb +44 -0
  92. data/tests/test_running.rb +9 -14
  93. data/tests/test_sasl.rb +32 -34
  94. data/tests/test_send_file.rb +175 -176
  95. data/tests/test_servers.rb +37 -41
  96. data/tests/test_smtpserver.rb +47 -55
  97. data/tests/test_spawn.rb +284 -291
  98. data/tests/test_ssl_args.rb +1 -1
  99. data/tests/test_ssl_methods.rb +1 -1
  100. data/tests/test_ssl_verify.rb +82 -0
  101. data/tests/test_timers.rb +81 -88
  102. data/tests/test_ud.rb +0 -7
  103. data/tests/testem.rb +1 -1
  104. metadata +68 -39
  105. data/lib/em/eventable.rb +0 -39
  106. data/lib/eventmachine_version.rb +0 -31
  107. data/lib/protocols/header_and_content.rb +0 -129
  108. data/lib/protocols/httpcli2.rb +0 -803
  109. data/lib/protocols/httpclient.rb +0 -270
  110. data/lib/protocols/linetext2.rb +0 -161
  111. data/lib/protocols/postgres.rb +0 -261
  112. data/lib/protocols/saslauth.rb +0 -179
  113. data/lib/protocols/smtpclient.rb +0 -308
  114. data/lib/protocols/smtpserver.rb +0 -556
  115. data/lib/protocols/stomp.rb +0 -153
  116. data/tests/test_eventables.rb +0 -77
data/ext/ed.h CHANGED
@@ -67,27 +67,28 @@ class EventableDescriptor: public Bindable_t
67
67
  virtual bool GetSubprocessPid (pid_t*) {return false;}
68
68
 
69
69
  virtual void StartTls() {}
70
- virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename) {}
70
+ virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer) {}
71
71
 
72
72
  #ifdef WITH_SSL
73
73
  virtual X509 *GetPeerCert() {return NULL;}
74
74
  #endif
75
75
 
76
- // Properties: return 0/1 to signify T/F, and handle the values
77
- // through arguments.
78
- virtual int GetCommInactivityTimeout (int *value) {return 0;}
79
- virtual int SetCommInactivityTimeout (int *value) {return 0;}
76
+ virtual float GetCommInactivityTimeout() {return 0.0;}
77
+ virtual int SetCommInactivityTimeout (float value) {return 0;}
80
78
 
81
79
  #ifdef HAVE_EPOLL
82
80
  struct epoll_event *GetEpollEvent() { return &EpollEvent; }
83
81
  #endif
84
82
 
83
+ virtual void StartProxy(const char*);
84
+ virtual void StopProxy();
85
+
85
86
  private:
86
87
  bool bCloseNow;
87
88
  bool bCloseAfterWriting;
88
- int MySocket;
89
89
 
90
90
  protected:
91
+ int MySocket;
91
92
  enum {
92
93
  // 4 seconds is too short, most other libraries default to OS settings
93
94
  // which in 2.6 kernel defaults to a 60 second connect timeout.
@@ -96,16 +97,17 @@ class EventableDescriptor: public Bindable_t
96
97
  //
97
98
  // updating to 50 seconds, so we catch it before the OS does
98
99
 
99
- PendingConnectTimeout = 50 // can easily be made an instance variable
100
+ // can easily be made an instance variable
101
+ PendingConnectTimeout = 50000000 // now in usec
100
102
  };
101
103
 
102
104
  void (*EventCallback)(const char*, int, const char*, int);
103
-
104
- time_t CreatedAt;
105
- time_t LastRead;
106
- time_t LastWritten;
105
+ void _GenericInboundDispatch(const char*, int);
106
+
107
+ Int64 CreatedAt;
107
108
  bool bCallbackUnbind;
108
109
  int UnbindReasonCode;
110
+ char *ProxyTarget;
109
111
 
110
112
  #ifdef HAVE_EPOLL
111
113
  struct epoll_event EpollEvent;
@@ -153,8 +155,8 @@ class ConnectionDescriptor: public EventableDescriptor
153
155
 
154
156
  void SetConnectPending (bool f);
155
157
 
156
- void SetNotifyReadable (bool readable) { bNotifyReadable = readable; }
157
- void SetNotifyWritable (bool writable) { bNotifyWritable = writable; }
158
+ void SetNotifyReadable (bool);
159
+ void SetNotifyWritable (bool);
158
160
 
159
161
  virtual void Read();
160
162
  virtual void Write();
@@ -167,10 +169,12 @@ class ConnectionDescriptor: public EventableDescriptor
167
169
  virtual int GetOutboundDataSize() {return OutboundDataSize;}
168
170
 
169
171
  virtual void StartTls();
170
- virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename);
172
+ virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer);
171
173
 
172
174
  #ifdef WITH_SSL
173
175
  virtual X509 *GetPeerCert();
176
+ virtual bool VerifySslPeer(const char*);
177
+ virtual void AcceptSslPeer();
174
178
  #endif
175
179
 
176
180
  void SetServerMode() {bIsServer = true;}
@@ -178,8 +182,8 @@ class ConnectionDescriptor: public EventableDescriptor
178
182
  virtual bool GetPeername (struct sockaddr*);
179
183
  virtual bool GetSockname (struct sockaddr*);
180
184
 
181
- virtual int GetCommInactivityTimeout (int *value);
182
- virtual int SetCommInactivityTimeout (int *value);
185
+ virtual float GetCommInactivityTimeout();
186
+ virtual int SetCommInactivityTimeout (float value);
183
187
 
184
188
 
185
189
  protected:
@@ -208,10 +212,16 @@ class ConnectionDescriptor: public EventableDescriptor
208
212
  std::string CertChainFilename;
209
213
  std::string PrivateKeyFilename;
210
214
  bool bHandshakeSignaled;
215
+ bool bSslVerifyPeer;
216
+ bool bSslPeerAccepted;
211
217
  #endif
212
218
  bool bIsServer;
213
219
 
214
- time_t LastIo;
220
+ #ifdef HAVE_KQUEUE
221
+ bool bGotExtraKqueueEvent;
222
+ #endif
223
+
224
+ Int64 LastIo;
215
225
  int InactivityTimeout;
216
226
 
217
227
  private:
@@ -251,8 +261,8 @@ class DatagramDescriptor: public EventableDescriptor
251
261
  virtual bool GetPeername (struct sockaddr*);
252
262
  virtual bool GetSockname (struct sockaddr*);
253
263
 
254
- virtual int GetCommInactivityTimeout (int *value);
255
- virtual int SetCommInactivityTimeout (int *value);
264
+ virtual float GetCommInactivityTimeout();
265
+ virtual int SetCommInactivityTimeout (float value);
256
266
 
257
267
  static int SendDatagram (const char*, const char*, int, const char*, int);
258
268
 
@@ -272,7 +282,7 @@ class DatagramDescriptor: public EventableDescriptor
272
282
 
273
283
  struct sockaddr_in ReturnAddress;
274
284
 
275
- time_t LastIo;
285
+ Int64 LastIo;
276
286
  int InactivityTimeout;
277
287
  };
278
288
 
@@ -333,7 +343,7 @@ class PipeDescriptor: public EventableDescriptor
333
343
 
334
344
  protected:
335
345
  bool bReadAttemptedAfterClose;
336
- time_t LastIo;
346
+ Int64 LastIo;
337
347
  int InactivityTimeout;
338
348
 
339
349
  deque<OutboundPage> OutboundPages;
@@ -366,7 +376,7 @@ class KeyboardDescriptor: public EventableDescriptor
366
376
 
367
377
  protected:
368
378
  bool bReadAttemptedAfterClose;
369
- time_t LastIo;
379
+ Int64 LastIo;
370
380
  int InactivityTimeout;
371
381
 
372
382
  private:
@@ -374,6 +384,23 @@ class KeyboardDescriptor: public EventableDescriptor
374
384
  };
375
385
 
376
386
 
387
+ /***********************
388
+ class InotifyDescriptor
389
+ ************************/
390
+
391
+ class InotifyDescriptor: public EventableDescriptor
392
+ {
393
+ public:
394
+ InotifyDescriptor (EventMachine_t*);
395
+ virtual ~InotifyDescriptor();
396
+
397
+ void Read();
398
+ void Write();
399
+
400
+ virtual void Heartbeat() {}
401
+ virtual bool SelectForRead() {return true;}
402
+ virtual bool SelectForWrite() {return false;}
403
+ };
377
404
 
378
405
  #endif // __EventableDescriptor__H_
379
406
 
data/ext/em.cpp CHANGED
@@ -26,7 +26,7 @@ See the file COPYING for complete licensing information.
26
26
  // Keep a global variable floating around
27
27
  // with the current loop time as set by the Event Machine.
28
28
  // This avoids the need for frequent expensive calls to time(NULL);
29
- time_t gCurrentLoopTime;
29
+ Int64 gCurrentLoopTime;
30
30
 
31
31
  #ifdef OS_WIN32
32
32
  unsigned gTickCountTickover;
@@ -87,7 +87,9 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const c
87
87
  bEpoll (false),
88
88
  bKqueue (false),
89
89
  kqfd (-1),
90
- epfd (-1)
90
+ epfd (-1),
91
+ inotify (NULL),
92
+ HeartbeatInterval(2)
91
93
  {
92
94
  // Default time-slice is just smaller than one hundred mills.
93
95
  Quantum.tv_sec = 0;
@@ -96,7 +98,7 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const c
96
98
  gTerminateSignalReceived = false;
97
99
  // Make sure the current loop time is sane, in case we do any initializations of
98
100
  // objects before we start running.
99
- gCurrentLoopTime = time(NULL);
101
+ _UpdateTime();
100
102
 
101
103
  /* We initialize the network library here (only on Windows of course)
102
104
  * and initialize "loop breakers." Our destructor also does some network-level
@@ -129,6 +131,10 @@ EventMachine_t::~EventMachine_t()
129
131
  close (LoopBreakerReader);
130
132
  close (LoopBreakerWriter);
131
133
 
134
+ // Remove any file watch descriptors
135
+ for(map<int, Bindable_t*>::iterator f=Files.begin(); f != Files.end(); f++)
136
+ UnwatchFile (f->first);
137
+
132
138
  if (epfd != -1)
133
139
  close (epfd);
134
140
  if (kqfd != -1)
@@ -339,6 +345,28 @@ void EventMachine_t::_InitializeLoopBreaker()
339
345
  #endif
340
346
  }
341
347
 
348
+ /***************************
349
+ EventMachine_t::_UpdateTime
350
+ ***************************/
351
+
352
+ void EventMachine_t::_UpdateTime()
353
+ {
354
+ #if defined(OS_UNIX)
355
+ struct timeval tv;
356
+ gettimeofday (&tv, NULL);
357
+ gCurrentLoopTime = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
358
+
359
+ #elif defined(OS_WIN32)
360
+ unsigned tick = GetTickCount();
361
+ if (tick < gLastTickCount)
362
+ gTickCountTickover += 1;
363
+ gLastTickCount = tick;
364
+ gCurrentLoopTime = ((Int64)gTickCountTickover << 32) + (Int64)tick;
365
+
366
+ #else
367
+ gCurrentLoopTime = (Int64)time(NULL) * 1000000LL;
368
+ #endif
369
+ }
342
370
 
343
371
  /*******************
344
372
  EventMachine_t::Run
@@ -388,7 +416,7 @@ void EventMachine_t::Run()
388
416
  #endif
389
417
 
390
418
  while (true) {
391
- gCurrentLoopTime = time(NULL);
419
+ _UpdateTime();
392
420
  if (!_RunTimers())
393
421
  break;
394
422
 
@@ -435,26 +463,25 @@ bool EventMachine_t::_RunEpollOnce()
435
463
  {
436
464
  #ifdef HAVE_EPOLL
437
465
  assert (epfd != -1);
438
- struct epoll_event ev [MaxEpollDescriptors];
439
466
  int s;
440
467
 
441
468
  #ifdef BUILD_FOR_RUBY
442
469
  TRAP_BEG;
443
470
  #endif
444
- s = epoll_wait (epfd, ev, MaxEpollDescriptors, 50);
471
+ s = epoll_wait (epfd, epoll_events, MaxEvents, 50);
445
472
  #ifdef BUILD_FOR_RUBY
446
473
  TRAP_END;
447
474
  #endif
448
475
 
449
476
  if (s > 0) {
450
477
  for (int i=0; i < s; i++) {
451
- EventableDescriptor *ed = (EventableDescriptor*) ev[i].data.ptr;
478
+ EventableDescriptor *ed = (EventableDescriptor*) epoll_events[i].data.ptr;
452
479
 
453
- if (ev[i].events & (EPOLLERR | EPOLLHUP))
480
+ if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
454
481
  ed->ScheduleClose (false);
455
- if (ev[i].events & EPOLLIN)
482
+ if (epoll_events[i].events & EPOLLIN)
456
483
  ed->Read();
457
- if (ev[i].events & EPOLLOUT) {
484
+ if (epoll_events[i].events & EPOLLOUT) {
458
485
  ed->Write();
459
486
  epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent());
460
487
  // Ignoring return value
@@ -551,29 +578,43 @@ bool EventMachine_t::_RunKqueueOnce()
551
578
  {
552
579
  #ifdef HAVE_KQUEUE
553
580
  assert (kqfd != -1);
554
- const int maxKevents = 2000;
555
- struct kevent Karray [maxKevents];
556
581
  struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region
557
582
 
558
583
  int k;
559
584
  #ifdef BUILD_FOR_RUBY
560
585
  TRAP_BEG;
561
586
  #endif
562
- k = kevent (kqfd, NULL, 0, Karray, maxKevents, &ts);
587
+ k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
563
588
  #ifdef BUILD_FOR_RUBY
564
589
  TRAP_END;
565
590
  #endif
591
+
566
592
  struct kevent *ke = Karray;
567
593
  while (k > 0) {
568
- EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
569
- assert (ed);
594
+ switch (ke->filter)
595
+ {
596
+ case EVFILT_VNODE:
597
+ _HandleKqueueFileEvent (ke);
598
+ break;
599
+
600
+ case EVFILT_PROC:
601
+ _HandleKqueuePidEvent (ke);
602
+ break;
603
+
604
+ case EVFILT_READ:
605
+ case EVFILT_WRITE:
606
+ EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
607
+ assert (ed);
570
608
 
571
- if (ke->filter == EVFILT_READ)
572
- ed->Read();
573
- else if (ke->filter == EVFILT_WRITE)
574
- ed->Write();
575
- else
576
- cerr << "Discarding unknown kqueue event " << ke->filter << endl;
609
+ if (ke->filter == EVFILT_READ)
610
+ ed->Read();
611
+ else if (ke->filter == EVFILT_WRITE)
612
+ ed->Write();
613
+ else
614
+ cerr << "Discarding unknown kqueue event " << ke->filter << endl;
615
+
616
+ break;
617
+ }
577
618
 
578
619
  --k;
579
620
  ++ke;
@@ -875,25 +916,11 @@ bool EventMachine_t::_RunTimers()
875
916
  // Just keep inspecting and processing the list head until we hit
876
917
  // one that hasn't expired yet.
877
918
 
878
- #ifdef OS_UNIX
879
- struct timeval tv;
880
- gettimeofday (&tv, NULL);
881
- Int64 now = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
882
- #endif
883
-
884
- #ifdef OS_WIN32
885
- unsigned tick = GetTickCount();
886
- if (tick < gLastTickCount)
887
- gTickCountTickover += 1;
888
- gLastTickCount = tick;
889
- Int64 now = ((Int64)gTickCountTickover << 32) + (Int64)tick;
890
- #endif
891
-
892
919
  while (true) {
893
920
  multimap<Int64,Timer_t>::iterator i = Timers.begin();
894
921
  if (i == Timers.end())
895
922
  break;
896
- if (i->first > now)
923
+ if (i->first > gCurrentLoopTime)
897
924
  break;
898
925
  if (EventCallback)
899
926
  (*EventCallback) ("", EM_TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
@@ -946,7 +973,7 @@ const char *EventMachine_t::InstallOneshotTimer (int milliseconds)
946
973
  EventMachine_t::ConnectToServer
947
974
  *******************************/
948
975
 
949
- const char *EventMachine_t::ConnectToServer (const char *server, int port)
976
+ const char *EventMachine_t::ConnectToServer (const char *bind_addr, int bind_port, const char *server, int port)
950
977
  {
951
978
  /* We want to spend no more than a few seconds waiting for a connection
952
979
  * to a remote host. So we use a nonblocking connect.
@@ -975,9 +1002,10 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
975
1002
  return NULL;
976
1003
 
977
1004
  int family, bind_size;
978
- struct sockaddr *bind_as = name2address (server, port, &family, &bind_size);
979
- if (!bind_as)
1005
+ struct sockaddr bind_as, *bind_as_ptr = name2address (server, port, &family, &bind_size);
1006
+ if (!bind_as_ptr)
980
1007
  return NULL;
1008
+ bind_as = *bind_as_ptr; // copy because name2address points to a static
981
1009
 
982
1010
  int sd = socket (family, SOCK_STREAM, 0);
983
1011
  if (sd == INVALID_SOCKET)
@@ -1020,12 +1048,27 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
1020
1048
  // Disable slow-start (Nagle algorithm).
1021
1049
  int one = 1;
1022
1050
  setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
1051
+ // Set reuseaddr to improve performance on restarts
1052
+ setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one));
1053
+
1054
+ if (bind_addr) {
1055
+ int bind_to_size, bind_to_family;
1056
+ struct sockaddr *bind_to = name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size);
1057
+ if (!bind_to) {
1058
+ closesocket (sd);
1059
+ throw std::runtime_error ("bad bind address");
1060
+ }
1061
+ if (bind (sd, bind_to, bind_to_size) < 0) {
1062
+ closesocket (sd);
1063
+ throw std::runtime_error ("couldn't bind to address");
1064
+ }
1065
+ }
1023
1066
 
1024
1067
  const char *out = NULL;
1025
1068
 
1026
1069
  #ifdef OS_UNIX
1027
1070
  //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
1028
- if (connect (sd, bind_as, bind_size) == 0) {
1071
+ if (connect (sd, &bind_as, bind_size) == 0) {
1029
1072
  // This is a connect success, which Linux appears
1030
1073
  // never to give when the socket is nonblocking,
1031
1074
  // even if the connection is intramachine or to
@@ -1098,7 +1141,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
1098
1141
 
1099
1142
  #ifdef OS_WIN32
1100
1143
  //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
1101
- if (connect (sd, bind_as, bind_size) == 0) {
1144
+ if (connect (sd, &bind_as, bind_size) == 0) {
1102
1145
  // This is a connect success, which Windows appears
1103
1146
  // never to give when the socket is nonblocking,
1104
1147
  // even if the connection is intramachine or to
@@ -1236,6 +1279,8 @@ const char *EventMachine_t::AttachFD (int fd, bool notify_readable, bool notify_
1236
1279
  }
1237
1280
  }
1238
1281
 
1282
+ SetSocketNonblocking(fd);
1283
+
1239
1284
  ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
1240
1285
  if (!cd)
1241
1286
  throw std::runtime_error ("no connection allocated");
@@ -1929,9 +1974,290 @@ EventMachine_t::GetConnectionCount
1929
1974
 
1930
1975
  int EventMachine_t::GetConnectionCount ()
1931
1976
  {
1932
- return Descriptors.size();
1977
+ return Descriptors.size() + NewDescriptors.size();
1933
1978
  }
1934
1979
 
1935
1980
 
1981
+ /************************
1982
+ EventMachine_t::WatchPid
1983
+ ************************/
1984
+
1985
+ const char *EventMachine_t::WatchPid (int pid)
1986
+ {
1987
+ #ifdef HAVE_KQUEUE
1988
+ if (!bKqueue)
1989
+ throw std::runtime_error("must enable kqueue");
1990
+
1991
+ struct kevent event;
1992
+ int kqres;
1993
+
1994
+ EV_SET(&event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT | NOTE_FORK, 0, 0);
1995
+
1996
+ // Attempt to register the event
1997
+ kqres = kevent(kqfd, &event, 1, NULL, 0, NULL);
1998
+ if (kqres == -1) {
1999
+ char errbuf[200];
2000
+ sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
2001
+ throw std::runtime_error(errbuf);
2002
+ }
2003
+ #endif
2004
+
2005
+ #ifdef HAVE_KQUEUE
2006
+ Bindable_t* b = new Bindable_t();
2007
+ Pids.insert(make_pair (pid, b));
2008
+
2009
+ return b->GetBinding().c_str();
2010
+ #endif
2011
+
2012
+ throw std::runtime_error("no pid watching support on this system");
2013
+ }
2014
+
2015
+ /**************************
2016
+ EventMachine_t::UnwatchPid
2017
+ **************************/
2018
+
2019
+ void EventMachine_t::UnwatchPid (int pid)
2020
+ {
2021
+ Bindable_t *b = Pids[pid];
2022
+ assert(b);
2023
+ Pids.erase(pid);
2024
+
2025
+ #ifdef HAVE_KQUEUE
2026
+ struct kevent k;
2027
+
2028
+ EV_SET(&k, pid, EVFILT_PROC, EV_DELETE, 0, 0, 0);
2029
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
2030
+ // t==-1 if the process already exited; ignore this for now
2031
+ #endif
2032
+
2033
+ if (EventCallback)
2034
+ (*EventCallback)(b->GetBinding().c_str(), EM_CONNECTION_UNBOUND, NULL, 0);
2035
+
2036
+ delete b;
2037
+ }
2038
+
2039
+ void EventMachine_t::UnwatchPid (const char *sig)
2040
+ {
2041
+ for(map<int, Bindable_t*>::iterator i=Pids.begin(); i != Pids.end(); i++)
2042
+ {
2043
+ if (strncmp(i->second->GetBinding().c_str(), sig, strlen(sig)) == 0) {
2044
+ UnwatchPid (i->first);
2045
+ return;
2046
+ }
2047
+ }
2048
+
2049
+ throw std::runtime_error("attempted to remove invalid pid signature");
2050
+ }
2051
+
2052
+
2053
+ /*************************
2054
+ EventMachine_t::WatchFile
2055
+ *************************/
2056
+
2057
+ const char *EventMachine_t::WatchFile (const char *fpath)
2058
+ {
2059
+ struct stat sb;
2060
+ int sres;
2061
+ int wd = -1;
2062
+
2063
+ sres = stat(fpath, &sb);
2064
+
2065
+ if (sres == -1) {
2066
+ char errbuf[300];
2067
+ sprintf(errbuf, "error registering file %s for watching: %s", fpath, strerror(errno));
2068
+ throw std::runtime_error(errbuf);
2069
+ }
2070
+
2071
+ #ifdef HAVE_INOTIFY
2072
+ if (!inotify) {
2073
+ inotify = new InotifyDescriptor(this);
2074
+ assert (inotify);
2075
+ Add(inotify);
2076
+ }
2077
+
2078
+ wd = inotify_add_watch(inotify->GetSocket(), fpath, IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
2079
+ if (wd == -1) {
2080
+ char errbuf[300];
2081
+ sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
2082
+ throw std::runtime_error(errbuf);
2083
+ }
2084
+ #endif
2085
+
2086
+ #ifdef HAVE_KQUEUE
2087
+ if (!bKqueue)
2088
+ throw std::runtime_error("must enable kqueue");
2089
+
2090
+ // With kqueue we have to open the file first and use the resulting fd to register for events
2091
+ wd = open(fpath, O_RDONLY);
2092
+ if (wd == -1) {
2093
+ char errbuf[300];
2094
+ sprintf(errbuf, "failed to open file %s for registering with kqueue: %s", fpath, strerror(errno));
2095
+ throw std::runtime_error(errbuf);
2096
+ }
2097
+ _RegisterKqueueFileEvent(wd);
2098
+ #endif
2099
+
2100
+ if (wd != -1) {
2101
+ Bindable_t* b = new Bindable_t();
2102
+ Files.insert(make_pair (wd, b));
2103
+
2104
+ return b->GetBinding().c_str();
2105
+ }
2106
+
2107
+ throw std::runtime_error("no file watching support on this system"); // is this the right thing to do?
2108
+ }
2109
+
2110
+
2111
+ /***************************
2112
+ EventMachine_t::UnwatchFile
2113
+ ***************************/
2114
+
2115
+ void EventMachine_t::UnwatchFile (int wd)
2116
+ {
2117
+ Bindable_t *b = Files[wd];
2118
+ assert(b);
2119
+ Files.erase(wd);
2120
+
2121
+ #ifdef HAVE_INOTIFY
2122
+ inotify_rm_watch(inotify->GetSocket(), wd);
2123
+ #elif HAVE_KQUEUE
2124
+ // With kqueue, closing the monitored fd automatically clears all registered events for it
2125
+ close(wd);
2126
+ #endif
2127
+
2128
+ if (EventCallback)
2129
+ (*EventCallback)(b->GetBinding().c_str(), EM_CONNECTION_UNBOUND, NULL, 0);
2130
+
2131
+ delete b;
2132
+ }
2133
+
2134
+ void EventMachine_t::UnwatchFile (const char *sig)
2135
+ {
2136
+ for(map<int, Bindable_t*>::iterator i=Files.begin(); i != Files.end(); i++)
2137
+ {
2138
+ if (strncmp(i->second->GetBinding().c_str(), sig, strlen(sig)) == 0) {
2139
+ UnwatchFile (i->first);
2140
+ return;
2141
+ }
2142
+ }
2143
+ throw std::runtime_error("attempted to remove invalid watch signature");
2144
+ }
2145
+
2146
+
2147
+ /***********************************
2148
+ EventMachine_t::_ReadInotify_Events
2149
+ ************************************/
2150
+
2151
+ void EventMachine_t::_ReadInotifyEvents()
2152
+ {
2153
+ #ifdef HAVE_INOTIFY
2154
+ struct inotify_event event;
2155
+
2156
+ assert(EventCallback);
2157
+
2158
+ while (read(inotify->GetSocket(), &event, INOTIFY_EVENT_SIZE) > 0) {
2159
+ assert(event.len == 0);
2160
+ if (event.mask & IN_MODIFY)
2161
+ (*EventCallback)(Files [event.wd]->GetBinding().c_str(), EM_CONNECTION_READ, "modified", 8);
2162
+ if (event.mask & IN_MOVE_SELF)
2163
+ (*EventCallback)(Files [event.wd]->GetBinding().c_str(), EM_CONNECTION_READ, "moved", 5);
2164
+ if (event.mask & IN_DELETE_SELF) {
2165
+ (*EventCallback)(Files [event.wd]->GetBinding().c_str(), EM_CONNECTION_READ, "deleted", 7);
2166
+ UnwatchFile (event.wd);
2167
+ }
2168
+ }
2169
+ #endif
2170
+ }
2171
+
2172
+
2173
+ /*************************************
2174
+ EventMachine_t::_HandleKqueuePidEvent
2175
+ *************************************/
2176
+
2177
+ #ifdef HAVE_KQUEUE
2178
+ void EventMachine_t::_HandleKqueuePidEvent(struct kevent *event)
2179
+ {
2180
+ assert(EventCallback);
2181
+
2182
+ if (event->fflags & NOTE_FORK)
2183
+ (*EventCallback)(Pids [(int) event->ident]->GetBinding().c_str(), EM_CONNECTION_READ, "fork", 4);
2184
+ if (event->fflags & NOTE_EXIT) {
2185
+ (*EventCallback)(Pids [(int) event->ident]->GetBinding().c_str(), EM_CONNECTION_READ, "exit", 4);
2186
+ // stop watching the pid if it died
2187
+ UnwatchPid (event->ident);
2188
+ }
2189
+ }
2190
+ #endif
2191
+
2192
+
2193
+ /**************************************
2194
+ EventMachine_t::_HandleKqueueFileEvent
2195
+ ***************************************/
2196
+
2197
+ #ifdef HAVE_KQUEUE
2198
+ void EventMachine_t::_HandleKqueueFileEvent(struct kevent *event)
2199
+ {
2200
+ assert(EventCallback);
2201
+
2202
+ if (event->fflags & NOTE_WRITE)
2203
+ (*EventCallback)(Files [(int) event->ident]->GetBinding().c_str(), EM_CONNECTION_READ, "modified", 8);
2204
+ if (event->fflags & NOTE_RENAME)
2205
+ (*EventCallback)(Files [(int) event->ident]->GetBinding().c_str(), EM_CONNECTION_READ, "moved", 5);
2206
+ if (event->fflags & NOTE_DELETE) {
2207
+ (*EventCallback)(Files [(int) event->ident]->GetBinding().c_str(), EM_CONNECTION_READ, "deleted", 7);
2208
+ UnwatchFile (event->ident);
2209
+ }
2210
+ }
2211
+ #endif
2212
+
2213
+
2214
+ /****************************************
2215
+ EventMachine_t::_RegisterKqueueFileEvent
2216
+ *****************************************/
2217
+
2218
+ #ifdef HAVE_KQUEUE
2219
+ void EventMachine_t::_RegisterKqueueFileEvent(int fd)
2220
+ {
2221
+ struct kevent newevent;
2222
+ int kqres;
2223
+
2224
+ // Setup the event with our fd and proper flags
2225
+ EV_SET(&newevent, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_WRITE, 0, 0);
2226
+
2227
+ // Attempt to register the event
2228
+ kqres = kevent(kqfd, &newevent, 1, NULL, 0, NULL);
2229
+ if (kqres == -1) {
2230
+ char errbuf[200];
2231
+ sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
2232
+ close(fd);
2233
+ throw std::runtime_error(errbuf);
2234
+ }
2235
+ }
2236
+ #endif
2237
+
2238
+
2239
+ /************************************
2240
+ EventMachine_t::GetHeartbeatInterval
2241
+ *************************************/
2242
+
2243
+ float EventMachine_t::GetHeartbeatInterval()
2244
+ {
2245
+ return ((float)HeartbeatInterval / 1000000);
2246
+ }
2247
+
2248
+
2249
+ /************************************
2250
+ EventMachine_t::SetHeartbeatInterval
2251
+ *************************************/
2252
+
2253
+ int EventMachine_t::SetHeartbeatInterval(float interval)
2254
+ {
2255
+ int iv = (int)(interval * 1000000);
2256
+ if (iv > 0) {
2257
+ HeartbeatInterval = iv;
2258
+ return 1;
2259
+ }
2260
+ return 0;
2261
+ }
1936
2262
  //#endif // OS_UNIX
1937
2263