eventmachine 0.12.10 → 1.0.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +1 -0
  3. data/README +1 -2
  4. data/Rakefile +4 -76
  5. data/docs/DEFERRABLES +183 -70
  6. data/docs/KEYBOARD +15 -11
  7. data/docs/LIGHTWEIGHT_CONCURRENCY +84 -24
  8. data/docs/SMTP +3 -1
  9. data/docs/SPAWNED_PROCESSES +84 -25
  10. data/eventmachine.gemspec +19 -26
  11. data/examples/ex_tick_loop_array.rb +15 -0
  12. data/examples/ex_tick_loop_counter.rb +32 -0
  13. data/ext/binder.cpp +0 -1
  14. data/ext/cmain.cpp +36 -11
  15. data/ext/cplusplus.cpp +1 -1
  16. data/ext/ed.cpp +104 -113
  17. data/ext/ed.h +24 -30
  18. data/ext/em.cpp +347 -248
  19. data/ext/em.h +23 -16
  20. data/ext/eventmachine.h +5 -3
  21. data/ext/extconf.rb +5 -3
  22. data/ext/fastfilereader/extconf.rb +5 -3
  23. data/ext/fastfilereader/mapper.cpp +1 -1
  24. data/ext/kb.cpp +1 -3
  25. data/ext/pipe.cpp +9 -11
  26. data/ext/project.h +12 -4
  27. data/ext/rubymain.cpp +138 -89
  28. data/java/src/com/rubyeventmachine/EmReactor.java +1 -0
  29. data/lib/em/channel.rb +1 -1
  30. data/lib/em/connection.rb +6 -1
  31. data/lib/em/deferrable.rb +16 -2
  32. data/lib/em/iterator.rb +270 -0
  33. data/lib/em/protocols.rb +1 -1
  34. data/lib/em/protocols/httpclient.rb +5 -0
  35. data/lib/em/protocols/line_protocol.rb +28 -0
  36. data/lib/em/protocols/smtpserver.rb +101 -8
  37. data/lib/em/protocols/stomp.rb +1 -1
  38. data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +1 -11
  39. data/lib/em/queue.rb +1 -0
  40. data/lib/em/streamer.rb +1 -1
  41. data/lib/em/tick_loop.rb +85 -0
  42. data/lib/em/timers.rb +2 -1
  43. data/lib/em/version.rb +1 -1
  44. data/lib/eventmachine.rb +38 -84
  45. data/lib/jeventmachine.rb +1 -0
  46. data/tests/test_attach.rb +13 -3
  47. data/tests/test_basic.rb +60 -95
  48. data/tests/test_channel.rb +3 -2
  49. data/tests/test_defer.rb +14 -12
  50. data/tests/test_deferrable.rb +35 -0
  51. data/tests/test_file_watch.rb +1 -1
  52. data/tests/test_futures.rb +1 -1
  53. data/tests/test_hc.rb +40 -68
  54. data/tests/test_httpclient.rb +15 -6
  55. data/tests/test_httpclient2.rb +3 -2
  56. data/tests/test_inactivity_timeout.rb +3 -3
  57. data/tests/test_ltp.rb +13 -5
  58. data/tests/test_next_tick.rb +1 -1
  59. data/tests/test_pending_connect_timeout.rb +2 -2
  60. data/tests/test_process_watch.rb +36 -34
  61. data/tests/test_proxy_connection.rb +52 -0
  62. data/tests/test_pure.rb +10 -1
  63. data/tests/test_sasl.rb +1 -1
  64. data/tests/test_send_file.rb +16 -7
  65. data/tests/test_servers.rb +1 -1
  66. data/tests/test_tick_loop.rb +59 -0
  67. data/tests/test_timers.rb +13 -15
  68. metadata +45 -17
  69. data/web/whatis +0 -7
data/ext/ed.h CHANGED
@@ -62,7 +62,7 @@ class EventableDescriptor: public Bindable_t
62
62
  bool IsCloseScheduled();
63
63
  virtual void HandleError(){ ScheduleClose (false); }
64
64
 
65
- void SetEventCallback (void (*cb)(const unsigned long, int, const char*, const unsigned long));
65
+ void SetEventCallback (EMCallback);
66
66
 
67
67
  virtual bool GetPeername (struct sockaddr*) {return false;}
68
68
  virtual bool GetSockname (struct sockaddr*) {return false;}
@@ -75,16 +75,16 @@ class EventableDescriptor: public Bindable_t
75
75
  virtual X509 *GetPeerCert() {return NULL;}
76
76
  #endif
77
77
 
78
- virtual float GetCommInactivityTimeout() {return 0.0;}
79
- virtual int SetCommInactivityTimeout (float value) {return 0;}
80
- float GetPendingConnectTimeout();
81
- int SetPendingConnectTimeout (float value);
78
+ virtual uint64_t GetCommInactivityTimeout() {return 0;}
79
+ virtual int SetCommInactivityTimeout (uint64_t value) {return 0;}
80
+ uint64_t GetPendingConnectTimeout();
81
+ int SetPendingConnectTimeout (uint64_t value);
82
82
 
83
83
  #ifdef HAVE_EPOLL
84
84
  struct epoll_event *GetEpollEvent() { return &EpollEvent; }
85
85
  #endif
86
86
 
87
- virtual void StartProxy(const unsigned long, const unsigned long);
87
+ virtual void StartProxy(const unsigned long, const unsigned long, const unsigned long);
88
88
  virtual void StopProxy();
89
89
  virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
90
90
  virtual int SendOutboundData(const char*,int){ return -1; }
@@ -92,6 +92,10 @@ class EventableDescriptor: public Bindable_t
92
92
  virtual bool Pause(){ return false; }
93
93
  virtual bool Resume(){ return false; }
94
94
 
95
+ virtual int ReportErrorStatus(){ return 0; }
96
+ virtual bool IsConnectPending(){ return false; }
97
+ virtual uint64_t GetNextHeartbeat();
98
+
95
99
  private:
96
100
  bool bCloseNow;
97
101
  bool bCloseAfterWriting;
@@ -99,12 +103,14 @@ class EventableDescriptor: public Bindable_t
99
103
  protected:
