eventmachine-eventmachine 0.12.7 → 0.12.8

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 (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/header_and_content.rb +138 -0
  45. data/lib/em/protocols/httpclient.rb +263 -0
  46. data/lib/em/protocols/httpclient2.rb +582 -0
  47. data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
  48. data/lib/em/protocols/linetext2.rb +160 -0
  49. data/lib/{protocols → em/protocols}/memcache.rb +37 -7
  50. data/lib/em/protocols/object_protocol.rb +39 -0
  51. data/lib/em/protocols/postgres3.rb +247 -0
  52. data/lib/em/protocols/saslauth.rb +175 -0
  53. data/lib/em/protocols/smtpclient.rb +331 -0
  54. data/lib/em/protocols/smtpserver.rb +547 -0
  55. data/lib/em/protocols/stomp.rb +200 -0
  56. data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
  57. data/lib/em/protocols.rb +35 -0
  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 +53 -37
  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/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
 
data/ext/em.h CHANGED
@@ -57,9 +57,10 @@ typedef long long Int64;
57
57
  typedef __int64 Int64;
58
58
  #endif
59
59
 
60
- extern time_t gCurrentLoopTime;
60
+ extern Int64 gCurrentLoopTime;
61
61
 
62
62
  class EventableDescriptor;
63
+ class InotifyDescriptor;
63
64
 
64
65
 
65
66
  /********************
@@ -80,9 +81,8 @@ class EventMachine_t
80
81
  void ScheduleHalt();
81
82
  void SignalLoopBreaker();
82
83
  const char *InstallOneshotTimer (int);
83
- const char *ConnectToServer (const char *, int);
84
+ const char *ConnectToServer (const char *, int, const char *, int);
84
85
  const char *ConnectToUnixServer (const char *);
85
- const char *AttachFD (int, bool, bool);
86
86
 
87
87
  const char *CreateTcpServer (const char *, int);
88
88
  const char *OpenDatagramSocket (const char *, int);
@@ -94,7 +94,10 @@ class EventMachine_t
94
94
 
95
95
  void Add (EventableDescriptor*);
96
96
  void Modify (EventableDescriptor*);
97
+
98
+ const char *AttachFD (int, bool, bool);
97
99
  int DetachFD (EventableDescriptor*);
100
+
98
101
  void ArmKqueueWriter (EventableDescriptor*);
99
102
  void ArmKqueueReader (EventableDescriptor*);
100
103
 
@@ -106,15 +109,37 @@ class EventMachine_t
106
109
  int SubprocessExitStatus;
107
110
 
108
111
  int GetConnectionCount();
112
+ float GetHeartbeatInterval();
113
+ int SetHeartbeatInterval(float);
114
+
115
+ const char *WatchFile (const char*);
116
+ void UnwatchFile (int);
117
+ void UnwatchFile (const char*);
118
+
119
+ #ifdef HAVE_KQUEUE
120
+ void _HandleKqueueFileEvent (struct kevent*);
121
+ void _RegisterKqueueFileEvent(int);
122
+ #endif
123
+
124
+ const char *WatchPid (int);
125
+ void UnwatchPid (int);
126
+ void UnwatchPid (const char *);
127
+
128
+ #ifdef HAVE_KQUEUE
129
+ void _HandleKqueuePidEvent (struct kevent*);
130
+ #endif
109
131
 
110
132
  // Temporary:
111
133
  void _UseEpoll();
112
134
  void _UseKqueue();
113
135
 
136
+ bool UsingKqueue() { return bKqueue; }
137
+ bool UsingEpoll() { return bEpoll; }
114
138
 
115
139
  private:
116
140
  bool _RunOnce();
117
141
  bool _RunTimers();
142
+ void _UpdateTime();
118
143
  void _AddNewDescriptors();
119
144
  void _ModifyDescriptors();
120
145
  void _InitializeLoopBreaker();
@@ -127,23 +152,27 @@ class EventMachine_t
127
152
 
128
153
  public:
129
154
  void _ReadLoopBreaker();
155
+ void _ReadInotifyEvents();
130
156
 
131
157
  private:
132
158
  enum {
133
- HeartbeatInterval = 2,
134
- MaxEpollDescriptors = 64*1024
159
+ MaxEpollDescriptors = 64*1024,
160
+ MaxEvents = 4096
135
161
  };
162
+ int HeartbeatInterval;
136
163
  void (*EventCallback)(const char*, int, const char*, int);
137
164
 
138
165
  class Timer_t: public Bindable_t {
139
166
  };
140
167
 
141
168
  multimap<Int64, Timer_t> Timers;
169
+ map<int, Bindable_t*> Files;
170
+ map<int, Bindable_t*> Pids;
142
171
  vector<EventableDescriptor*> Descriptors;
143
172
  vector<EventableDescriptor*> NewDescriptors;
144
173
  set<EventableDescriptor*> ModifiedDescriptors;
145
174
 
146
- time_t NextHeartbeatTime;
175
+ Int64 NextHeartbeatTime;
147
176
 
148
177
  int LoopBreakerReader;
149
178
  int LoopBreakerWriter;
@@ -156,9 +185,17 @@ class EventMachine_t
156
185
  private:
157
186
  bool bEpoll;
158
187
  int epfd; // Epoll file-descriptor
188
+ #ifdef HAVE_EPOLL
189
+ struct epoll_event epoll_events [MaxEvents];
190
+ #endif
159
191
 
160
192
  bool bKqueue;
161
193
  int kqfd; // Kqueue file-descriptor
194
+ #ifdef HAVE_KQUEUE
195
+ struct kevent Karray [MaxEvents];
196
+ #endif
197
+
198
+ InotifyDescriptor *inotify; // pollable descriptor for our inotify instance
162
199
  };
163
200
 
164
201