eventmachine-maglev- 0.12.10 → 1.0.0.beta.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.yardopts +7 -0
- data/Gemfile +3 -0
- data/README.md +109 -0
- data/Rakefile +14 -368
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/old/DEFERRABLES +246 -0
- data/docs/{KEYBOARD → old/KEYBOARD} +15 -11
- data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
- data/docs/old/SMTP +4 -0
- data/docs/old/SPAWNED_PROCESSES +148 -0
- data/eventmachine.gemspec +20 -26
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
- data/examples/old/ex_tick_loop_array.rb +15 -0
- data/examples/old/ex_tick_loop_counter.rb +32 -0
- data/ext/binder.cpp +0 -1
- data/ext/cmain.cpp +40 -29
- data/ext/ed.cpp +189 -134
- data/ext/ed.h +34 -40
- data/ext/em.cpp +388 -340
- data/ext/em.h +29 -32
- data/ext/eventmachine.h +7 -6
- data/ext/extconf.rb +57 -48
- data/ext/fastfilereader/extconf.rb +5 -3
- data/ext/fastfilereader/mapper.cpp +1 -1
- data/ext/fastfilereader/rubymain.cpp +0 -1
- data/ext/kb.cpp +1 -3
- data/ext/pipe.cpp +9 -11
- data/ext/project.h +12 -8
- data/ext/rubymain.cpp +158 -112
- data/java/src/com/rubyeventmachine/EmReactor.java +3 -2
- data/lib/em/buftok.rb +35 -63
- data/lib/em/callback.rb +43 -11
- data/lib/em/channel.rb +22 -15
- data/lib/em/completion.rb +303 -0
- data/lib/em/connection.rb +341 -208
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/deferrable.rb +20 -2
- data/lib/em/file_watch.rb +37 -18
- data/lib/em/iterator.rb +270 -0
- data/lib/em/pool.rb +146 -0
- data/lib/em/process_watch.rb +5 -4
- data/lib/em/processes.rb +8 -4
- data/lib/em/protocols/httpclient.rb +27 -11
- data/lib/em/protocols/httpclient2.rb +15 -5
- data/lib/em/protocols/line_protocol.rb +29 -0
- data/lib/em/protocols/memcache.rb +17 -9
- data/lib/em/protocols/object_protocol.rb +2 -1
- data/lib/em/protocols/postgres3.rb +2 -1
- data/lib/em/protocols/smtpclient.rb +19 -11
- data/lib/em/protocols/smtpserver.rb +101 -8
- data/lib/em/protocols/stomp.rb +9 -7
- data/lib/em/protocols/tcptest.rb +3 -2
- data/lib/em/protocols.rb +1 -1
- data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +188 -205
- data/lib/em/queue.rb +23 -13
- data/lib/em/resolver.rb +192 -0
- data/lib/em/spawnable.rb +9 -10
- data/lib/em/streamer.rb +34 -46
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +8 -3
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +582 -686
- data/lib/jeventmachine.rb +25 -3
- data/tasks/package.rake +98 -0
- data/tasks/test.rake +8 -0
- data/tests/em_test_helper.rb +64 -0
- data/tests/test_attach.rb +56 -56
- data/tests/test_basic.rb +111 -168
- data/tests/test_channel.rb +5 -6
- data/tests/test_completion.rb +177 -0
- data/tests/test_connection_count.rb +1 -3
- data/tests/test_defer.rb +3 -32
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_epoll.rb +27 -57
- data/tests/test_error_handler.rb +10 -7
- data/tests/test_exc.rb +6 -33
- data/tests/test_file_watch.rb +51 -35
- data/tests/test_futures.rb +10 -38
- data/tests/test_get_sock_opt.rb +27 -20
- data/tests/test_handler_check.rb +1 -3
- data/tests/test_hc.rb +49 -112
- data/tests/test_httpclient.rb +34 -62
- data/tests/test_httpclient2.rb +14 -39
- data/tests/test_inactivity_timeout.rb +44 -40
- data/tests/test_kb.rb +26 -52
- data/tests/test_ltp.rb +27 -71
- data/tests/test_ltp2.rb +1 -30
- data/tests/test_next_tick.rb +2 -31
- data/tests/test_object_protocol.rb +8 -9
- data/tests/test_pause.rb +45 -37
- data/tests/test_pending_connect_timeout.rb +42 -38
- data/tests/test_pool.rb +128 -0
- data/tests/test_process_watch.rb +37 -37
- data/tests/test_processes.rb +92 -110
- data/tests/test_proxy_connection.rb +137 -61
- data/tests/test_pure.rb +30 -67
- data/tests/test_queue.rb +10 -4
- data/tests/test_resolver.rb +55 -0
- data/tests/test_running.rb +1 -29
- data/tests/test_sasl.rb +8 -33
- data/tests/test_send_file.rb +163 -188
- data/tests/test_servers.rb +12 -55
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_smtpclient.rb +1 -29
- data/tests/test_smtpserver.rb +1 -29
- data/tests/test_spawn.rb +2 -31
- data/tests/test_ssl_args.rb +9 -10
- data/tests/test_ssl_methods.rb +1 -3
- data/tests/test_ssl_verify.rb +63 -63
- data/tests/test_threaded_resource.rb +53 -0
- data/tests/test_tick_loop.rb +59 -0
- data/tests/test_timers.rb +52 -91
- data/tests/test_ud.rb +1 -29
- data/tests/test_unbind_reason.rb +31 -0
- metadata +113 -70
- data/README +0 -82
- data/docs/DEFERRABLES +0 -133
- data/docs/LIGHTWEIGHT_CONCURRENCY +0 -70
- data/docs/SMTP +0 -2
- data/docs/SPAWNED_PROCESSES +0 -89
- data/ext/cplusplus.cpp +0 -202
- data/ext/emwin.cpp +0 -300
- data/ext/emwin.h +0 -94
- data/ext/epoll.cpp +0 -26
- data/ext/epoll.h +0 -25
- data/ext/eventmachine_cpp.h +0 -96
- data/ext/files.cpp +0 -94
- data/ext/files.h +0 -65
- data/ext/sigs.cpp +0 -89
- data/ext/sigs.h +0 -32
- data/java/src/com/rubyeventmachine/application/Application.java +0 -194
- data/java/src/com/rubyeventmachine/application/Connection.java +0 -74
- data/java/src/com/rubyeventmachine/application/ConnectionFactory.java +0 -37
- data/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +0 -46
- data/java/src/com/rubyeventmachine/application/PeriodicTimer.java +0 -38
- data/java/src/com/rubyeventmachine/application/Timer.java +0 -54
- data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +0 -109
- data/java/src/com/rubyeventmachine/tests/ConnectTest.java +0 -148
- data/java/src/com/rubyeventmachine/tests/EMTest.java +0 -80
- data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +0 -53
- data/java/src/com/rubyeventmachine/tests/TestServers.java +0 -75
- data/java/src/com/rubyeventmachine/tests/TestTimers.java +0 -90
- data/lib/evma/callback.rb +0 -32
- data/lib/evma/container.rb +0 -75
- data/lib/evma/factory.rb +0 -77
- data/lib/evma/protocol.rb +0 -87
- data/lib/evma/reactor.rb +0 -48
- data/lib/evma.rb +0 -32
- data/setup.rb +0 -1585
- data/tests/test_errors.rb +0 -82
- data/tests/testem.rb +0 -31
- data/web/whatis +0 -7
- /data/{docs/GNU → GNU} +0 -0
- /data/{docs/COPYING → LICENSE} +0 -0
- /data/docs/{ChangeLog → old/ChangeLog} +0 -0
- /data/docs/{EPOLL → old/EPOLL} +0 -0
- /data/docs/{INSTALL → old/INSTALL} +0 -0
- /data/docs/{LEGAL → old/LEGAL} +0 -0
- /data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
- /data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
- /data/docs/{TODO → old/TODO} +0 -0
- /data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
- /data/examples/{helper.rb → old/helper.rb} +0 -0
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 =
|
28
|
+
static unsigned int MaxOutstandingTimers = 100000;
|
41
29
|
|
42
30
|
|
43
31
|
/* Internal helper to convert strings to internet addresses. IPv6-aware.
|
@@ -79,12 +67,13 @@ void EventMachine_t::SetMaxTimerCount (int count)
|
|
79
67
|
EventMachine_t::EventMachine_t
|
80
68
|
******************************/
|
81
69
|
|
82
|
-
EventMachine_t::EventMachine_t (
|
70
|
+
EventMachine_t::EventMachine_t (EMCallback event_callback):
|
83
71
|
HeartbeatInterval(2000000),
|
84
72
|
EventCallback (event_callback),
|
85
73
|
NextHeartbeatTime (0),
|
86
74
|
LoopBreakerReader (-1),
|
87
75
|
LoopBreakerWriter (-1),
|
76
|
+
bTerminateSignalReceived (false),
|
88
77
|
bEpoll (false),
|
89
78
|
epfd (-1),
|
90
79
|
bKqueue (false),
|
@@ -95,7 +84,6 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const unsigned long, int,
|
|
95
84
|
Quantum.tv_sec = 0;
|
96
85
|
Quantum.tv_usec = 90000;
|
97
86
|
|
98
|
-
gTerminateSignalReceived = false;
|
99
87
|
// Make sure the current loop time is sane, in case we do any initializations of
|
100
88
|
// objects before we start running.
|
101
89
|
_UpdateTime();
|
@@ -197,7 +185,7 @@ void EventMachine_t::ScheduleHalt()
|
|
197
185
|
* We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens?
|
198
186
|
* The answer is to call evma_stop_machine, which calls here, from a SIGINT handler.
|
199
187
|
*/
|
200
|
-
|
188
|
+
bTerminateSignalReceived = true;
|
201
189
|
}
|
202
190
|
|
203
191
|
|
@@ -316,10 +304,14 @@ void EventMachine_t::_InitializeLoopBreaker()
|
|
316
304
|
#ifdef OS_UNIX
|
317
305
|
int fd[2];
|
318
306
|
if (pipe (fd))
|
319
|
-
throw std::runtime_error (
|
307
|
+
throw std::runtime_error (strerror(errno));
|
320
308
|
|
321
309
|
LoopBreakerWriter = fd[1];
|
322
310
|
LoopBreakerReader = fd[0];
|
311
|
+
|
312
|
+
/* 16Jan11: Make sure the pipe is non-blocking, so more than 65k loopbreaks
|
313
|
+
* in one tick do not fill up the pipe and block the process on write() */
|
314
|
+
SetSocketNonblocking (LoopBreakerWriter);
|
323
315
|
#endif
|
324
316
|
|
325
317
|
#ifdef OS_WIN32
|
@@ -353,21 +345,87 @@ EventMachine_t::_UpdateTime
|
|
353
345
|
|
354
346
|
void EventMachine_t::_UpdateTime()
|
355
347
|
{
|
348
|
+
MyCurrentLoopTime = GetRealTime();
|
349
|
+
}
|
350
|
+
|
351
|
+
/***************************
|
352
|
+
EventMachine_t::GetRealTime
|
353
|
+
***************************/
|
354
|
+
|
355
|
+
uint64_t EventMachine_t::GetRealTime()
|
356
|
+
{
|
357
|
+
uint64_t current_time;
|
358
|
+
|
356
359
|
#if defined(OS_UNIX)
|
357
360
|
struct timeval tv;
|
358
361
|
gettimeofday (&tv, NULL);
|
359
|
-
|
362
|
+
current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)(tv.tv_usec));
|
360
363
|
|
361
364
|
#elif defined(OS_WIN32)
|
362
365
|
unsigned tick = GetTickCount();
|
363
|
-
if (tick <
|
364
|
-
|
365
|
-
|
366
|
-
|
366
|
+
if (tick < LastTickCount)
|
367
|
+
TickCountTickover += 1;
|
368
|
+
LastTickCount = tick;
|
369
|
+
current_time = ((uint64_t)TickCountTickover << 32) + (uint64_t)tick;
|
370
|
+
current_time *= 1000; // convert to microseconds
|
367
371
|
|
368
372
|
#else
|
369
|
-
|
373
|
+
current_time = (uint64_t)time(NULL) * 1000000LL;
|
370
374
|
#endif
|
375
|
+
|
376
|
+
return current_time;
|
377
|
+
}
|
378
|
+
|
379
|
+
/***********************************
|
380
|
+
EventMachine_t::_DispatchHeartbeats
|
381
|
+
***********************************/
|
382
|
+
|
383
|
+
void EventMachine_t::_DispatchHeartbeats()
|
384
|
+
{
|
385
|
+
while (true) {
|
386
|
+
multimap<uint64_t,EventableDescriptor*>::iterator i = Heartbeats.begin();
|
387
|
+
if (i == Heartbeats.end())
|
388
|
+
break;
|
389
|
+
if (i->first > MyCurrentLoopTime)
|
390
|
+
break;
|
391
|
+
EventableDescriptor *ed = i->second;
|
392
|
+
ed->Heartbeat();
|
393
|
+
QueueHeartbeat(ed);
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
397
|
+
/******************************
|
398
|
+
EventMachine_t::QueueHeartbeat
|
399
|
+
******************************/
|
400
|
+
|
401
|
+
void EventMachine_t::QueueHeartbeat(EventableDescriptor *ed)
|
402
|
+
{
|
403
|
+
uint64_t heartbeat = ed->GetNextHeartbeat();
|
404
|
+
|
405
|
+
if (heartbeat) {
|
406
|
+
#ifndef HAVE_MAKE_PAIR
|
407
|
+
Heartbeats.insert (multimap<uint64_t,EventableDescriptor*>::value_type (heartbeat, ed));
|
408
|
+
#else
|
409
|
+
Heartbeats.insert (make_pair (heartbeat, ed));
|
410
|
+
#endif
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
/******************************
|
415
|
+
EventMachine_t::ClearHeartbeat
|
416
|
+
******************************/
|
417
|
+
|
418
|
+
void EventMachine_t::ClearHeartbeat(uint64_t key, EventableDescriptor* ed)
|
419
|
+
{
|
420
|
+
multimap<uint64_t,EventableDescriptor*>::iterator it;
|
421
|
+
pair<multimap<uint64_t,EventableDescriptor*>::iterator,multimap<uint64_t,EventableDescriptor*>::iterator> ret;
|
422
|
+
ret = Heartbeats.equal_range (key);
|
423
|
+
for (it = ret.first; it != ret.second; ++it) {
|
424
|
+
if (it->second == ed) {
|
425
|
+
Heartbeats.erase (it);
|
426
|
+
break;
|
427
|
+
}
|
428
|
+
}
|
371
429
|
}
|
372
430
|
|
373
431
|
/*******************
|
@@ -376,10 +434,6 @@ EventMachine_t::Run
|
|
376
434
|
|
377
435
|
void EventMachine_t::Run()
|
378
436
|
{
|
379
|
-
#ifdef OS_WIN32
|
380
|
-
HookControlC (true);
|
381
|
-
#endif
|
382
|
-
|
383
437
|
#ifdef HAVE_EPOLL
|
384
438
|
if (bEpoll) {
|
385
439
|
epfd = epoll_create (MaxEpollDescriptors);
|
@@ -419,8 +473,7 @@ void EventMachine_t::Run()
|
|
419
473
|
|
420
474
|
while (true) {
|
421
475
|
_UpdateTime();
|
422
|
-
|
423
|
-
break;
|
476
|
+
_RunTimers();
|
424
477
|
|
425
478
|
/* _Add must precede _Modify because the same descriptor might
|
426
479
|
* be on both lists during the same pass through the machine,
|
@@ -431,13 +484,9 @@ void EventMachine_t::Run()
|
|
431
484
|
|
432
485
|
if (!_RunOnce())
|
433
486
|
break;
|
434
|
-
if (
|
487
|
+
if (bTerminateSignalReceived)
|
435
488
|
break;
|
436
489
|
}
|
437
|
-
|
438
|
-
#ifdef OS_WIN32
|
439
|
-
HookControlC (false);
|
440
|
-
#endif
|
441
490
|
}
|
442
491
|
|
443
492
|
|
@@ -447,12 +496,16 @@ EventMachine_t::_RunOnce
|
|
447
496
|
|
448
497
|
bool EventMachine_t::_RunOnce()
|
449
498
|
{
|
499
|
+
bool ret;
|
450
500
|
if (bEpoll)
|
451
|
-
|
501
|
+
ret = _RunEpollOnce();
|
452
502
|
else if (bKqueue)
|
453
|
-
|
503
|
+
ret = _RunKqueueOnce();
|
454
504
|
else
|
455
|
-
|
505
|
+
ret = _RunSelectOnce();
|
506
|
+
_DispatchHeartbeats();
|
507
|
+
_CleanupSockets();
|
508
|
+
return ret;
|
456
509
|
}
|
457
510
|
|
458
511
|
|
@@ -467,12 +520,31 @@ bool EventMachine_t::_RunEpollOnce()
|
|
467
520
|
assert (epfd != -1);
|
468
521
|
int s;
|
469
522
|
|
523
|
+
timeval tv = _TimeTilNextEvent();
|
524
|
+
|
470
525
|
#if defined(BUILD_FOR_RUBY) && !defined(BUILD_FOR_MAGLEV)
|
526
|
+
int ret = 0;
|
527
|
+
fd_set fdreads;
|
528
|
+
|
529
|
+
FD_ZERO(&fdreads);
|
530
|
+
FD_SET(epfd, &fdreads);
|
531
|
+
|
532
|
+
if ((ret = rb_thread_select(epfd + 1, &fdreads, NULL, NULL, &tv)) < 1) {
|
533
|
+
if (ret == -1) {
|
534
|
+
assert(errno != EINVAL);
|
535
|
+
assert(errno != EBADF);
|
536
|
+
}
|
537
|
+
return true;
|
538
|
+
}
|
539
|
+
|
471
540
|
TRAP_BEG;
|
472
|
-
|
473
|
-
s = epoll_wait (epfd, epoll_events, MaxEvents, 50);
|
474
|
-
#if defined(BUILD_FOR_RUBY) && !defined(BUILD_FOR_MAGLEV)
|
541
|
+
s = epoll_wait (epfd, epoll_events, MaxEvents, 0);
|
475
542
|
TRAP_END;
|
543
|
+
#else
|
544
|
+
int duration = 0;
|
545
|
+
duration = duration + (tv.tv_sec * 1000);
|
546
|
+
duration = duration + (tv.tv_usec / 1000);
|
547
|
+
s = epoll_wait (epfd, epoll_events, MaxEvents, duration);
|
476
548
|
#endif
|
477
549
|
|
478
550
|
if (s > 0) {
|
@@ -501,72 +573,6 @@ bool EventMachine_t::_RunEpollOnce()
|
|
501
573
|
EmSelect (0, NULL, NULL, NULL, &tv);
|
502
574
|
}
|
503
575
|
|
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
|
-
#if defined(BUILD_FOR_RUBY) && !defined(BUILD_FOR_MAGLEV)
|
565
|
-
if (!rb_thread_alone()) {
|
566
|
-
rb_thread_schedule();
|
567
|
-
}
|
568
|
-
#endif
|
569
|
-
|
570
576
|
return true;
|
571
577
|
#else
|
572
578
|
throw std::runtime_error ("epoll is not implemented on this platform");
|
@@ -582,15 +588,35 @@ bool EventMachine_t::_RunKqueueOnce()
|
|
582
588
|
{
|
583
589
|
#ifdef HAVE_KQUEUE
|
584
590
|
assert (kqfd != -1);
|
585
|
-
struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region
|
586
|
-
|
587
591
|
int k;
|
592
|
+
|
593
|
+
timeval tv = _TimeTilNextEvent();
|
594
|
+
|
595
|
+
struct timespec ts;
|
596
|
+
ts.tv_sec = tv.tv_sec;
|
597
|
+
ts.tv_nsec = tv.tv_usec * 1000;
|
598
|
+
|
588
599
|
#if defined(BUILD_FOR_RUBY) && !defined(BUILD_FOR_MAGLEV)
|
600
|
+
int ret = 0;
|
601
|
+
fd_set fdreads;
|
602
|
+
|
603
|
+
FD_ZERO(&fdreads);
|
604
|
+
FD_SET(kqfd, &fdreads);
|
605
|
+
|
606
|
+
if ((ret = rb_thread_select(kqfd + 1, &fdreads, NULL, NULL, &tv)) < 1) {
|
607
|
+
if (ret == -1) {
|
608
|
+
assert(errno != EINVAL);
|
609
|
+
assert(errno != EBADF);
|
610
|
+
}
|
611
|
+
return true;
|
612
|
+
}
|
613
|
+
|
589
614
|
TRAP_BEG;
|
590
|
-
|
615
|
+
ts.tv_sec = ts.tv_nsec = 0;
|
591
616
|
k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
|
592
|
-
#if defined(BUILD_FOR_RUBY) && !defined(BUILD_FOR_MAGLEV)
|
593
617
|
TRAP_END;
|
618
|
+
#else
|
619
|
+
k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
|
594
620
|
#endif
|
595
621
|
|
596
622
|
struct kevent *ke = Karray;
|
@@ -627,42 +653,6 @@ bool EventMachine_t::_RunKqueueOnce()
|
|
627
653
|
++ke;
|
628
654
|
}
|
629
655
|
|
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
656
|
// TODO, replace this with rb_thread_blocking_region for 1.9 builds.
|
667
657
|
#if defined(BUILD_FOR_RUBY) && !defined(BUILD_FOR_MAGLEV)
|
668
658
|
if (!rb_thread_alone()) {
|
@@ -677,6 +667,93 @@ bool EventMachine_t::_RunKqueueOnce()
|
|
677
667
|
}
|
678
668
|
|
679
669
|
|
670
|
+
/*********************************
|
671
|
+
EventMachine_t::_TimeTilNextEvent
|
672
|
+
*********************************/
|
673
|
+
|
674
|
+
timeval EventMachine_t::_TimeTilNextEvent()
|
675
|
+
{
|
676
|
+
uint64_t next_event = 0;
|
677
|
+
|
678
|
+
if (!Heartbeats.empty()) {
|
679
|
+
multimap<uint64_t,EventableDescriptor*>::iterator heartbeats = Heartbeats.begin();
|
680
|
+
next_event = heartbeats->first;
|
681
|
+
}
|
682
|
+
|
683
|
+
if (!Timers.empty()) {
|
684
|
+
multimap<uint64_t,Timer_t>::iterator timers = Timers.begin();
|
685
|
+
if (next_event == 0 || timers->first < next_event)
|
686
|
+
next_event = timers->first;
|
687
|
+
}
|
688
|
+
|
689
|
+
if (!NewDescriptors.empty() || !ModifiedDescriptors.empty()) {
|
690
|
+
next_event = MyCurrentLoopTime;
|
691
|
+
}
|
692
|
+
|
693
|
+
timeval tv;
|
694
|
+
|
695
|
+
if (next_event == 0) {
|
696
|
+
tv = Quantum;
|
697
|
+
} else {
|
698
|
+
if (next_event > MyCurrentLoopTime) {
|
699
|
+
uint64_t duration = next_event - MyCurrentLoopTime;
|
700
|
+
tv.tv_sec = duration / 1000000;
|
701
|
+
tv.tv_usec = duration % 1000000;
|
702
|
+
} else {
|
703
|
+
tv.tv_sec = tv.tv_usec = 0;
|
704
|
+
}
|
705
|
+
}
|
706
|
+
|
707
|
+
return tv;
|
708
|
+
}
|
709
|
+
|
710
|
+
/*******************************
|
711
|
+
EventMachine_t::_CleanupSockets
|
712
|
+
*******************************/
|
713
|
+
|
714
|
+
void EventMachine_t::_CleanupSockets()
|
715
|
+
{
|
716
|
+
// TODO, rip this out and only delete the descriptors we know have died,
|
717
|
+
// rather than traversing the whole list.
|
718
|
+
// Modified 05Jan08 per suggestions by Chris Heath. It's possible that
|
719
|
+
// an EventableDescriptor will have a descriptor value of -1. That will
|
720
|
+
// happen if EventableDescriptor::Close was called on it. In that case,
|
721
|
+
// don't call epoll_ctl to remove the socket's filters from the epoll set.
|
722
|
+
// According to the epoll docs, this happens automatically when the
|
723
|
+
// descriptor is closed anyway. This is different from the case where
|
724
|
+
// the socket has already been closed but the descriptor in the ED object
|
725
|
+
// hasn't yet been set to INVALID_SOCKET.
|
726
|
+
// In kqueue, closing a descriptor automatically removes its event filters.
|
727
|
+
int i, j;
|
728
|
+
int nSockets = Descriptors.size();
|
729
|
+
for (i=0, j=0; i < nSockets; i++) {
|
730
|
+
EventableDescriptor *ed = Descriptors[i];
|
731
|
+
assert (ed);
|
732
|
+
if (ed->ShouldDelete()) {
|
733
|
+
#ifdef HAVE_EPOLL
|
734
|
+
if (bEpoll) {
|
735
|
+
assert (epfd != -1);
|
736
|
+
if (ed->GetSocket() != INVALID_SOCKET) {
|
737
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
738
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
739
|
+
if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
|
740
|
+
char buf [200];
|
741
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
742
|
+
throw std::runtime_error (buf);
|
743
|
+
}
|
744
|
+
}
|
745
|
+
ModifiedDescriptors.erase(ed);
|
746
|
+
}
|
747
|
+
#endif
|
748
|
+
delete ed;
|
749
|
+
}
|
750
|
+
else
|
751
|
+
Descriptors [j++] = ed;
|
752
|
+
}
|
753
|
+
while ((size_t)j < Descriptors.size())
|
754
|
+
Descriptors.pop_back();
|
755
|
+
}
|
756
|
+
|
680
757
|
/*********************************
|
681
758
|
EventMachine_t::_ModifyEpollEvent
|
682
759
|
*********************************/
|
@@ -829,7 +906,7 @@ bool EventMachine_t::_RunSelectOnce()
|
|
829
906
|
{ // read and write the sockets
|
830
907
|
//timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
|
831
908
|
//timeval tv = Quantum;
|
832
|
-
SelectData.tv =
|
909
|
+
SelectData.tv = _TimeTilNextEvent();
|
833
910
|
int s = SelectData._Select();
|
834
911
|
//rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0);
|
835
912
|
//int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
|
@@ -865,48 +942,54 @@ bool EventMachine_t::_RunSelectOnce()
|
|
865
942
|
_ReadLoopBreaker();
|
866
943
|
}
|
867
944
|
else if (s < 0) {
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
945
|
+
switch (errno) {
|
946
|
+
case EBADF:
|
947
|
+
_CleanBadDescriptors();
|
948
|
+
break;
|
949
|
+
case EINVAL:
|
950
|
+
throw std::runtime_error ("Somehow EM passed an invalid nfds or invalid timeout to select(2), please report this!");
|
951
|
+
break;
|
952
|
+
default:
|
953
|
+
// select can fail on error in a handful of ways.
|
954
|
+
// If this happens, then wait for a little while to avoid busy-looping.
|
955
|
+
// If the error was EINTR, we probably caught SIGCHLD or something,
|
956
|
+
// so keep the wait short.
|
957
|
+
timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
|
958
|
+
EmSelect (0, NULL, NULL, NULL, &tv);
|
959
|
+
}
|
874
960
|
}
|
875
961
|
}
|
876
962
|
|
963
|
+
return true;
|
964
|
+
}
|
877
965
|
|
878
|
-
|
879
|
-
|
880
|
-
|
966
|
+
void EventMachine_t::_CleanBadDescriptors()
|
967
|
+
{
|
968
|
+
size_t i;
|
881
969
|
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
}
|
887
|
-
}
|
888
|
-
}
|
970
|
+
for (i = 0; i < Descriptors.size(); i++) {
|
971
|
+
EventableDescriptor *ed = Descriptors[i];
|
972
|
+
if (ed->ShouldDelete())
|
973
|
+
continue;
|
889
974
|
|
890
|
-
|
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();
|
975
|
+
int sd = ed->GetSocket();
|
904
976
|
|
905
|
-
|
977
|
+
struct timeval tv;
|
978
|
+
tv.tv_sec = 0;
|
979
|
+
tv.tv_usec = 0;
|
906
980
|
|
907
|
-
|
908
|
-
|
981
|
+
fd_set fds;
|
982
|
+
FD_ZERO(&fds);
|
983
|
+
FD_SET(sd, &fds);
|
984
|
+
|
985
|
+
int ret = select(sd + 1, &fds, NULL, NULL, &tv);
|
909
986
|
|
987
|
+
if (ret == -1) {
|
988
|
+
if (errno == EBADF)
|
989
|
+
ed->ScheduleClose(false);
|
990
|
+
}
|
991
|
+
}
|
992
|
+
}
|
910
993
|
|
911
994
|
/********************************
|
912
995
|
EventMachine_t::_ReadLoopBreaker
|
@@ -921,7 +1004,7 @@ void EventMachine_t::_ReadLoopBreaker()
|
|
921
1004
|
char buffer [1024];
|
922
1005
|
read (LoopBreakerReader, buffer, sizeof(buffer));
|
923
1006
|
if (EventCallback)
|
924
|
-
(*EventCallback)(
|
1007
|
+
(*EventCallback)(0, EM_LOOPBREAK_SIGNAL, "", 0);
|
925
1008
|
}
|
926
1009
|
|
927
1010
|
|
@@ -929,26 +1012,24 @@ void EventMachine_t::_ReadLoopBreaker()
|
|
929
1012
|
EventMachine_t::_RunTimers
|
930
1013
|
**************************/
|
931
1014
|
|
932
|
-
|
1015
|
+
void EventMachine_t::_RunTimers()
|
933
1016
|
{
|
934
1017
|
// These are caller-defined timer handlers.
|
935
|
-
// Return T/F to indicate whether we should continue the main loop.
|
936
1018
|
// We rely on the fact that multimaps sort by their keys to avoid
|
937
1019
|
// inspecting the whole list every time we come here.
|
938
1020
|
// Just keep inspecting and processing the list head until we hit
|
939
1021
|
// one that hasn't expired yet.
|
940
1022
|
|
941
1023
|
while (true) {
|
942
|
-
multimap<
|
1024
|
+
multimap<uint64_t,Timer_t>::iterator i = Timers.begin();
|
943
1025
|
if (i == Timers.end())
|
944
1026
|
break;
|
945
|
-
if (i->first >
|
1027
|
+
if (i->first > MyCurrentLoopTime)
|
946
1028
|
break;
|
947
1029
|
if (EventCallback)
|
948
|
-
(*EventCallback) (
|
1030
|
+
(*EventCallback) (0, EM_TIMER_FIRED, NULL, i->second.GetBinding());
|
949
1031
|
Timers.erase (i);
|
950
1032
|
}
|
951
|
-
return true;
|
952
1033
|
}
|
953
1034
|
|
954
1035
|
|
@@ -961,31 +1042,15 @@ const unsigned long EventMachine_t::InstallOneshotTimer (int milliseconds)
|
|
961
1042
|
{
|
962
1043
|
if (Timers.size() > MaxOutstandingTimers)
|
963
1044
|
return false;
|
964
|
-
// Don't use the global loop-time variable here, because we might
|
965
|
-
// get called before the main event machine is running.
|
966
1045
|
|
967
|
-
|
968
|
-
|
969
|
-
gettimeofday (&tv, NULL);
|
970
|
-
Int64 fire_at = (((Int64)(tv.tv_sec)) * 1000000LL) + ((Int64)(tv.tv_usec));
|
971
|
-
fire_at += ((Int64)milliseconds) * 1000LL;
|
972
|
-
#endif
|
973
|
-
|
974
|
-
#ifdef OS_WIN32
|
975
|
-
unsigned tick = GetTickCount();
|
976
|
-
if (tick < gLastTickCount)
|
977
|
-
gTickCountTickover += 1;
|
978
|
-
gLastTickCount = tick;
|
979
|
-
|
980
|
-
Int64 fire_at = ((Int64)gTickCountTickover << 32) + (Int64)tick;
|
981
|
-
fire_at += (Int64)milliseconds;
|
982
|
-
#endif
|
1046
|
+
uint64_t fire_at = GetRealTime();
|
1047
|
+
fire_at += ((uint64_t)milliseconds) * 1000LL;
|
983
1048
|
|
984
1049
|
Timer_t t;
|
985
1050
|
#ifndef HAVE_MAKE_PAIR
|
986
|
-
multimap<
|
1051
|
+
multimap<uint64_t,Timer_t>::iterator i = Timers.insert (multimap<uint64_t,Timer_t>::value_type (fire_at, t));
|
987
1052
|
#else
|
988
|
-
multimap<
|
1053
|
+
multimap<uint64_t,Timer_t>::iterator i = Timers.insert (make_pair (fire_at, t));
|
989
1054
|
#endif
|
990
1055
|
return i->second.GetBinding();
|
991
1056
|
}
|
@@ -1030,41 +1095,16 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1030
1095
|
bind_as = *bind_as_ptr; // copy because name2address points to a static
|
1031
1096
|
|
1032
1097
|
int sd = socket (family, SOCK_STREAM, 0);
|
1033
|
-
if (sd == INVALID_SOCKET)
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
sockaddr_in pin;
|
1038
|
-
unsigned long HostAddr;
|
1039
|
-
|
1040
|
-
HostAddr = inet_addr (server);
|
1041
|
-
if (HostAddr == INADDR_NONE) {
|
1042
|
-
hostent *hp = gethostbyname ((char*)server); // Windows requires (char*)
|
1043
|
-
if (!hp) {
|
1044
|
-
// TODO: This gives the caller a fatal error. Not good.
|
1045
|
-
// They can respond by catching RuntimeError (blecch).
|
1046
|
-
// Possibly we need to fire an unbind event and provide
|
1047
|
-
// a status code so user code can detect the cause of the
|
1048
|
-
// failure.
|
1049
|
-
return NULL;
|
1050
|
-
}
|
1051
|
-
HostAddr = ((in_addr*)(hp->h_addr))->s_addr;
|
1098
|
+
if (sd == INVALID_SOCKET) {
|
1099
|
+
char buf [200];
|
1100
|
+
snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno));
|
1101
|
+
throw std::runtime_error (buf);
|
1052
1102
|
}
|
1053
1103
|
|
1054
|
-
memset (&pin, 0, sizeof(pin));
|
1055
|
-
pin.sin_family = AF_INET;
|
1056
|
-
pin.sin_addr.s_addr = HostAddr;
|
1057
|
-
pin.sin_port = htons (port);
|
1058
|
-
|
1059
|
-
int sd = socket (AF_INET, SOCK_STREAM, 0);
|
1060
|
-
if (sd == INVALID_SOCKET)
|
1061
|
-
return NULL;
|
1062
|
-
*/
|
1063
|
-
|
1064
1104
|
// From here on, ALL error returns must close the socket.
|
1065
1105
|
// Set the new socket nonblocking.
|
1066
1106
|
if (!SetSocketNonblocking (sd)) {
|
1067
|
-
|
1107
|
+
close (sd);
|
1068
1108
|
throw std::runtime_error ("unable to set socket as non-blocking");
|
1069
1109
|
}
|
1070
1110
|
// Disable slow-start (Nagle algorithm).
|
@@ -1077,16 +1117,17 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1077
1117
|
int bind_to_size, bind_to_family;
|
1078
1118
|
struct sockaddr *bind_to = name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size);
|
1079
1119
|
if (!bind_to) {
|
1080
|
-
|
1120
|
+
close (sd);
|
1081
1121
|
throw std::runtime_error ("invalid bind address");
|
1082
1122
|
}
|
1083
1123
|
if (bind (sd, bind_to, bind_to_size) < 0) {
|
1084
|
-
|
1124
|
+
close (sd);
|
1085
1125
|
throw std::runtime_error ("couldn't bind to address");
|
1086
1126
|
}
|
1087
1127
|
}
|
1088
1128
|
|
1089
|
-
unsigned long out =
|
1129
|
+
unsigned long out = 0;
|
1130
|
+
int e = 0;
|
1090
1131
|
|
1091
1132
|
#ifdef OS_UNIX
|
1092
1133
|
//if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
@@ -1120,7 +1161,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1120
1161
|
else if (errno == EINPROGRESS) {
|
1121
1162
|
// Errno will generally always be EINPROGRESS, but on Linux
|
1122
1163
|
// we have to look at getsockopt to be sure what really happened.
|
1123
|
-
int error;
|
1164
|
+
int error = 0;
|
1124
1165
|
socklen_t len;
|
1125
1166
|
len = sizeof(error);
|
1126
1167
|
int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
@@ -1134,30 +1175,37 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1134
1175
|
cd->SetConnectPending (true);
|
1135
1176
|
Add (cd);
|
1136
1177
|
out = cd->GetBinding();
|
1137
|
-
}
|
1138
|
-
|
1139
|
-
|
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();
|
1178
|
+
} else {
|
1179
|
+
// Fall through to the !out case below.
|
1180
|
+
e = error;
|
1157
1181
|
}
|
1158
1182
|
}
|
1159
1183
|
else {
|
1160
|
-
// The error from connect was something other then EINPROGRESS.
|
1184
|
+
// The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
|
1185
|
+
// Fall through to the !out case below
|
1186
|
+
e = errno;
|
1187
|
+
}
|
1188
|
+
|
1189
|
+
if (!out) {
|
1190
|
+
/* This could be connection refused or some such thing.
|
1191
|
+
* We will come here on Linux if a localhost connection fails.
|
1192
|
+
* Changed 16Jul06: Originally this branch was a no-op, and
|
1193
|
+
* we'd drop down to the end of the method, close the socket,
|
1194
|
+
* and return NULL, which would cause the caller to GET A
|
1195
|
+
* FATAL EXCEPTION. Now we keep the socket around but schedule an
|
1196
|
+
* immediate close on it, so the caller will get a close-event
|
1197
|
+
* scheduled on it. This was only an issue for localhost connections
|
1198
|
+
* to non-listening ports. We may eventually need to revise this
|
1199
|
+
* revised behavior, in case it causes problems like making it hard
|
1200
|
+
* for people to know that a failure occurred.
|
1201
|
+
*/
|
1202
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
1203
|
+
if (!cd)
|
1204
|
+
throw std::runtime_error ("no connection allocated");
|
1205
|
+
cd->SetUnbindReasonCode(e);
|
1206
|
+
cd->ScheduleClose (false);
|
1207
|
+
Add (cd);
|
1208
|
+
out = cd->GetBinding();
|
1161
1209
|
}
|
1162
1210
|
#endif
|
1163
1211
|
|
@@ -1190,7 +1238,7 @@ const unsigned long EventMachine_t::ConnectToServer (const char *bind_addr, int
|
|
1190
1238
|
#endif
|
1191
1239
|
|
1192
1240
|
if (!out)
|
1193
|
-
|
1241
|
+
close (sd);
|
1194
1242
|
return out;
|
1195
1243
|
}
|
1196
1244
|
|
@@ -1215,10 +1263,10 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
|
|
1215
1263
|
// The whole rest of this function is only compiled on Unix systems.
|
1216
1264
|
#ifdef OS_UNIX
|
1217
1265
|
|
1218
|
-
unsigned long out =
|
1266
|
+
unsigned long out = 0;
|
1219
1267
|
|
1220
1268
|
if (!server || !*server)
|
1221
|
-
return
|
1269
|
+
return 0;
|
1222
1270
|
|
1223
1271
|
sockaddr_un pun;
|
1224
1272
|
memset (&pun, 0, sizeof(pun));
|
@@ -1234,19 +1282,19 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
|
|
1234
1282
|
|
1235
1283
|
int fd = socket (AF_LOCAL, SOCK_STREAM, 0);
|
1236
1284
|
if (fd == INVALID_SOCKET)
|
1237
|
-
return
|
1285
|
+
return 0;
|
1238
1286
|
|
1239
1287
|
// From here on, ALL error returns must close the socket.
|
1240
1288
|
// NOTE: At this point, the socket is still a blocking socket.
|
1241
1289
|
if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) {
|
1242
|
-
|
1243
|
-
return
|
1290
|
+
close (fd);
|
1291
|
+
return 0;
|
1244
1292
|
}
|
1245
1293
|
|
1246
1294
|
// Set the newly-connected socket nonblocking.
|
1247
1295
|
if (!SetSocketNonblocking (fd)) {
|
1248
|
-
|
1249
|
-
return
|
1296
|
+
close (fd);
|
1297
|
+
return 0;
|
1250
1298
|
}
|
1251
1299
|
|
1252
1300
|
// Set up a connection descriptor and add it to the event-machine.
|
@@ -1261,7 +1309,7 @@ const unsigned long EventMachine_t::ConnectToUnixServer (const char *server)
|
|
1261
1309
|
out = cd->GetBinding();
|
1262
1310
|
|
1263
1311
|
if (!out)
|
1264
|
-
|
1312
|
+
close (fd);
|
1265
1313
|
|
1266
1314
|
return out;
|
1267
1315
|
#endif
|
@@ -1450,9 +1498,9 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
|
|
1450
1498
|
int family, bind_size;
|
1451
1499
|
struct sockaddr *bind_here = name2address (server, port, &family, &bind_size);
|
1452
1500
|
if (!bind_here)
|
1453
|
-
return
|
1501
|
+
return 0;
|
1454
1502
|
|
1455
|
-
unsigned long output_binding =
|
1503
|
+
unsigned long output_binding = 0;
|
1456
1504
|
|
1457
1505
|
//struct sockaddr_in sin;
|
1458
1506
|
|
@@ -1461,25 +1509,6 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
|
|
1461
1509
|
goto fail;
|
1462
1510
|
}
|
1463
1511
|
|
1464
|
-
/*
|
1465
|
-
memset (&sin, 0, sizeof(sin));
|
1466
|
-
sin.sin_family = AF_INET;
|
1467
|
-
sin.sin_addr.s_addr = INADDR_ANY;
|
1468
|
-
sin.sin_port = htons (port);
|
1469
|
-
|
1470
|
-
if (server && *server) {
|
1471
|
-
sin.sin_addr.s_addr = inet_addr (server);
|
1472
|
-
if (sin.sin_addr.s_addr == INADDR_NONE) {
|
1473
|
-
hostent *hp = gethostbyname ((char*)server); // Windows requires the cast.
|
1474
|
-
if (hp == NULL) {
|
1475
|
-
//__warning ("hostname not resolved: ", server);
|
1476
|
-
goto fail;
|
1477
|
-
}
|
1478
|
-
sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
|
1479
|
-
}
|
1480
|
-
}
|
1481
|
-
*/
|
1482
|
-
|
1483
1512
|
{ // set reuseaddr to improve performance on restarts.
|
1484
1513
|
int oval = 1;
|
1485
1514
|
if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
|
@@ -1531,8 +1560,8 @@ const unsigned long EventMachine_t::CreateTcpServer (const char *server, int por
|
|
1531
1560
|
|
1532
1561
|
fail:
|
1533
1562
|
if (sd_accept != INVALID_SOCKET)
|
1534
|
-
|
1535
|
-
return
|
1563
|
+
close (sd_accept);
|
1564
|
+
return 0;
|
1536
1565
|
}
|
1537
1566
|
|
1538
1567
|
|
@@ -1542,7 +1571,7 @@ EventMachine_t::OpenDatagramSocket
|
|
1542
1571
|
|
1543
1572
|
const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int port)
|
1544
1573
|
{
|
1545
|
-
unsigned long output_binding =
|
1574
|
+
unsigned long output_binding = 0;
|
1546
1575
|
|
1547
1576
|
int sd = socket (AF_INET, SOCK_DGRAM, 0);
|
1548
1577
|
if (sd == INVALID_SOCKET)
|
@@ -1592,8 +1621,8 @@ const unsigned long EventMachine_t::OpenDatagramSocket (const char *address, int
|
|
1592
1621
|
|
1593
1622
|
fail:
|
1594
1623
|
if (sd != INVALID_SOCKET)
|
1595
|
-
|
1596
|
-
return
|
1624
|
+
close (sd);
|
1625
|
+
return 0;
|
1597
1626
|
}
|
1598
1627
|
|
1599
1628
|
|
@@ -1702,6 +1731,7 @@ void EventMachine_t::_AddNewDescriptors()
|
|
1702
1731
|
*/
|
1703
1732
|
#endif
|
1704
1733
|
|
1734
|
+
QueueHeartbeat(ed);
|
1705
1735
|
Descriptors.push_back (ed);
|
1706
1736
|
}
|
1707
1737
|
NewDescriptors.clear();
|
@@ -1758,28 +1788,31 @@ void EventMachine_t::Modify (EventableDescriptor *ed)
|
|
1758
1788
|
}
|
1759
1789
|
|
1760
1790
|
|
1761
|
-
|
1762
|
-
EventMachine_t::
|
1763
|
-
|
1791
|
+
/***********************
|
1792
|
+
EventMachine_t::Closing
|
1793
|
+
***********************/
|
1764
1794
|
|
1765
|
-
|
1795
|
+
void EventMachine_t::Closing (EventableDescriptor *ed)
|
1766
1796
|
{
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1797
|
+
if (!ed)
|
1798
|
+
throw std::runtime_error ("modified bad descriptor");
|
1799
|
+
#ifdef HAVE_EPOLL
|
1800
|
+
// cut/paste from _CleanupSockets(). The error handling could be
|
1801
|
+
// refactored out of there, but it is cut/paste all over the
|
1802
|
+
// file already.
|
1803
|
+
if (bEpoll) {
|
1804
|
+
assert (epfd != -1);
|
1805
|
+
assert (ed->GetSocket() != INVALID_SOCKET);
|
1806
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
1807
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
1808
|
+
if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
|
1809
|
+
char buf [200];
|
1810
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
1811
|
+
throw std::runtime_error (buf);
|
1812
|
+
}
|
1813
|
+
ModifiedDescriptors.erase(ed);
|
1814
|
+
}
|
1815
|
+
#endif
|
1783
1816
|
}
|
1784
1817
|
|
1785
1818
|
|
@@ -1802,7 +1835,7 @@ const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename
|
|
1802
1835
|
|
1803
1836
|
// The whole rest of this function is only compiled on Unix systems.
|
1804
1837
|
#ifdef OS_UNIX
|
1805
|
-
unsigned long output_binding =
|
1838
|
+
unsigned long output_binding = 0;
|
1806
1839
|
|
1807
1840
|
struct sockaddr_un s_sun;
|
1808
1841
|
|
@@ -1862,8 +1895,8 @@ const unsigned long EventMachine_t::CreateUnixDomainServer (const char *filename
|
|
1862
1895
|
|
1863
1896
|
fail:
|
1864
1897
|
if (sd_accept != INVALID_SOCKET)
|
1865
|
-
|
1866
|
-
return
|
1898
|
+
close (sd_accept);
|
1899
|
+
return 0;
|
1867
1900
|
#endif // OS_UNIX
|
1868
1901
|
}
|
1869
1902
|
|
@@ -1924,18 +1957,18 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
|
|
1924
1957
|
#ifdef OS_UNIX
|
1925
1958
|
// Make sure the incoming array of command strings is sane.
|
1926
1959
|
if (!cmd_strings)
|
1927
|
-
return
|
1960
|
+
return 0;
|
1928
1961
|
int j;
|
1929
|
-
for (j=0; j <
|
1962
|
+
for (j=0; j < 2048 && cmd_strings[j]; j++)
|
1930
1963
|
;
|
1931
|
-
if ((j==0) || (j==
|
1932
|
-
return
|
1964
|
+
if ((j==0) || (j==2048))
|
1965
|
+
return 0;
|
1933
1966
|
|
1934
|
-
unsigned long output_binding =
|
1967
|
+
unsigned long output_binding = 0;
|
1935
1968
|
|
1936
1969
|
int sv[2];
|
1937
1970
|
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
|
1938
|
-
return
|
1971
|
+
return 0;
|
1939
1972
|
// from here, all early returns must close the pair of sockets.
|
1940
1973
|
|
1941
1974
|
// Set the parent side of the socketpair nonblocking.
|
@@ -1945,7 +1978,7 @@ const unsigned long EventMachine_t::Socketpair (char * const*cmd_strings)
|
|
1945
1978
|
if (!SetSocketNonblocking (sv[0])) {
|
1946
1979
|
close (sv[0]);
|
1947
1980
|
close (sv[1]);
|
1948
|
-
return
|
1981
|
+
return 0;
|
1949
1982
|
}
|
1950
1983
|
|
1951
1984
|
pid_t f = fork();
|
@@ -2005,7 +2038,7 @@ const unsigned long EventMachine_t::WatchPid (int pid)
|
|
2005
2038
|
{
|
2006
2039
|
#ifdef HAVE_KQUEUE
|
2007
2040
|
if (!bKqueue)
|
2008
|
-
throw std::runtime_error("must enable kqueue");
|
2041
|
+
throw std::runtime_error("must enable kqueue (EM.kqueue=true) for pid watching support");
|
2009
2042
|
|
2010
2043
|
struct kevent event;
|
2011
2044
|
int kqres;
|
@@ -2094,7 +2127,8 @@ const unsigned long EventMachine_t::WatchFile (const char *fpath)
|
|
2094
2127
|
Add(inotify);
|
2095
2128
|
}
|
2096
2129
|
|
2097
|
-
wd = inotify_add_watch(inotify->GetSocket(), fpath,
|
2130
|
+
wd = inotify_add_watch(inotify->GetSocket(), fpath,
|
2131
|
+
IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_MOVE) ;
|
2098
2132
|
if (wd == -1) {
|
2099
2133
|
char errbuf[300];
|
2100
2134
|
sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
|
@@ -2104,7 +2138,7 @@ const unsigned long EventMachine_t::WatchFile (const char *fpath)
|
|
2104
2138
|
|
2105
2139
|
#ifdef HAVE_KQUEUE
|
2106
2140
|
if (!bKqueue)
|
2107
|
-
throw std::runtime_error("must enable kqueue");
|
2141
|
+
throw std::runtime_error("must enable kqueue (EM.kqueue=true) for file watching support");
|
2108
2142
|
|
2109
2143
|
// With kqueue we have to open the file first and use the resulting fd to register for events
|
2110
2144
|
wd = open(fpath, O_RDONLY);
|
@@ -2170,19 +2204,33 @@ EventMachine_t::_ReadInotify_Events
|
|
2170
2204
|
void EventMachine_t::_ReadInotifyEvents()
|
2171
2205
|
{
|
2172
2206
|
#ifdef HAVE_INOTIFY
|
2173
|
-
|
2207
|
+
char buffer[1024];
|
2174
2208
|
|
2175
2209
|
assert(EventCallback);
|
2176
2210
|
|
2177
|
-
|
2178
|
-
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2211
|
+
for (;;) {
|
2212
|
+
int returned = read(inotify->GetSocket(), buffer, sizeof(buffer));
|
2213
|
+
assert(!(returned == 0 || returned == -1 && errno == EINVAL));
|
2214
|
+
if (returned <= 0) {
|
2215
|
+
break;
|
2216
|
+
}
|
2217
|
+
int current = 0;
|
2218
|
+
while (current < returned) {
|
2219
|
+
struct inotify_event* event = (struct inotify_event*)(buffer+current);
|
2220
|
+
map<int, Bindable_t*>::const_iterator bindable = Files.find(event->wd);
|
2221
|
+
if (bindable != Files.end()) {
|
2222
|
+
if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE)){
|
2223
|
+
(*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "modified", 8);
|
2224
|
+
}
|
2225
|
+
if (event->mask & IN_MOVE_SELF){
|
2226
|
+
(*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "moved", 5);
|
2227
|
+
}
|
2228
|
+
if (event->mask & IN_DELETE_SELF) {
|
2229
|
+
(*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
|
2230
|
+
UnwatchFile ((int)event->wd);
|
2231
|
+
}
|
2232
|
+
}
|
2233
|
+
current += sizeof(struct inotify_event) + event->len;
|
2186
2234
|
}
|
2187
2235
|
}
|
2188
2236
|
#endif
|