100
104
  int MySocket;
101
105
 
102
- void (*EventCallback)(const unsigned long, int, const char*, const unsigned long);
106
+ EMCallback EventCallback;
103
107
  void _GenericInboundDispatch(const char*, int);
104
108
 
105
- Int64 CreatedAt;
109
+ uint64_t CreatedAt;
106
110
  bool bCallbackUnbind;
107
111
  int UnbindReasonCode;
112
+
113
+ unsigned long BytesToProxy;
108
114
  EventableDescriptor *ProxyTarget;
109
115
  EventableDescriptor *ProxiedFrom;
110
116
 
@@ -115,7 +121,10 @@ class EventableDescriptor: public Bindable_t
115
121
  #endif
116
122
 
117
123
  EventMachine_t *MyEventMachine;
118
- int PendingConnectTimeout;
124
+ uint64_t PendingConnectTimeout;
125
+ uint64_t InactivityTimeout;
126
+ uint64_t LastActivity;
127
+ uint64_t NextHeartbeat;
119
128
  };
120
129
 
121
130
 
@@ -149,10 +158,6 @@ class ConnectionDescriptor: public EventableDescriptor
149
158
  ConnectionDescriptor (int, EventMachine_t*);
150
159
  virtual ~ConnectionDescriptor();
151
160
 
152
- static int SendDataToConnection (const unsigned long, const char*, int);
153
- static void CloseConnection (const unsigned long, bool);
154
- static int ReportErrorStatus (const unsigned long);
155
-
156
161
  int SendOutboundData (const char*, int);
157
162
 
158
163
  void SetConnectPending (bool f);
@@ -195,9 +200,11 @@ class ConnectionDescriptor: public EventableDescriptor
195
200
  virtual bool GetPeername (struct sockaddr*);
196
201
  virtual bool GetSockname (struct sockaddr*);
197
202
 
198
- virtual float GetCommInactivityTimeout();
199
- virtual int SetCommInactivityTimeout (float value);
203
+ virtual uint64_t GetCommInactivityTimeout();
204
+ virtual int SetCommInactivityTimeout (uint64_t value);
200
205
 
206
+ virtual int ReportErrorStatus();
207
+ virtual bool IsConnectPending(){ return bConnectPending; }
201
208
 
202
209
  protected:
203
210
  struct OutboundPage {
@@ -236,8 +243,6 @@ class ConnectionDescriptor: public EventableDescriptor
236
243
  #endif
237
244
 
238
245
  bool bIsServer;
239
- Int64 LastIo;
240
- int InactivityTimeout;
241
246
 
242
247
  private:
243
248
  void _UpdateEvents();
@@ -246,7 +251,6 @@ class ConnectionDescriptor: public EventableDescriptor
246
251
  void _DispatchInboundData (const char *buffer, int size);
247
252
  void _DispatchCiphertext();
248
253
  int _SendRawOutboundData (const char*, int);
249
- int _ReportErrorStatus();
250
254
  void _CheckHandshakeStatus();
251
255
 
252
256
  };
@@ -278,11 +282,8 @@ class DatagramDescriptor: public EventableDescriptor
278
282
  virtual bool GetPeername (struct sockaddr*);
279
283
  virtual bool GetSockname (struct sockaddr*);
280
284
 
281
- virtual float GetCommInactivityTimeout();
282
- virtual int SetCommInactivityTimeout (float value);
283
-
284
- static int SendDatagram (const unsigned long, const char*, int, const char*, int);
285
-
285
+ virtual uint64_t GetCommInactivityTimeout();
286
+ virtual int SetCommInactivityTimeout (uint64_t value);
286
287
 
287
288
  protected:
288
289
  struct OutboundPage {
@@ -298,9 +299,6 @@ class DatagramDescriptor: public EventableDescriptor
298
299
  int OutboundDataSize;
299
300
 
300
301
  struct sockaddr_in ReturnAddress;
301
-
302
- Int64 LastIo;
303
- int InactivityTimeout;
304
302
  };
305
303
 
306
304
 
@@ -360,8 +358,6 @@ class PipeDescriptor: public EventableDescriptor
360
358
 
361
359
  protected:
362
360
  bool bReadAttemptedAfterClose;
363
- Int64 LastIo;
364
- int InactivityTimeout;
365
361
 
366
362
  deque<OutboundPage> OutboundPages;
367
363
  int OutboundDataSize;
@@ -393,8 +389,6 @@ class KeyboardDescriptor: public EventableDescriptor
393
389
 
394
390
  protected:
395
391
  bool bReadAttemptedAfterClose;
396
- Int64 LastIo;
397
- int InactivityTimeout;
398
392
 
399
393
  private:
400
394
  void _DispatchInboundData (const char *buffer, int size);
data/ext/em.cpp CHANGED
@@ -20,24 +20,12 @@ See the file COPYING for complete licensing information.
20
20
  // THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY.
21
21
  //#ifdef OS_UNIX
22
22
 
23
-
24
23
  #include "project.h"
25
24
 
26
- // Keep a global variable floating around
27
- // with the current loop time as set by the Event Machine.
28
- // This avoids the need for frequent expensive calls to time(NULL);
29
- Int64 gCurrentLoopTime;
30
-
31
- #ifdef OS_WIN32
32
- unsigned gTickCountTickover;
33
- unsigned gLastTickCount;
34
- #endif
35
-
36
-
37
25
  /* The numer of max outstanding timers was once a const enum defined in em.h.
38
26
  * Now we define it here so that users can change its value if necessary.
39
27
  */
40
- static unsigned int MaxOutstandingTimers = 10000;
28
+ static unsigned int MaxOutstandingTimers = 100000;
41
29
 
42
30
 
43
31
  /* Internal helper to convert strings to internet addresses. IPv6-aware.
@@ -79,7 +67,7 @@ void EventMachine_t::SetMaxTimerCount (int count)
79
67
  EventMachine_t::EventMachine_t
80
68
  ******************************/
81
69
 
82
- EventMachine_t::EventMachine_t (void (*event_callback)(const unsigned long, int, const char*, const unsigned long)):
70
+ EventMachine_t::EventMachine_t (EMCallback event_callback):
83
71
  HeartbeatInterval(2000000),
