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.
- data/{docs/README → README} +21 -13
- data/Rakefile +14 -4
- data/docs/DEFERRABLES +0 -5
- data/docs/INSTALL +2 -4
- data/docs/LEGAL +1 -1
- data/docs/LIGHTWEIGHT_CONCURRENCY +0 -2
- data/docs/PURE_RUBY +0 -2
- data/docs/RELEASE_NOTES +0 -2
- data/docs/SMTP +0 -7
- data/docs/SPAWNED_PROCESSES +0 -4
- data/docs/TODO +0 -2
- data/eventmachine.gemspec +17 -8
- data/examples/ex_channel.rb +43 -0
- data/examples/ex_queue.rb +2 -0
- data/examples/helper.rb +2 -0
- data/ext/cmain.cpp +119 -20
- data/ext/cplusplus.cpp +15 -6
- data/ext/ed.cpp +303 -93
- data/ext/ed.h +49 -22
- data/ext/em.cpp +368 -42
- data/ext/em.h +43 -6
- data/ext/eventmachine.h +21 -8
- data/ext/eventmachine_cpp.h +1 -0
- data/ext/extconf.rb +4 -0
- data/ext/kb.cpp +1 -2
- data/ext/pipe.cpp +1 -3
- data/ext/project.h +21 -0
- data/ext/rubymain.cpp +232 -32
- data/ext/ssl.cpp +38 -1
- data/ext/ssl.h +5 -1
- data/java/src/com/rubyeventmachine/Application.java +7 -3
- data/java/src/com/rubyeventmachine/EmReactor.java +16 -1
- data/java/src/com/rubyeventmachine/tests/ConnectTest.java +25 -3
- data/lib/{protocols → em}/buftok.rb +16 -5
- data/lib/em/callback.rb +26 -0
- data/lib/em/channel.rb +57 -0
- data/lib/em/connection.rb +505 -0
- data/lib/em/deferrable.rb +144 -165
- data/lib/em/file_watch.rb +54 -0
- data/lib/em/future.rb +24 -25
- data/lib/em/messages.rb +1 -1
- data/lib/em/process_watch.rb +44 -0
- data/lib/em/processes.rb +58 -52
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +263 -0
- data/lib/em/protocols/httpclient2.rb +582 -0
- data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
- data/lib/em/protocols/linetext2.rb +160 -0
- data/lib/{protocols → em/protocols}/memcache.rb +37 -7
- data/lib/em/protocols/object_protocol.rb +39 -0
- data/lib/em/protocols/postgres3.rb +247 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +331 -0
- data/lib/em/protocols/smtpserver.rb +547 -0
- data/lib/em/protocols/stomp.rb +200 -0
- data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
- data/lib/em/protocols.rb +35 -0
- data/lib/em/queue.rb +61 -0
- data/lib/em/spawnable.rb +53 -56
- data/lib/em/streamer.rb +92 -74
- data/lib/em/timers.rb +55 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1008 -1298
- data/lib/evma.rb +1 -1
- data/lib/jeventmachine.rb +106 -101
- data/lib/pr_eventmachine.rb +47 -36
- data/tasks/project.rake +2 -1
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/test_attach.rb +18 -0
- data/tests/test_basic.rb +108 -54
- data/tests/test_channel.rb +63 -0
- data/tests/test_connection_count.rb +2 -2
- data/tests/test_epoll.rb +109 -110
- data/tests/test_errors.rb +36 -36
- data/tests/test_exc.rb +22 -25
- data/tests/test_file_watch.rb +49 -0
- data/tests/test_futures.rb +77 -93
- data/tests/test_hc.rb +2 -2
- data/tests/test_httpclient.rb +55 -52
- data/tests/test_httpclient2.rb +110 -112
- data/tests/test_inactivity_timeout.rb +30 -0
- data/tests/test_kb.rb +8 -9
- data/tests/test_ltp2.rb +274 -277
- data/tests/test_next_tick.rb +91 -65
- data/tests/test_object_protocol.rb +37 -0
- data/tests/test_process_watch.rb +48 -0
- data/tests/test_processes.rb +56 -23
- data/tests/test_proxy_connection.rb +92 -0
- data/tests/test_pure.rb +1 -5
- data/tests/test_queue.rb +44 -0
- data/tests/test_running.rb +9 -14
- data/tests/test_sasl.rb +32 -34
- data/tests/test_send_file.rb +175 -176
- data/tests/test_servers.rb +37 -41
- data/tests/test_smtpserver.rb +47 -55
- data/tests/test_spawn.rb +284 -291
- data/tests/test_ssl_args.rb +1 -1
- data/tests/test_ssl_methods.rb +1 -1
- data/tests/test_ssl_verify.rb +82 -0
- data/tests/test_timers.rb +81 -88
- data/tests/test_ud.rb +0 -7
- data/tests/testem.rb +1 -1
- metadata +53 -37
- data/lib/em/eventable.rb +0 -39
- data/lib/eventmachine_version.rb +0 -31
- data/lib/protocols/header_and_content.rb +0 -129
- data/lib/protocols/httpcli2.rb +0 -803
- data/lib/protocols/httpclient.rb +0 -270
- data/lib/protocols/linetext2.rb +0 -161
- data/lib/protocols/postgres.rb +0 -261
- data/lib/protocols/saslauth.rb +0 -179
- data/lib/protocols/smtpclient.rb +0 -308
- data/lib/protocols/smtpserver.rb +0 -556
- data/lib/protocols/stomp.rb +0 -153
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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*)
|
|
478
|
+
EventableDescriptor *ed = (EventableDescriptor*) epoll_events[i].data.ptr;
|
|
452
479
|
|
|
453
|
-
if (
|
|
480
|
+
if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
|
|
454
481
|
ed->ScheduleClose (false);
|
|
455
|
-
if (
|
|
482
|
+
if (epoll_events[i].events & EPOLLIN)
|
|
456
483
|
ed->Read();
|
|
457
|
-
if (
|
|
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,
|
|
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
|
-
|
|
569
|
-
|
|
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
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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 >
|
|
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
|
|
979
|
-
if (!
|
|
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
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
|