eventmachine 0.12.10 → 1.0.0.beta.1

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