84
72
  EventCallback (event_callback),
85
73
  NextHeartbeatTime (0),
@@ -316,7 +304,7 @@ void EventMachine_t::_InitializeLoopBreaker()
316
304
  #ifdef OS_UNIX
317
305
  int fd[2];
318
306
  if (pipe (fd))
319
- throw std::runtime_error ("no loop breaker");
307
+ throw std::runtime_error (strerror(errno));
320
308
 
321
309
  LoopBreakerWriter = fd[1];
322
310
  LoopBreakerReader = fd[0];
@@ -353,21 +341,76 @@ EventMachine_t::_UpdateTime
353
341
 
354
342
  void EventMachine_t::_UpdateTime()
355
343
  {
344
+ MyCurrentLoopTime = GetRealTime();
345
+ }
346
+
347
+ /***************************
348
+ EventMachine_t::GetRealTime
349
+ ***************************/
350
+
351
+ uint64_t EventMachine_t::GetRealTime()
352
+ {
353
+ uint64_t current_time;
356
354
  #if defined(OS_UNIX)
357
355
  struct timeval tv;
358
356
  gettimeofday (&tv, NULL);
359
- gCurrentLoopTime = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
357
+ current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)(tv.tv_usec));
360
358
 
361
359
  #elif defined(OS_WIN32)
362
360
  unsigned tick = GetTickCount();
363
- if (tick < gLastTickCount)
364
- gTickCountTickover += 1;
365
- gLastTickCount = tick;
366
- gCurrentLoopTime = ((Int64)gTickCountTickover << 32) + (Int64)tick;
361
+ if (tick < LastTickCount)
362
+ TickCountTickover += 1;
363
+ LastTickCount = tick;
364
+ current_time = ((uint64_t)TickCountTickover << 32) + (uint64_t)tick;
367
365
 
368
366
  #else
369
- gCurrentLoopTime = (Int64)time(NULL) * 1000000LL;
367
+ current_time = (uint64_t)time(NULL) * 1000000LL;
370
368
  #endif
369
+ return current_time;
370
+ }
371
+
372
+ /***********************************
373
+ EventMachine_t::_DispatchHeartbeats
374
+ ***********************************/
375
+
376
+ void EventMachine_t::_DispatchHeartbeats()
377
+ {
378
+ while (true) {
379
+ multimap<uint64_t,EventableDescriptor*>::iterator i = Heartbeats.begin();
380
+ if (i == Heartbeats.end())
381
+ break;
382
+ if (i->first > MyCurrentLoopTime)
383
+ break;
384
+ EventableDescriptor *ed = i->second;
385
+ ed->Heartbeat();
386
+ QueueHeartbeat(ed);
387
+ }
388
+ }
389
+
390
+ /******************************
391
+ EventMachine_t::QueueHeartbeat
392
+ ******************************/
393
+
394
+ void EventMachine_t::QueueHeartbeat(EventableDescriptor *ed)
395
+ {
396
+ uint64_t heartbeat = ed->GetNextHeartbeat();
397
+
398
+ if (heartbeat) {
399
+ #ifndef HAVE_MAKE_PAIR
400
+ Heartbeats.insert (multimap<uint64_t,EventableDescriptor*>::value_type (heartbeat, ed));
401
+ #else
402
+ Heartbeats.insert (make_pair (heartbeat, ed));
403
+ #endif
404
+ }
405
+ }
406
+
407
+ /******************************
408
+ EventMachine_t::ClearHeartbeat
409
+ ******************************/
410
+
411
+ void EventMachine_t::ClearHeartbeat(uint64_t key)
412
+ {
413
+ Heartbeats.erase(key);
371
414
  }
372
415
 
373
416
  /*******************
@@ -447,12 +490,16 @@ EventMachine_t::_RunOnce
447
490
 
448
491
  bool EventMachine_t::_RunOnce()
449
492
  {
493
+ bool ret;
450
494
  if (bEpoll)
451
- return _RunEpollOnce();
495
+ ret = _RunEpollOnce();
452
496
  else if (bKqueue)
453
- return _RunKqueueOnce();
497
+ ret = _RunKqueueOnce();
454
498
  else
455
- return _RunSelectOnce();
499
+ ret = _RunSelectOnce();
500
+ _DispatchHeartbeats();
501
+ _CleanupSockets();
502
+ return ret;
456
503
  }
457
504
 
458
505
 
@@ -467,12 +514,31 @@ bool EventMachine_t::_RunEpollOnce()
467
514
  assert (epfd != -1);
468
515
  int s;
469
516
 
517
+ timeval tv = _TimeTilNextEvent();
518
+
470
519
  #ifdef BUILD_FOR_RUBY
520
+ int ret = 0;
521
+ fd_set fdreads;
522
+
523
+ FD_ZERO(&fdreads);
524
+ FD_SET(epfd, &fdreads);
525
+
526
+ if ((ret = rb_thread_select(epfd + 1, &fdreads, NULL, NULL, &tv)) < 1) {
527
+ if (ret == -1) {
528
+ assert(errno != EINVAL);
529
+ assert(errno != EBADF);
530
+ }
531
+ return true;
532
+ }
533
+
471
534
  TRAP_BEG;
472
- #endif
473
- s = epoll_wait (epfd, epoll_events, MaxEvents, 50);
474
- #ifdef BUILD_FOR_RUBY
535
+ s = epoll_wait (epfd, epoll_events, MaxEvents, 0);
475
536
  TRAP_END;
537
+ #else
538
+ int duration = 0;
539
+ duration = duration + (tv.tv_sec * 1000);
540
+ duration = duration + (tv.tv_usec / 1000);
541
+ s = epoll_wait (epfd, epoll_events, MaxEvents, duration);
476
542
  #endif
477
543
 
478
544
  if (s > 0) {
@@ -501,72 +567,6 @@ bool EventMachine_t::_RunEpollOnce()
501
567
  EmSelect (0, NULL, NULL, NULL, &tv);
502
568
  }
503
569
 
504
- { // cleanup dying sockets
505
- // vector::pop_back works in constant time.
506
- // TODO, rip this out and only delete the descriptors we know have died,
507
- // rather than traversing the whole list.
508
- // Modified 05Jan08 per suggestions by Chris Heath. It's possible that
509
- // an EventableDescriptor will have a descriptor value of -1. That will
510
- // happen if EventableDescriptor::Close was called on it. In that case,
511
- // don't call epoll_ctl to remove the socket's filters from the epoll set.
512
- // According to the epoll docs, this happens automatically when the
513
- // descriptor is closed anyway. This is different from the case where
514
- // the socket has already been closed but the descriptor in the ED object
515
- // hasn't yet been set to INVALID_SOCKET.
516
- int i, j;
517
- int nSockets = Descriptors.size();
518
- for (i=0, j=0; i < nSockets; i++) {
519
- EventableDescriptor *ed = Descriptors[i];
520
- assert (ed);
521
- if (ed->ShouldDelete()) {
522
- if (ed->GetSocket() != INVALID_SOCKET) {
523
- assert (bEpoll); // wouldn't be in this method otherwise.
524
- assert (epfd != -1);
525
- int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
526
- // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
527
- if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
528
- char buf [200];
529
- snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
530
- throw std::runtime_error (buf);
531
- }
532
- }
533
-
534
- ModifiedDescriptors.erase (ed);
535
- delete ed;
536
- }
537
- else
538
- Descriptors [j++] = ed;
539
- }
540
- while ((size_t)j < Descriptors.size())
541
- Descriptors.pop_back();
542
-
543
- }
544
-
545
- // TODO, heartbeats.
546
- // Added 14Sep07, its absence was noted by Brian Candler. But the comment was here, indicated
547
- // that this got thought about and not done when EPOLL was originally written. Was there a reason
548
- // not to do it, or was it an oversight? Certainly, running a heartbeat on 50,000 connections every
549
- // two seconds can get to be a real bear, especially if all we're doing is timing out dead ones.
550
- // Maybe there's a better way to do this. (Or maybe it's not that expensive after all.)
551
- //
552
- { // dispatch heartbeats
553
- if (gCurrentLoopTime >= NextHeartbeatTime) {
554
- NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
555
-
556
- for (int i=0; i < Descriptors.size(); i++) {
557
- EventableDescriptor *ed = Descriptors[i];
558
- assert (ed);
559
- ed->Heartbeat();
560
- }
561
- }
562
- }
563
-
564
- #ifdef BUILD_FOR_RUBY
565
- if (!rb_thread_alone()) {
566
- rb_thread_schedule();
567
- }
568
- #endif
569
-
570
570
  return true;
571
571
  #else
572
572
  throw std::runtime_error ("epoll is not implemented on this platform");
@@ -582,15 +582,33 @@ bool EventMachine_t::_RunKqueueOnce()
582
582
  {
583
583
  #ifdef HAVE_KQUEUE
584
584
  assert (kqfd != -1);
585
- struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region
586
-
587
585
  int k;
586
+
587
+ timeval tv = _TimeTilNextEvent();
588
+
588
589
  #ifdef BUILD_FOR_RUBY
590
+ int ret = 0;
591
+ fd_set fdreads;
592
+
593
+ FD_ZERO(&fdreads);
594
+ FD_SET(kqfd, &fdreads);
595
+
596
+ if ((ret = rb_thread_select(kqfd + 1, &fdreads, NULL, NULL, &tv)) < 1) {
597
+ if (ret == -1) {
598
+ assert(errno != EINVAL);
599
+ assert(errno != EBADF);
600
+ }
601
+ return true;
602
+ }
603
+
589
604
  TRAP_BEG;
590
- #endif
591
- k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
592
- #ifdef BUILD_FOR_RUBY
605
+ k = kevent (kqfd, NULL, 0, Karray, MaxEvents, NULL);
593
606
  TRAP_END;
607
+ #else
608
+ struct timespec ts;
609
+ ts.tv_sec = tv.tv_sec;
610
+ ts.tv_nsec = tv.tv_usec * 1000;
611
+ k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
594
612
  #endif
595
613
 
596
614
  struct kevent *ke = Karray;
@@ -627,42 +645,6 @@ bool EventMachine_t::_RunKqueueOnce()
627
645
  ++ke;
628
646
  }
629
647
 
630
- { // cleanup dying sockets
631
- // vector::pop_back works in constant time.
632
- // TODO, rip this out and only delete the descriptors we know have died,
633
- // rather than traversing the whole list.
634
- // In kqueue, closing a descriptor automatically removes its event filters.
635
-
636
- int i, j;
637
- int nSockets = Descriptors.size();
638
- for (i=0, j=0; i < nSockets; i++) {
639
- EventableDescriptor *ed = Descriptors[i];
640
- assert (ed);
641
- if (ed->ShouldDelete()) {
642
- ModifiedDescriptors.erase (ed);
643
- delete ed;
644
- }
645
- else
646
- Descriptors [j++] = ed;
647
- }
648
- while ((size_t)j < Descriptors.size())
649
- Descriptors.pop_back();
650
-
651
- }
652
-
653
- { // dispatch heartbeats
654
- if (gCurrentLoopTime >= NextHeartbeatTime) {
655
- NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
656
-
657
- for (unsigned int i=0; i < Descriptors.size(); i++) {
658
- EventableDescriptor *ed = Descriptors[i];
659
- assert (ed);
660
- ed->Heartbeat();
661
- }
662
- }
663
- }
664
-
665
-
666
648
  // TODO, replace this with rb_thread_blocking_region for 1.9 builds.
667
649
  #ifdef BUILD_FOR_RUBY
668
650
  if (!rb_thread_alone()) {
@@ -677,6 +659,93 @@ bool EventMachine_t::_RunKqueueOnce()
677
659
  }
678
660
 
679
661
 
662
+ /*********************************
663
+ EventMachine_t::_TimeTilNextEvent
664
+ *********************************/
665
+
666
+ timeval EventMachine_t::_TimeTilNextEvent()
667
+ {
668
+ uint64_t next_event = 0;
669
+
670
+ if (!Heartbeats.empty()) {
671
+ multimap<uint64_t,EventableDescriptor*>::iterator heartbeats = Heartbeats.begin();
672
+ next_event = heartbeats->first;
673
+ }
674
+
675
+ if (!Timers.empty()) {
676
+ multimap<uint64_t,Timer_t>::iterator timers = Timers.begin();
677
+ if (next_event == 0 || timers->first < next_event)
678
+ next_event = timers->first;
679
+ }
680
+
681
+ if (!NewDescriptors.empty() || !ModifiedDescriptors.empty()) {
682
+ next_event = MyCurrentLoopTime;
683
+ }
684
+
685
+ timeval tv;
686
+
687
+ if (next_event == 0) {
688
+ tv = Quantum;
689
+ } else {
690
+ if (next_event > MyCurrentLoopTime) {
691
+ uint64_t duration = next_event - MyCurrentLoopTime;
692
+ tv.tv_sec = duration / 1000000;
693
+ tv.tv_usec = duration % 1000000;
694
+ } else {
695
+ tv.tv_sec = tv.tv_usec = 0;
696
+ }
697
+ }
698
+
699
+ return tv;
700
+ }
701
+
702
+ /*******************************
703
+ EventMachine_t::_CleanupSockets
704
+ *******************************/
705
+
706
+ void EventMachine_t::_CleanupSockets()
707
+ {
708
+ // TODO, rip this out and only delete the descriptors we know have died,
709
+ // rather than traversing the whole list.
710
+ // Modified 05Jan08 per suggestions by Chris Heath. It's possible that
711
+ // an EventableDescriptor will have a descriptor value of -1. That will
712
+ // happen if EventableDescriptor::Close was called on it. In that case,
713
+ // don't call epoll_ctl to remove the socket's filters from the epoll set.
714
+ // According to the epoll docs, this happens automatically when the
715
+ // descriptor is closed anyway. This is different from the case where
716
+ // the socket has already been closed but the descriptor in the ED object
717
+ // hasn't yet been set to INVALID_SOCKET.
718
+ // In kqueue, closing a descriptor automatically removes its event filters.
719
+ int i, j;
720
+ int nSockets = Descriptors.size();
721
+ for (i=0, j=0; i < nSockets; i++) {
722
+ EventableDescriptor *ed = Descriptors[i];
723
+ assert (ed);
724
+ if (ed->ShouldDelete()) {
725
+ #ifdef HAVE_EPOLL
726
+ if (bEpoll) {
727
+ assert (epfd != -1);
728
+ if (ed->GetSocket() != INVALID_SOCKET) {
729
+ int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
730
+ // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
731
+ if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
732
+ char buf [200];
733
+ snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
734
+ throw std::runtime_error (buf);
735
+ }
736
+ }
737
+ ModifiedDescriptors.erase(ed);
738
+ }
739
+ #endif
740
+ delete ed;
741
+ }
742
+ else
743
+ Descriptors [j++] = ed;
744
+ }
745
+ while ((size_t)j < Descriptors.size())
746
+ Descriptors.pop_back();
747
+ }
748
+
680
749
  /*********************************
681
750
  EventMachine_t::_ModifyEpollEvent
682
751
  *********************************/
@@ -829,7 +898,7 @@ bool EventMachine_t::_RunSelectOnce()
829
898
  { // read and write the sockets
830
899
  //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
831
900
  //timeval tv = Quantum;
832
- SelectData.tv = Quantum;
901
+ SelectData.tv = _TimeTilNextEvent();
833
902
  int s = SelectData._Select();
834
903
  //rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0);
835
904
  //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
@@ -865,48 +934,54 @@ bool EventMachine_t::_RunSelectOnce()
865
934
  _ReadLoopBreaker();
866
935
  }
867
936
  else if (s < 0) {
868
- // select can fail on error in a handful of ways.
869
- // If this happens, then wait for a little while to avoid busy-looping.
870
- // If the error was EINTR, we probably caught SIGCHLD or something,
871
- // so keep the wait short.
872
- timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
873
- EmSelect (0, NULL, NULL, NULL, &tv);
937
+ switch (errno) {
938
+ case EBADF:
939
+ _CleanBadDescriptors();
940
+ break;
941
+ case EINVAL:
942
+ throw std::runtime_error ("Somehow EM passed an invalid nfds or invalid timeout to select(2), please report this!");
943
+ break;
944
+ default:
945
+ // select can fail on error in a handful of ways.
946
+ // If this happens, then wait for a little while to avoid busy-looping.
947
+ // If the error was EINTR, we probably caught SIGCHLD or something,
948
+ // so keep the wait short.
949
+ timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
950
+ EmSelect (0, NULL, NULL, NULL, &tv);
951
+ }
874
952
  }
875
953
  }
876
954
 
955
+ return true;
956
+ }
877
957
 
878
- { // dispatch heartbeats
879
- if (gCurrentLoopTime >= NextHeartbeatTime) {
880
- NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
958
+ void EventMachine_t::_CleanBadDescriptors()
959
+ {
960
+ size_t i;
881
961
 
882
- for (i=0; i < Descriptors.size(); i++) {
883
- EventableDescriptor *ed = Descriptors[i];
884
- assert (ed);
885
- ed->Heartbeat();
886
- }
887
- }
888
- }
962
+ for (i = 0; i < Descriptors.size(); i++) {
963
+ EventableDescriptor *ed = Descriptors[i];
964
+ if (ed->ShouldDelete())
965
+ continue;
889
966
 
890
- { // cleanup dying sockets
891
- // vector::pop_back works in constant time.
892
- int i, j;
893
- int nSockets = Descriptors.size();
894
- for (i=0, j=0; i < nSockets; i++) {
895
- EventableDescriptor *ed = Descriptors[i];
896
- assert (ed);
897
- if (ed->ShouldDelete())
898
- delete ed;
899
- else
900
- Descriptors [j++] = ed;
901
- }
902
- while ((size_t)j < Descriptors.size())
903
- Descriptors.pop_back();
967
+ int sd = ed->GetSocket();
904
968
 
905
- }
969
+ struct timeval tv;
970
+ tv.tv_sec = 0;
971
+ tv.tv_usec = 0;
906
972
 
907
- return true;
908
- }
973
+ fd_set fds;
974
+ FD_ZERO(&fds);
975
+ FD_SET(sd, &fds);
976
+
977
+ int ret = select(sd + 1, &fds, NULL, NULL, &tv);
909
978
 
979
+ if (ret == -1) {
980
+ if (errno == EBADF)
981
+ ed->ScheduleClose(false);
982
+ }
983
+ }
984
+ }
910
985
 
911
986
  /********************************
912
987
  EventMachine_t::_ReadLoopBreaker
@@ -921,7 +996,7 @@ void EventMachine_t::_ReadLoopBreaker()
921
996
  char buffer [1024];
922
997
  read (LoopBreakerReader, buffer, sizeof(buffer));
923
998
  if (EventCallback)
924
- (*EventCallback)(NULL, EM_LOOPBREAK_SIGNAL, "", 0);
999
+ (*EventCallback)(0, EM_LOOPBREAK_SIGNAL, "", 0);
925
1000
  }
926
1001
 
927
1002
 
@@ -939,13 +1014,13 @@ bool EventMachine_t::_RunTimers()
939
1014
  // one that hasn't expired yet.
940
1015
 
941
1016
  while (true) {
942
- multimap<Int64,Timer_t>::iterator i = Timers.begin();
1017
+ multimap<uint64_t,Timer_t>::iterator i = Timers.begin();
943
1018
  if (i == Timers.end())
944
1019
  break;
945
- if (i->first > gCurrentLoopTime)
1020
+ if (i->first > MyCurrentLoopTime)
946
1021
  break;
947
1022
  if (EventCallback)
948
- (*EventCallback) (NULL, EM_TIMER_FIRED, NULL, i->second.GetBinding());
1023
+ (*EventCallback) (0, EM_TIMER_FIRED, NULL, i->second.GetBinding());
949
1024
  Timers.erase (i);
950
1025
  }
951
1026
  return true;
@@ -964,28 +1039,31 @@ const unsigned long EventMachine_t::InstallOneshotTimer (int milliseconds)
964
1039
  // Don't use the global loop-time variable here, because we might
965
1040
  // get called before the main event machine is running.
966
1041
 
1042
+ // XXX This should be replaced with a call to _GetRealTime(), but I don't
1043
+ // understand if this is a bug or not? For OS_UNIX we multiply the argument
1044
+ // milliseconds by 1000, but for OS_WIN32 we do not? This needs to be sorted out.
967
1045
  #ifdef OS_UNIX
968
1046
  struct timeval tv;
969
1047
  gettimeofday (&tv, NULL);
970
- Int64 fire_at = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
971
- fire_at += ((Int64)milliseconds) * 1000LL;
1048
+ uint64_t fire_at = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)(tv.tv_usec));
1049
+ fire_at += ((uint64_t)milliseconds) * 1000LL;
972
1050
  #endif
973
1051
 
974
1052
  #ifdef OS_WIN32
975
1053
  unsigned tick = GetTickCount();
976
- if (tick < gLastTickCount)
977
- gTickCountTickover += 1;
978
- gLastTickCount = tick;
1054
+ if (tick < LastTickCount)
1055
+ TickCountTickover += 1;
1056
+ LastTickCount = tick;
979
1057
 
980
- Int64 fire_at = ((Int64)gTickCountTickover << 32) + (Int64)tick;
981
- fire_at += (Int64)milliseconds;
1058
+ uint64_t fire_at = ((uint64_t)TickCountTickover << 32) + (uint64_t)tick;
1059
+ fire_at += (uint64_t)milliseconds;
982
1060
  #endif
983
1061
 
984
1062
  Timer_t t;
985
1063
  #ifndef HAVE_MAKE_PAIR
986
- multimap<Int64,Timer_t>::iterator i = Timers.insert (multimap<Int64,Timer_t>::value_type (fire_at, t));
1064
+ multimap<uint64_t,Timer_t>::iterator i = Timers.insert (multimap<uint64_t,Timer_t>::value_type (fire_at, t));
987
1065
  #else
988
- multimap<Int64,Timer_t>::iterator i = Timers.insert (make_pair (fire_at, t));
1066
+ multimap<uint64_t,Timer_t>::iterator i = Timers.insert (make_pair (fire_at, t));
989
1067
  #endif
990
1068
  return i->second.GetBinding();
991
1069
  }
@@ -1030,8 +1108,11 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1030
1108
  bind_as = *bind_as_ptr; // copy because name2address points to a static
1031
1109
 
1032
1110
  int sd = socket (family, SOCK_STREAM, 0);
1033
- if (sd == INVALID_SOCKET)
1034
- throw std::runtime_error ("unable to create new socket");
1111
+ if (sd == INVALID_SOCKET) {
1112
+ char buf [200];
1113
+ snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno));
1114
+ throw std::runtime_error (buf);
1115
+ }
1035
1116
 
1036
1117
  /*
1037
1118
  sockaddr_in pin;
@@ -1064,7 +1145,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1064
1145
  // From here on, ALL error returns must close the socket.
1065
1146
  // Set the new socket nonblocking.
1066
1147
  if (!SetSocketNonblocking (sd)) {
1067
- closesocket (sd);
1148
+ close (sd);
1068
1149
  throw std::runtime_error ("unable to set socket as non-blocking");
1069
1150
  }
1070
1151
  // Disable slow-start (Nagle algorithm).
@@ -1077,16 +1158,16 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1077
1158
  int bind_to_size, bind_to_family;
1078
1159
  struct sockaddr *bind_to = name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size);
1079
1160
  if (!bind_to) {
1080
- closesocket (sd);
1161
+ close (sd);
1081
1162
  throw std::runtime_error ("invalid bind address");
1082
1163
  }
1083
1164
  if (bind (sd, bind_to, bind_to_size) < 0) {
1084
- closesocket (sd);
1165
+ close (sd);
1085
1166
  throw std::runtime_error ("couldn't bind to address");
1086
1167
  }
1087
1168
  }
1088
1169
 
1089
- unsigned long out = NULL;
1170
+ unsigned long out = 0;
1090
1171
 
1091
1172
  #ifdef OS_UNIX
1092
1173
  //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
@@ -1135,29 +1216,31 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1135
1216
  Add (cd);
1136
1217
  out = cd->GetBinding();
1137
1218
  }
1138
- else {
1139
- /* This could be connection refused or some such thing.
1140
- * We will come here on Linux if a localhost connection fails.
1141
- * Changed 16Jul06: Originally this branch was a no-op, and
1142
- * we'd drop down to the end of the method, close the socket,
1143
- * and return NULL, which would cause the caller to GET A
1144
- * FATAL EXCEPTION. Now we keep the socket around but schedule an
1145
- * immediate close on it, so the caller will get a close-event
1146
- * scheduled on it. This was only an issue for localhost connections
1147
- * to non-listening ports. We may eventually need to revise this
1148
- * revised behavior, in case it causes problems like making it hard
1149
- * for people to know that a failure occurred.
1150
- */
1151
- ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
1152
- if (!cd)
1153
- throw std::runtime_error ("no connection allocated");
1154
- cd->ScheduleClose (false);
1155
- Add (cd);
1156
- out = cd->GetBinding();
1157
- }
1158
1219
  }
1159
1220
  else {
1160
- // The error from connect was something other then EINPROGRESS.
1221
+ // The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
1222
+ // Fall through to the !out case below
1223
+ }
1224
+
1225
+ if (!out) {
1226
+ /* This could be connection refused or some such thing.
1227
+ * We will come here on Linux if a localhost connection fails.
1228
+ * Changed 16Jul06: Originally this branch was a no-op, and
1229
+ * we'd drop down to the end of the method, close the socket,
1230
+ * and return NULL, which would cause the caller to GET A
1231
+ * FATAL EXCEPTION. Now we keep the socket around but schedule an
1232
+ * immediate close on it, so the caller will get a close-event
1233
+ * scheduled on it. This was only an issue for localhost connections
1234
+ * to non-listening ports. We may eventually need to revise this
1235
+ * revised behavior, in case it causes problems like making it hard
1236
+ * for people to know that a failure occurred.
1237
+ */
1238
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
1239
+ if (!cd)
1240
+ throw std::runtime_error ("no connection allocated");
1241
+ cd->ScheduleClose (false);
1242
+ Add (cd);
1243
+ out = cd->GetBinding();
1161
1244
  }
1162
1245
  #endif
1163
1246
 
@@ -1190,7 +1273,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
1190
1273
  #endif
1191
1274
 
1192
1275
  if (!out)
1193
- closesocket (sd);
1276
+ close (sd);
1194
1277
  return out;
1195
1278
  }
1196
1279
 
@@ -1215,10 +1298,10 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
1215
1298
  // The whole rest of this function is only compiled on Unix systems.
1216
1299
  #ifdef OS_UNIX
1217
1300
 
1218
- unsigned long out = NULL;
1301
+ unsigned long out = 0;
1219
1302
 
1220
1303
  if (!server || !*server)
1221
- return NULL;
1304
+ return 0;
1222
1305
 
1223
1306
  sockaddr_un pun;
1224
1307
  memset (&pun, 0, sizeof(pun));
@@ -1234,19 +1317,19 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
1234
1317
 
1235
1318
  int fd = socket (AF_LOCAL, SOCK_STREAM, 0);
1236
1319
  if (fd == INVALID_SOCKET)
1237
- return NULL;
1320
+ return 0;
1238
1321
 
1239
1322
  // From here on, ALL error returns must close the socket.
1240
1323
  // NOTE: At this point, the socket is still a blocking socket.
1241
1324
  if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) {
1242
- closesocket (fd);
1243
- return NULL;
1325
+ close (fd);
1326
+ return 0;
1244
1327
  }
1245
1328
 
1246
1329
  // Set the newly-connected socket nonblocking.
1247
1330
  if (!SetSocketNonblocking (fd)) {
1248
- closesocket (fd);
1249
- return NULL;
1331
+ close (fd);
1332
+ return 0;
1250
1333
  }
1251
1334
 
1252
1335
  // Set up a connection descriptor and add it to the event-machine.
@@ -1261,7 +1344,7 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
1261
1344
  out = cd->GetBinding();
1262
1345
 
1263
1346
  if (!out)
1264
- closesocket (fd);
1347
+ close (fd);
1265
1348
 
1266
1349
  return out;
1267
1350
  #endif
@@ -1450,9 +1533,9 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
1450
1533
  int family, bind_size;
1451
1534
  struct sockaddr *bind_here = name2address (server, port, &family, &bind_size);
1452
1535
  if (!bind_here)
1453
- return NULL;
1536
+ return 0;
1454
1537
 
1455
- unsigned long output_binding = NULL;
1538
+ unsigned long output_binding = 0;
1456
1539
 
1457
1540
  //struct sockaddr_in sin;
1458
1541
 
@@ -1531,8 +1614,8 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
1531
1614
 
1532
1615
  fail:
1533
1616
  if (sd_accept != INVALID_SOCKET)
1534
- closesocket (sd_accept);
1535
- return NULL;
1617
+ close (sd_accept);
1618
+ return 0;
1536
1619
  }
1537
1620
 
1538
1621
 
@@ -1542,7 +1625,7 @@ EventMachine_t::OpenDatagramSocket
1542
1625
 
1543
1626
  const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int port)
1544
1627
  {
1545
- unsigned long output_binding = NULL;
1628
+ unsigned long output_binding = 0;
1546
1629
 
1547
1630
  int sd = socket (AF_INET, SOCK_DGRAM, 0);
1548
1631
  if (sd == INVALID_SOCKET)
@@ -1592,8 +1675,8 @@ const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int
1592
1675
 
1593
1676
  fail:
1594
1677
  if (sd != INVALID_SOCKET)
1595
- closesocket (sd);
1596
- return NULL;
1678
+ close (sd);
1679
+ return 0;
1597
1680
  }
1598
1681
 
1599
1682
 
@@ -1702,6 +1785,7 @@ void EventMachine_t::_AddNewDescriptors()
1702
1785
  */
1703
1786
  #endif
1704
1787
 
1788
+ QueueHeartbeat(ed);
1705
1789
  Descriptors.push_back (ed);
1706
1790
  }
1707
1791
  NewDescriptors.clear();
@@ -1770,7 +1854,7 @@ const unsigned long EventMachine_t::_OpenFileForWriting (const char *filename)
1770
1854
  */
1771
1855
 
1772
1856
  if (!filename || !*filename)
1773
- return NULL;
1857
+ return 0;
1774
1858
 
1775
1859
  int fd = open (filename, O_CREAT|O_TRUNC|O_WRONLY|O_NONBLOCK, 0644);
1776
1860
 
@@ -1802,7 +1886,7 @@ const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename
1802
1886
 
1803
1887
  // The whole rest of this function is only compiled on Unix systems.
1804
1888
  #ifdef OS_UNIX
1805
- unsigned long output_binding = NULL;
1889
+ unsigned long output_binding = 0;
1806
1890
 
1807
1891
  struct sockaddr_un s_sun;
1808
1892
 
@@ -1862,8 +1946,8 @@ const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename
1862
1946
 
1863
1947
  fail:
1864
1948
  if (sd_accept != INVALID_SOCKET)
1865
- closesocket (sd_accept);
1866
- return NULL;
1949
+ close (sd_accept);
1950
+ return 0;
1867
1951
  #endif // OS_UNIX
1868
1952
  }
1869
1953
 
@@ -1924,18 +2008,18 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
1924
2008
  #ifdef OS_UNIX
1925
2009
  // Make sure the incoming array of command strings is sane.
1926
2010
  if (!cmd_strings)
1927
- return NULL;
2011
+ return 0;
1928
2012
  int j;
1929
- for (j=0; j < 100 && cmd_strings[j]; j++)
2013
+ for (j=0; j < 2048 && cmd_strings[j]; j++)
1930
2014
  ;
1931
- if ((j==0) || (j==100))
1932
- return NULL;
2015
+ if ((j==0) || (j==2048))
2016
+ return 0;
1933
2017
 
1934
- unsigned long output_binding = NULL;
2018
+ unsigned long output_binding = 0;
1935
2019
 
1936
2020
  int sv[2];
1937
2021
  if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
1938
- return NULL;
2022
+ return 0;
1939
2023
  // from here, all early returns must close the pair of sockets.
1940
2024
 
1941
2025
  // Set the parent side of the socketpair nonblocking.
@@ -1945,7 +2029,7 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
1945
2029
  if (!SetSocketNonblocking (sv[0])) {
1946
2030
  close (sv[0]);
1947
2031
  close (sv[1]);
1948
- return NULL;
2032
+ return 0;
1949
2033
  }
1950
2034
 
1951
2035
  pid_t f = fork();
@@ -2005,7 +2089,7 @@ const unsigned long EventMachine_t::WatchPid (int pid)
2005
2089
  {
2006
2090
  #ifdef HAVE_KQUEUE
2007
2091
  if (!bKqueue)
2008
- throw std::runtime_error("must enable kqueue");
2092
+ throw std::runtime_error("must enable kqueue (EM.kqueue=true) for pid watching support");
2009
2093
 
2010
2094
  struct kevent event;
2011
2095
  int kqres;
@@ -2094,7 +2178,8 @@ const unsigned long EventMachine_t::WatchFile (const char *fpath)
2094
2178
  Add(inotify);
2095
2179
  }
2096
2180
 
2097
- wd = inotify_add_watch(inotify->GetSocket(), fpath, IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
2181
+ wd = inotify_add_watch(inotify->GetSocket(), fpath,
2182
+ IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_MOVE) ;
2098
2183
  if (wd == -1) {
2099
2184
  char errbuf[300];
2100
2185
  sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
@@ -2104,7 +2189,7 @@ const unsigned long EventMachine_t::WatchFile (const char *fpath)
2104
2189
 
2105
2190
  #ifdef HAVE_KQUEUE
2106
2191
  if (!bKqueue)
2107
- throw std::runtime_error("must enable kqueue");
2192
+ throw std::runtime_error("must enable kqueue (EM.kqueue=true) for file watching support");
2108
2193
 
2109
2194
  // With kqueue we have to open the file first and use the resulting fd to register for events
2110
2195
  wd = open(fpath, O_RDONLY);
@@ -2170,19 +2255,33 @@ EventMachine_t::_ReadInotify_Events
2170
2255
  void EventMachine_t::_ReadInotifyEvents()
2171
2256
  {
2172
2257
  #ifdef HAVE_INOTIFY
2173
- struct inotify_event event;
2258
+ char buffer[1024];
2174
2259
 
2175
2260
  assert(EventCallback);
2176
2261
 
2177
- while (read(inotify->GetSocket(), &event, INOTIFY_EVENT_SIZE) > 0) {
2178
- assert(event.len == 0);
2179
- if (event.mask & IN_MODIFY)
2180
- (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
2181
- if (event.mask & IN_MOVE_SELF)
2182
- (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
2183
- if (event.mask & IN_DELETE_SELF) {
2184
- (*EventCallback)(Files [event.wd]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
2185
- UnwatchFile ((int)event.wd);
2262
+ for (;;) {
2263
+ int returned = read(inotify->GetSocket(), buffer, sizeof(buffer));
2264
+ assert(!(returned == 0 || returned == -1 && errno == EINVAL));
2265
+ if (returned <= 0) {
2266
+ break;
2267
+ }
2268
+ int current = 0;
2269
+ while (current < returned) {
2270
+ struct inotify_event* event = (struct inotify_event*)(buffer+current);
2271
+ map<int, Bindable_t*>::const_iterator bindable = Files.find(event->wd);
2272
+ if (bindable != Files.end()) {
2273
+ if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE)){
2274
+ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "modified", 8);
2275
+ }
2276
+ if (event->mask & IN_MOVE_SELF){
2277
+ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "moved", 5);
2278
+ }
2279
+ if (event->mask & IN_DELETE_SELF) {
2280
+ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
2281
+ UnwatchFile ((int)event->wd);
2282
+ }
2283
+ }
2284
+ current += sizeof(struct inotify_event) + event->len;
2186
2285
  }
2187
2286
  }
2188
2287
  #endif