eventmachine 0.10.0 → 0.12.0
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/DEFERRABLES +1 -1
- data/LIGHTWEIGHT_CONCURRENCY +1 -1
- data/PURE_RUBY +1 -1
- data/README +1 -1
- data/RELEASE_NOTES +1 -1
- data/SMTP +1 -1
- data/SPAWNED_PROCESSES +1 -1
- data/TODO +1 -1
- data/ext/binder.cpp +1 -1
- data/ext/binder.h +1 -1
- data/ext/cmain.cpp +29 -1
- data/ext/cplusplus.cpp +1 -1
- data/ext/ed.cpp +79 -1
- data/ext/ed.h +6 -1
- data/ext/em.cpp +343 -27
- data/ext/em.h +25 -1
- data/ext/emwin.cpp +1 -1
- data/ext/emwin.h +1 -1
- data/ext/epoll.cpp +1 -1
- data/ext/epoll.h +1 -1
- data/ext/eventmachine.h +3 -1
- data/ext/eventmachine_cpp.h +1 -1
- data/ext/extconf.rb +31 -1
- data/ext/files.cpp +1 -1
- data/ext/files.h +1 -1
- data/ext/kb.cpp +4 -1
- data/ext/page.cpp +1 -1
- data/ext/page.h +1 -1
- data/ext/pipe.cpp +4 -1
- data/ext/project.h +8 -2
- data/ext/rubymain.cpp +73 -2
- data/ext/sigs.cpp +1 -1
- data/ext/sigs.h +1 -1
- data/ext/ssl.cpp +4 -1
- data/ext/ssl.h +1 -1
- data/lib/em/deferrable.rb +1 -1
- data/lib/em/eventable.rb +1 -1
- data/lib/em/future.rb +1 -1
- data/lib/em/messages.rb +1 -1
- data/lib/em/processes.rb +68 -0
- data/lib/em/spawnable.rb +1 -1
- data/lib/em/streamer.rb +1 -1
- data/lib/eventmachine.rb +113 -66
- data/lib/eventmachine_version.rb +2 -2
- data/lib/evma.rb +1 -1
- data/lib/evma/callback.rb +1 -1
- data/lib/evma/container.rb +1 -1
- data/lib/evma/factory.rb +1 -1
- data/lib/evma/protocol.rb +1 -1
- data/lib/evma/reactor.rb +1 -1
- data/lib/jeventmachine.rb +1 -1
- data/lib/pr_eventmachine.rb +35 -8
- data/lib/protocols/header_and_content.rb +1 -1
- data/lib/protocols/httpcli2.rb +1 -1
- data/lib/protocols/httpclient.rb +1 -1
- data/lib/protocols/line_and_text.rb +1 -1
- data/lib/protocols/linetext2.rb +1 -1
- data/lib/protocols/saslauth.rb +59 -1
- data/lib/protocols/smtpclient.rb +4 -3
- data/lib/protocols/smtpserver.rb +3 -3
- data/lib/protocols/stomp.rb +1 -1
- data/lib/protocols/tcptest.rb +1 -1
- data/tests/test_basic.rb +2 -1
- data/tests/test_defer.rb +63 -0
- data/tests/test_epoll.rb +10 -5
- data/tests/test_errors.rb +13 -3
- data/tests/test_eventables.rb +1 -1
- data/tests/test_exc.rb +2 -1
- data/tests/test_futures.rb +2 -1
- data/tests/test_hc.rb +26 -2
- data/tests/test_httpclient.rb +2 -1
- data/tests/test_httpclient2.rb +2 -1
- data/tests/test_kb.rb +2 -1
- data/tests/test_ltp.rb +3 -1
- data/tests/test_ltp2.rb +2 -1
- data/tests/test_next_tick.rb +2 -1
- data/tests/test_processes.rb +56 -0
- data/tests/test_pure.rb +2 -1
- data/tests/test_running.rb +2 -1
- data/tests/test_sasl.rb +73 -0
- data/tests/test_send_file.rb +3 -1
- data/tests/test_servers.rb +4 -1
- data/tests/test_smtpclient.rb +2 -1
- data/tests/test_smtpserver.rb +3 -2
- data/tests/test_spawn.rb +2 -1
- data/tests/test_timers.rb +2 -2
- data/tests/test_ud.rb +2 -1
- data/tests/testem.rb +1 -1
- metadata +115 -104
data/DEFERRABLES
CHANGED
data/LIGHTWEIGHT_CONCURRENCY
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
$Id: LIGHTWEIGHT_CONCURRENCY
|
1
|
+
$Id: LIGHTWEIGHT_CONCURRENCY 668 2008-01-04 23:00:34Z blackhedd $
|
2
2
|
|
3
3
|
EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use them.
|
4
4
|
|
data/PURE_RUBY
CHANGED
data/README
CHANGED
data/RELEASE_NOTES
CHANGED
data/SMTP
CHANGED
data/SPAWNED_PROCESSES
CHANGED
data/TODO
CHANGED
data/ext/binder.cpp
CHANGED
data/ext/binder.h
CHANGED
data/ext/cmain.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: cmain.cpp
|
3
|
+
$Id: cmain.cpp 679 2008-01-19 01:40:06Z blackhedd $
|
4
4
|
|
5
5
|
File: cmain.cpp
|
6
6
|
Date: 06Apr06
|
@@ -22,6 +22,7 @@ See the file COPYING for complete licensing information.
|
|
22
22
|
|
23
23
|
static EventMachine_t *EventMachine;
|
24
24
|
static int bUseEpoll = 0;
|
25
|
+
static int bUseKqueue = 0;
|
25
26
|
|
26
27
|
|
27
28
|
/***********************
|
@@ -38,6 +39,8 @@ extern "C" void evma_initialize_library (void(*cb)(const char*, int, const char*
|
|
38
39
|
EventMachine = new EventMachine_t (cb);
|
39
40
|
if (bUseEpoll)
|
40
41
|
EventMachine->_UseEpoll();
|
42
|
+
if (bUseKqueue)
|
43
|
+
EventMachine->_UseKqueue();
|
41
44
|
}
|
42
45
|
|
43
46
|
|
@@ -259,6 +262,22 @@ extern "C" int evma_get_peername (const char *binding, struct sockaddr *sa)
|
|
259
262
|
return 0;
|
260
263
|
}
|
261
264
|
|
265
|
+
/*****************
|
266
|
+
evma_get_sockname
|
267
|
+
*****************/
|
268
|
+
|
269
|
+
extern "C" int evma_get_sockname (const char *binding, struct sockaddr *sa)
|
270
|
+
{
|
271
|
+
if (!EventMachine)
|
272
|
+
throw std::runtime_error ("not initialized");
|
273
|
+
EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
|
274
|
+
if (ed) {
|
275
|
+
return ed->GetSockname (sa) ? 1 : 0;
|
276
|
+
}
|
277
|
+
else
|
278
|
+
return 0;
|
279
|
+
}
|
280
|
+
|
262
281
|
/***********************
|
263
282
|
evma_get_subprocess_pid
|
264
283
|
***********************/
|
@@ -418,6 +437,15 @@ extern "C" void evma__epoll()
|
|
418
437
|
bUseEpoll = 1;
|
419
438
|
}
|
420
439
|
|
440
|
+
/************
|
441
|
+
evma__kqueue
|
442
|
+
************/
|
443
|
+
|
444
|
+
extern "C" void evma__kqueue()
|
445
|
+
{
|
446
|
+
bUseKqueue = 1;
|
447
|
+
}
|
448
|
+
|
421
449
|
|
422
450
|
/**********************
|
423
451
|
evma_set_rlimit_nofile
|
data/ext/cplusplus.cpp
CHANGED
data/ext/ed.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: ed.cpp
|
3
|
+
$Id: ed.cpp 687 2008-05-15 14:28:32Z francis $
|
4
4
|
|
5
5
|
File: ed.cpp
|
6
6
|
Date: 06Apr06
|
@@ -187,6 +187,9 @@ ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
|
|
187
187
|
#ifdef HAVE_EPOLL
|
188
188
|
EpollEvent.events = EPOLLOUT;
|
189
189
|
#endif
|
190
|
+
#ifdef HAVE_KQUEUE
|
191
|
+
MyEventMachine->ArmKqueueWriter (this);
|
192
|
+
#endif
|
190
193
|
}
|
191
194
|
|
192
195
|
|
@@ -321,6 +324,9 @@ int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
|
|
321
324
|
assert (MyEventMachine);
|
322
325
|
MyEventMachine->Modify (this);
|
323
326
|
#endif
|
327
|
+
#ifdef HAVE_KQUEUE
|
328
|
+
MyEventMachine->ArmKqueueWriter (this);
|
329
|
+
#endif
|
324
330
|
return length;
|
325
331
|
}
|
326
332
|
|
@@ -531,6 +537,12 @@ void ConnectionDescriptor::Write()
|
|
531
537
|
// The callback may have scheduled outbound data.
|
532
538
|
EpollEvent.events = EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0);
|
533
539
|
#endif
|
540
|
+
#ifdef HAVE_KQUEUE
|
541
|
+
MyEventMachine->ArmKqueueReader (this);
|
542
|
+
// The callback may have scheduled outbound data.
|
543
|
+
if (SelectForWrite())
|
544
|
+
MyEventMachine->ArmKqueueWriter (this);
|
545
|
+
#endif
|
534
546
|
}
|
535
547
|
else
|
536
548
|
ScheduleClose (false);
|
@@ -614,6 +626,10 @@ void ConnectionDescriptor::_WriteOutboundData()
|
|
614
626
|
assert (MyEventMachine);
|
615
627
|
MyEventMachine->Modify (this);
|
616
628
|
#endif
|
629
|
+
#ifdef HAVE_KQUEUE
|
630
|
+
if (SelectForWrite())
|
631
|
+
MyEventMachine->ArmKqueueWriter (this);
|
632
|
+
#endif
|
617
633
|
}
|
618
634
|
else {
|
619
635
|
#ifdef OS_UNIX
|
@@ -797,6 +813,9 @@ LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em):
|
|
797
813
|
#ifdef HAVE_EPOLL
|
798
814
|
EpollEvent.events = EPOLLIN;
|
799
815
|
#endif
|
816
|
+
#ifdef HAVE_KQUEUE
|
817
|
+
MyEventMachine->ArmKqueueReader (this);
|
818
|
+
#endif
|
800
819
|
}
|
801
820
|
|
802
821
|
|
@@ -834,6 +853,9 @@ AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
|
|
834
853
|
#ifdef HAVE_EPOLL
|
835
854
|
EpollEvent.events = EPOLLIN;
|
836
855
|
#endif
|
856
|
+
#ifdef HAVE_KQUEUE
|
857
|
+
MyEventMachine->ArmKqueueReader (this);
|
858
|
+
#endif
|
837
859
|
}
|
838
860
|
|
839
861
|
|
@@ -922,6 +944,11 @@ void AcceptorDescriptor::Read()
|
|
922
944
|
#endif
|
923
945
|
assert (MyEventMachine);
|
924
946
|
MyEventMachine->Add (cd);
|
947
|
+
#ifdef HAVE_KQUEUE
|
948
|
+
if (cd->SelectForWrite())
|
949
|
+
MyEventMachine->ArmKqueueWriter (cd);
|
950
|
+
MyEventMachine->ArmKqueueReader (cd);
|
951
|
+
#endif
|
925
952
|
}
|
926
953
|
|
927
954
|
}
|
@@ -948,6 +975,22 @@ void AcceptorDescriptor::Heartbeat()
|
|
948
975
|
}
|
949
976
|
|
950
977
|
|
978
|
+
/*******************************
|
979
|
+
AcceptorDescriptor::GetSockname
|
980
|
+
*******************************/
|
981
|
+
|
982
|
+
bool AcceptorDescriptor::GetSockname (struct sockaddr *s)
|
983
|
+
{
|
984
|
+
bool ok = false;
|
985
|
+
if (s) {
|
986
|
+
socklen_t len = sizeof(*s);
|
987
|
+
int gp = getsockname (GetSocket(), s, &len);
|
988
|
+
if (gp == 0)
|
989
|
+
ok = true;
|
990
|
+
}
|
991
|
+
return ok;
|
992
|
+
}
|
993
|
+
|
951
994
|
|
952
995
|
|
953
996
|
/**************************************
|
@@ -986,6 +1029,9 @@ DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em):
|
|
986
1029
|
#ifdef HAVE_EPOLL
|
987
1030
|
EpollEvent.events = EPOLLIN;
|
988
1031
|
#endif
|
1032
|
+
#ifdef HAVE_KQUEUE
|
1033
|
+
MyEventMachine->ArmKqueueReader (this);
|
1034
|
+
#endif
|
989
1035
|
}
|
990
1036
|
|
991
1037
|
|
@@ -1273,6 +1319,22 @@ bool ConnectionDescriptor::GetPeername (struct sockaddr *s)
|
|
1273
1319
|
return ok;
|
1274
1320
|
}
|
1275
1321
|
|
1322
|
+
/*********************************
|
1323
|
+
ConnectionDescriptor::GetSockname
|
1324
|
+
*********************************/
|
1325
|
+
|
1326
|
+
bool ConnectionDescriptor::GetSockname (struct sockaddr *s)
|
1327
|
+
{
|
1328
|
+
bool ok = false;
|
1329
|
+
if (s) {
|
1330
|
+
socklen_t len = sizeof(*s);
|
1331
|
+
int gp = getsockname (GetSocket(), s, &len);
|
1332
|
+
if (gp == 0)
|
1333
|
+
ok = true;
|
1334
|
+
}
|
1335
|
+
return ok;
|
1336
|
+
}
|
1337
|
+
|
1276
1338
|
|
1277
1339
|
/**********************************************
|
1278
1340
|
ConnectionDescriptor::GetCommInactivityTimeout
|
@@ -1333,6 +1395,22 @@ bool DatagramDescriptor::GetPeername (struct sockaddr *s)
|
|
1333
1395
|
return ok;
|
1334
1396
|
}
|
1335
1397
|
|
1398
|
+
/*******************************
|
1399
|
+
DatagramDescriptor::GetSockname
|
1400
|
+
*******************************/
|
1401
|
+
|
1402
|
+
bool DatagramDescriptor::GetSockname (struct sockaddr *s)
|
1403
|
+
{
|
1404
|
+
bool ok = false;
|
1405
|
+
if (s) {
|
1406
|
+
socklen_t len = sizeof(*s);
|
1407
|
+
int gp = getsockname (GetSocket(), s, &len);
|
1408
|
+
if (gp == 0)
|
1409
|
+
ok = true;
|
1410
|
+
}
|
1411
|
+
return ok;
|
1412
|
+
}
|
1413
|
+
|
1336
1414
|
|
1337
1415
|
|
1338
1416
|
/********************************************
|
data/ext/ed.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: ed.h
|
3
|
+
$Id: ed.h 687 2008-05-15 14:28:32Z francis $
|
4
4
|
|
5
5
|
File: ed.h
|
6
6
|
Date: 06Apr06
|
@@ -62,6 +62,7 @@ class EventableDescriptor: public Bindable_t
|
|
62
62
|
void SetEventCallback (void (*cb)(const char*, int, const char*, int));
|
63
63
|
|
64
64
|
virtual bool GetPeername (struct sockaddr*) {return false;}
|
65
|
+
virtual bool GetSockname (struct sockaddr*) {return false;}
|
65
66
|
virtual bool GetSubprocessPid (pid_t*) {return false;}
|
66
67
|
|
67
68
|
virtual void StartTls() {}
|
@@ -154,6 +155,7 @@ class ConnectionDescriptor: public EventableDescriptor
|
|
154
155
|
void SetServerMode() {bIsServer = true;}
|
155
156
|
|
156
157
|
virtual bool GetPeername (struct sockaddr*);
|
158
|
+
virtual bool GetSockname (struct sockaddr*);
|
157
159
|
|
158
160
|
virtual int GetCommInactivityTimeout (int *value);
|
159
161
|
virtual int SetCommInactivityTimeout (int *value);
|
@@ -220,6 +222,7 @@ class DatagramDescriptor: public EventableDescriptor
|
|
220
222
|
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
221
223
|
|
222
224
|
virtual bool GetPeername (struct sockaddr*);
|
225
|
+
virtual bool GetSockname (struct sockaddr*);
|
223
226
|
|
224
227
|
virtual int GetCommInactivityTimeout (int *value);
|
225
228
|
virtual int SetCommInactivityTimeout (int *value);
|
@@ -264,6 +267,8 @@ class AcceptorDescriptor: public EventableDescriptor
|
|
264
267
|
virtual bool SelectForRead() {return true;}
|
265
268
|
virtual bool SelectForWrite() {return false;}
|
266
269
|
|
270
|
+
virtual bool GetSockname (struct sockaddr*);
|
271
|
+
|
267
272
|
static void StopAcceptor (const char *binding);
|
268
273
|
};
|
269
274
|
|
data/ext/em.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: em.cpp
|
3
|
+
$Id: em.cpp 686 2008-05-14 21:21:10Z francis $
|
4
4
|
|
5
5
|
File: em.cpp
|
6
6
|
Date: 06Apr06
|
@@ -40,6 +40,11 @@ unsigned gLastTickCount;
|
|
40
40
|
static int MaxOutstandingTimers = 1000;
|
41
41
|
|
42
42
|
|
43
|
+
/* Internal helper to convert strings to internet addresses. IPv6-aware.
|
44
|
+
* Not reentrant or threadsafe, optimized for speed.
|
45
|
+
*/
|
46
|
+
static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size);
|
47
|
+
|
43
48
|
|
44
49
|
/***************************************
|
45
50
|
STATIC EventMachine_t::SetMaxTimerCount
|
@@ -71,6 +76,7 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const c
|
|
71
76
|
LoopBreakerReader (-1),
|
72
77
|
LoopBreakerWriter (-1),
|
73
78
|
bEpoll (false),
|
79
|
+
bKqueue (false),
|
74
80
|
epfd (-1)
|
75
81
|
{
|
76
82
|
// Default time-slice is just smaller than one hundred mills.
|
@@ -115,6 +121,8 @@ EventMachine_t::~EventMachine_t()
|
|
115
121
|
|
116
122
|
if (epfd != -1)
|
117
123
|
close (epfd);
|
124
|
+
if (kqfd != -1)
|
125
|
+
close (kqfd);
|
118
126
|
}
|
119
127
|
|
120
128
|
|
@@ -136,6 +144,21 @@ void EventMachine_t::_UseEpoll()
|
|
136
144
|
#endif
|
137
145
|
}
|
138
146
|
|
147
|
+
/**************************
|
148
|
+
EventMachine_t::_UseKqueue
|
149
|
+
**************************/
|
150
|
+
|
151
|
+
void EventMachine_t::_UseKqueue()
|
152
|
+
{
|
153
|
+
/* Temporary.
|
154
|
+
* See comments under _UseEpoll.
|
155
|
+
*/
|
156
|
+
|
157
|
+
#ifdef HAVE_KQUEUE
|
158
|
+
bKqueue = true;
|
159
|
+
#endif
|
160
|
+
}
|
161
|
+
|
139
162
|
|
140
163
|
/****************************
|
141
164
|
EventMachine_t::ScheduleHalt
|
@@ -337,6 +360,23 @@ void EventMachine_t::Run()
|
|
337
360
|
}
|
338
361
|
#endif
|
339
362
|
|
363
|
+
#ifdef HAVE_KQUEUE
|
364
|
+
if (bKqueue) {
|
365
|
+
kqfd = kqueue();
|
366
|
+
if (kqfd == -1) {
|
367
|
+
char buf[200];
|
368
|
+
snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
|
369
|
+
throw std::runtime_error (buf);
|
370
|
+
}
|
371
|
+
// cloexec not needed. By definition, kqueues are not carried across forks.
|
372
|
+
|
373
|
+
assert (LoopBreakerReader >= 0);
|
374
|
+
LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
|
375
|
+
assert (ld);
|
376
|
+
Add (ld);
|
377
|
+
}
|
378
|
+
#endif
|
379
|
+
|
340
380
|
while (true) {
|
341
381
|
gCurrentLoopTime = time(NULL);
|
342
382
|
if (!_RunTimers())
|
@@ -369,6 +409,8 @@ bool EventMachine_t::_RunOnce()
|
|
369
409
|
{
|
370
410
|
if (bEpoll)
|
371
411
|
return _RunEpollOnce();
|
412
|
+
else if (bKqueue)
|
413
|
+
return _RunKqueueOnce();
|
372
414
|
else
|
373
415
|
return _RunSelectOnce();
|
374
416
|
}
|
@@ -384,7 +426,7 @@ bool EventMachine_t::_RunEpollOnce()
|
|
384
426
|
#ifdef HAVE_EPOLL
|
385
427
|
assert (epfd != -1);
|
386
428
|
struct epoll_event ev [MaxEpollDescriptors];
|
387
|
-
int s = epoll_wait (epfd, ev, MaxEpollDescriptors,
|
429
|
+
int s = epoll_wait (epfd, ev, MaxEpollDescriptors, 50);
|
388
430
|
if (s > 0) {
|
389
431
|
for (int i=0; i < s; i++) {
|
390
432
|
EventableDescriptor *ed = (EventableDescriptor*) ev[i].data.ptr;
|
@@ -413,20 +455,30 @@ bool EventMachine_t::_RunEpollOnce()
|
|
413
455
|
// vector::pop_back works in constant time.
|
414
456
|
// TODO, rip this out and only delete the descriptors we know have died,
|
415
457
|
// rather than traversing the whole list.
|
458
|
+
// Modified 05Jan08 per suggestions by Chris Heath. It's possible that
|
459
|
+
// an EventableDescriptor will have a descriptor value of -1. That will
|
460
|
+
// happen if EventableDescriptor::Close was called on it. In that case,
|
461
|
+
// don't call epoll_ctl to remove the socket's filters from the epoll set.
|
462
|
+
// According to the epoll docs, this happens automatically when the
|
463
|
+
// descriptor is closed anyway. This is different from the case where
|
464
|
+
// the socket has already been closed but the descriptor in the ED object
|
465
|
+
// hasn't yet been set to INVALID_SOCKET.
|
416
466
|
int i, j;
|
417
467
|
int nSockets = Descriptors.size();
|
418
468
|
for (i=0, j=0; i < nSockets; i++) {
|
419
469
|
EventableDescriptor *ed = Descriptors[i];
|
420
470
|
assert (ed);
|
421
471
|
if (ed->ShouldDelete()) {
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
472
|
+
if (ed->GetSocket() != INVALID_SOCKET) {
|
473
|
+
assert (bEpoll); // wouldn't be in this method otherwise.
|
474
|
+
assert (epfd != -1);
|
475
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
476
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
477
|
+
if (e && (errno != ENOENT) && (errno != EBADF)) {
|
478
|
+
char buf [200];
|
479
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
480
|
+
throw std::runtime_error (buf);
|
481
|
+
}
|
430
482
|
}
|
431
483
|
|
432
484
|
ModifiedDescriptors.erase (ed);
|
@@ -469,6 +521,82 @@ bool EventMachine_t::_RunEpollOnce()
|
|
469
521
|
}
|
470
522
|
|
471
523
|
|
524
|
+
/******************************
|
525
|
+
EventMachine_t::_RunKqueueOnce
|
526
|
+
******************************/
|
527
|
+
|
528
|
+
bool EventMachine_t::_RunKqueueOnce()
|
529
|
+
{
|
530
|
+
#ifdef HAVE_KQUEUE
|
531
|
+
assert (kqfd != -1);
|
532
|
+
const int maxKevents = 2000;
|
533
|
+
struct kevent Karray [maxKevents];
|
534
|
+
struct timespec ts = {0, 10000000}; // Too frequent. Use blocking_region
|
535
|
+
|
536
|
+
int k = kevent (kqfd, NULL, 0, Karray, maxKevents, &ts);
|
537
|
+
struct kevent *ke = Karray;
|
538
|
+
while (k > 0) {
|
539
|
+
EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
|
540
|
+
assert (ed);
|
541
|
+
|
542
|
+
if (ke->filter == EVFILT_READ)
|
543
|
+
ed->Read();
|
544
|
+
else if (ke->filter == EVFILT_WRITE)
|
545
|
+
ed->Write();
|
546
|
+
else
|
547
|
+
cerr << "Discarding unknown kqueue event " << ke->filter << endl;
|
548
|
+
|
549
|
+
--k;
|
550
|
+
++ke;
|
551
|
+
}
|
552
|
+
|
553
|
+
{ // cleanup dying sockets
|
554
|
+
// vector::pop_back works in constant time.
|
555
|
+
// TODO, rip this out and only delete the descriptors we know have died,
|
556
|
+
// rather than traversing the whole list.
|
557
|
+
// In kqueue, closing a descriptor automatically removes its event filters.
|
558
|
+
|
559
|
+
int i, j;
|
560
|
+
int nSockets = Descriptors.size();
|
561
|
+
for (i=0, j=0; i < nSockets; i++) {
|
562
|
+
EventableDescriptor *ed = Descriptors[i];
|
563
|
+
assert (ed);
|
564
|
+
if (ed->ShouldDelete()) {
|
565
|
+
ModifiedDescriptors.erase (ed);
|
566
|
+
delete ed;
|
567
|
+
}
|
568
|
+
else
|
569
|
+
Descriptors [j++] = ed;
|
570
|
+
}
|
571
|
+
while ((size_t)j < Descriptors.size())
|
572
|
+
Descriptors.pop_back();
|
573
|
+
|
574
|
+
}
|
575
|
+
|
576
|
+
{ // dispatch heartbeats
|
577
|
+
if (gCurrentLoopTime >= NextHeartbeatTime) {
|
578
|
+
NextHeartbeatTime = gCurrentLoopTime + HeartbeatInterval;
|
579
|
+
|
580
|
+
for (int i=0; i < Descriptors.size(); i++) {
|
581
|
+
EventableDescriptor *ed = Descriptors[i];
|
582
|
+
assert (ed);
|
583
|
+
ed->Heartbeat();
|
584
|
+
}
|
585
|
+
}
|
586
|
+
}
|
587
|
+
|
588
|
+
|
589
|
+
// TODO, replace this with rb_thread_blocking_region for 1.9 builds.
|
590
|
+
timeval tv = {0,0};
|
591
|
+
EmSelect (0, NULL, NULL, NULL, &tv);
|
592
|
+
|
593
|
+
return true;
|
594
|
+
#else
|
595
|
+
throw std::runtime_error ("kqueue is not implemented on this platform");
|
596
|
+
#endif
|
597
|
+
}
|
598
|
+
|
599
|
+
|
472
600
|
/*********************************
|
473
601
|
EventMachine_t::_ModifyEpollEvent
|
474
602
|
*********************************/
|
@@ -490,6 +618,48 @@ void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
|
|
490
618
|
}
|
491
619
|
|
492
620
|
|
621
|
+
|
622
|
+
/**************************
|
623
|
+
SelectData_t::SelectData_t
|
624
|
+
**************************/
|
625
|
+
|
626
|
+
SelectData_t::SelectData_t()
|
627
|
+
{
|
628
|
+
maxsocket = 0;
|
629
|
+
FD_ZERO (&fdreads);
|
630
|
+
FD_ZERO (&fdwrites);
|
631
|
+
}
|
632
|
+
|
633
|
+
|
634
|
+
/*****************
|
635
|
+
_SelectDataSelect
|
636
|
+
*****************/
|
637
|
+
|
638
|
+
static VALUE _SelectDataSelect (void *v)
|
639
|
+
{
|
640
|
+
SelectData_t *sd = (SelectData_t*)v;
|
641
|
+
sd->nSockets = select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), NULL, &(sd->tv));
|
642
|
+
return Qnil;
|
643
|
+
}
|
644
|
+
|
645
|
+
/*********************
|
646
|
+
SelectData_t::_Select
|
647
|
+
*********************/
|
648
|
+
|
649
|
+
int SelectData_t::_Select()
|
650
|
+
{
|
651
|
+
#ifdef HAVE_TBR
|
652
|
+
rb_thread_blocking_region (_SelectDataSelect, (void*)this, RB_UBF_DFL, 0);
|
653
|
+
return nSockets;
|
654
|
+
#endif
|
655
|
+
|
656
|
+
#ifndef HAVE_TBR
|
657
|
+
return rb_thread_select (maxsocket+1, &fdreads, &fdwrites, NULL, &tv);
|
658
|
+
#endif
|
659
|
+
}
|
660
|
+
|
661
|
+
|
662
|
+
|
493
663
|
/******************************
|
494
664
|
EventMachine_t::_RunSelectOnce
|
495
665
|
******************************/
|
@@ -525,20 +695,23 @@ bool EventMachine_t::_RunSelectOnce()
|
|
525
695
|
}
|
526
696
|
*/
|
527
697
|
|
698
|
+
SelectData_t SelectData;
|
699
|
+
/*
|
528
700
|
fd_set fdreads, fdwrites;
|
529
701
|
FD_ZERO (&fdreads);
|
530
702
|
FD_ZERO (&fdwrites);
|
531
703
|
|
532
704
|
int maxsocket = 0;
|
705
|
+
*/
|
533
706
|
|
534
707
|
// Always read the loop-breaker reader.
|
535
708
|
// Changed 23Aug06, provisionally implemented for Windows with a UDP socket
|
536
709
|
// running on localhost with a randomly-chosen port. (*Puke*)
|
537
710
|
// Windows has a version of the Unix pipe() library function, but it doesn't
|
538
711
|
// give you back descriptors that are selectable.
|
539
|
-
FD_SET (LoopBreakerReader, &fdreads);
|
540
|
-
if (maxsocket < LoopBreakerReader)
|
541
|
-
maxsocket = LoopBreakerReader;
|
712
|
+
FD_SET (LoopBreakerReader, &(SelectData.fdreads));
|
713
|
+
if (SelectData.maxsocket < LoopBreakerReader)
|
714
|
+
SelectData.maxsocket = LoopBreakerReader;
|
542
715
|
|
543
716
|
// prepare the sockets for reading and writing
|
544
717
|
size_t i;
|
@@ -549,19 +722,23 @@ bool EventMachine_t::_RunSelectOnce()
|
|
549
722
|
assert (sd != INVALID_SOCKET);
|
550
723
|
|
551
724
|
if (ed->SelectForRead())
|
552
|
-
FD_SET (sd, &fdreads);
|
725
|
+
FD_SET (sd, &(SelectData.fdreads));
|
553
726
|
if (ed->SelectForWrite())
|
554
|
-
FD_SET (sd, &fdwrites);
|
727
|
+
FD_SET (sd, &(SelectData.fdwrites));
|
555
728
|
|
556
|
-
if (maxsocket < sd)
|
557
|
-
maxsocket = sd;
|
729
|
+
if (SelectData.maxsocket < sd)
|
730
|
+
SelectData.maxsocket = sd;
|
558
731
|
}
|
559
732
|
|
560
733
|
|
561
734
|
{ // read and write the sockets
|
562
735
|
//timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
|
563
|
-
timeval tv = Quantum;
|
564
|
-
|
736
|
+
//timeval tv = Quantum;
|
737
|
+
SelectData.tv = Quantum;
|
738
|
+
int s = SelectData._Select();
|
739
|
+
//rb_thread_blocking_region(xxx,(void*)&SelectData,RB_UBF_DFL,0);
|
740
|
+
//int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
|
741
|
+
//int s = SelectData.nSockets;
|
565
742
|
if (s > 0) {
|
566
743
|
/* Changed 01Jun07. We used to handle the Loop-breaker right here.
|
567
744
|
* Now we do it AFTER all the regular descriptors. There's an
|
@@ -579,13 +756,13 @@ bool EventMachine_t::_RunSelectOnce()
|
|
579
756
|
int sd = ed->GetSocket();
|
580
757
|
assert (sd != INVALID_SOCKET);
|
581
758
|
|
582
|
-
if (FD_ISSET (sd, &fdwrites))
|
759
|
+
if (FD_ISSET (sd, &(SelectData.fdwrites)))
|
583
760
|
ed->Write();
|
584
|
-
if (FD_ISSET (sd, &fdreads))
|
761
|
+
if (FD_ISSET (sd, &(SelectData.fdreads)))
|
585
762
|
ed->Read();
|
586
763
|
}
|
587
764
|
|
588
|
-
if (FD_ISSET (LoopBreakerReader, &fdreads))
|
765
|
+
if (FD_ISSET (LoopBreakerReader, &(SelectData.fdreads)))
|
589
766
|
_ReadLoopBreaker();
|
590
767
|
}
|
591
768
|
else if (s < 0) {
|
@@ -758,6 +935,16 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
758
935
|
if (!server || !*server || !port)
|
759
936
|
return NULL;
|
760
937
|
|
938
|
+
int family, bind_size;
|
939
|
+
struct sockaddr *bind_as = name2address (server, port, &family, &bind_size);
|
940
|
+
if (!bind_as)
|
941
|
+
return NULL;
|
942
|
+
|
943
|
+
int sd = socket (family, SOCK_STREAM, 0);
|
944
|
+
if (sd == INVALID_SOCKET)
|
945
|
+
return NULL;
|
946
|
+
|
947
|
+
/*
|
761
948
|
sockaddr_in pin;
|
762
949
|
unsigned long HostAddr;
|
763
950
|
|
@@ -783,6 +970,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
783
970
|
int sd = socket (AF_INET, SOCK_STREAM, 0);
|
784
971
|
if (sd == INVALID_SOCKET)
|
785
972
|
return NULL;
|
973
|
+
*/
|
786
974
|
|
787
975
|
// From here on, ALL error returns must close the socket.
|
788
976
|
// Set the new socket nonblocking.
|
@@ -790,11 +978,15 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
790
978
|
closesocket (sd);
|
791
979
|
return NULL;
|
792
980
|
}
|
981
|
+
// Disable slow-start (Nagle algorithm).
|
982
|
+
int one = 1;
|
983
|
+
setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
|
793
984
|
|
794
985
|
const char *out = NULL;
|
795
986
|
|
796
987
|
#ifdef OS_UNIX
|
797
|
-
if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
988
|
+
//if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
989
|
+
if (connect (sd, bind_as, bind_size) == 0) {
|
798
990
|
// This is a connect success, which Linux appears
|
799
991
|
// never to give when the socket is nonblocking,
|
800
992
|
// even if the connection is intramachine or to
|
@@ -866,7 +1058,8 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
866
1058
|
#endif
|
867
1059
|
|
868
1060
|
#ifdef OS_WIN32
|
869
|
-
if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
1061
|
+
//if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) {
|
1062
|
+
if (connect (sd, bind_as, bind_size) == 0) {
|
870
1063
|
// This is a connect success, which Windows appears
|
871
1064
|
// never to give when the socket is nonblocking,
|
872
1065
|
// even if the connection is intramachine or to
|
@@ -971,6 +1164,71 @@ const char *EventMachine_t::ConnectToUnixServer (const char *server)
|
|
971
1164
|
}
|
972
1165
|
|
973
1166
|
|
1167
|
+
/************
|
1168
|
+
name2address
|
1169
|
+
************/
|
1170
|
+
|
1171
|
+
struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size)
|
1172
|
+
{
|
1173
|
+
// THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed.
|
1174
|
+
// Check the more-common cases first.
|
1175
|
+
// Return NULL if no resolution.
|
1176
|
+
|
1177
|
+
static struct sockaddr_in in4;
|
1178
|
+
static struct sockaddr_in6 in6;
|
1179
|
+
struct hostent *hp;
|
1180
|
+
|
1181
|
+
if (!server || !*server)
|
1182
|
+
server = "0.0.0.0";
|
1183
|
+
|
1184
|
+
memset (&in4, 0, sizeof(in4));
|
1185
|
+
if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) {
|
1186
|
+
if (family)
|
1187
|
+
*family = AF_INET;
|
1188
|
+
if (bind_size)
|
1189
|
+
*bind_size = sizeof(in4);
|
1190
|
+
in4.sin_family = AF_INET;
|
1191
|
+
in4.sin_port = htons (port);
|
1192
|
+
return (struct sockaddr*)&in4;
|
1193
|
+
}
|
1194
|
+
|
1195
|
+
#ifdef OS_UNIX
|
1196
|
+
memset (&in6, 0, sizeof(in6));
|
1197
|
+
if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) {
|
1198
|
+
if (family)
|
1199
|
+
*family = AF_INET6;
|
1200
|
+
if (bind_size)
|
1201
|
+
*bind_size = sizeof(in6);
|
1202
|
+
in6.sin6_family = AF_INET6;
|
1203
|
+
in6.sin6_port = htons (port);
|
1204
|
+
return (struct sockaddr*)&in6;
|
1205
|
+
}
|
1206
|
+
#endif
|
1207
|
+
|
1208
|
+
#ifdef OS_WIN32
|
1209
|
+
// TODO, must complete this branch. Windows doesn't have inet_pton.
|
1210
|
+
// A possible approach is to make a getaddrinfo call with the supplied
|
1211
|
+
// server address, constraining the hints to ipv6 and seeing if we
|
1212
|
+
// get any addresses.
|
1213
|
+
// For the time being, Ipv6 addresses aren't supported on Windows.
|
1214
|
+
#endif
|
1215
|
+
|
1216
|
+
hp = gethostbyname ((char*)server); // Windows requires the cast.
|
1217
|
+
if (hp) {
|
1218
|
+
in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
|
1219
|
+
if (family)
|
1220
|
+
*family = AF_INET;
|
1221
|
+
if (bind_size)
|
1222
|
+
*bind_size = sizeof(in4);
|
1223
|
+
in4.sin_family = AF_INET;
|
1224
|
+
in4.sin_port = htons (port);
|
1225
|
+
return (struct sockaddr*)&in4;
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
return NULL;
|
1229
|
+
}
|
1230
|
+
|
1231
|
+
|
974
1232
|
/*******************************
|
975
1233
|
EventMachine_t::CreateTcpServer
|
976
1234
|
*******************************/
|
@@ -983,15 +1241,22 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
|
|
983
1241
|
* to indicate accepted connections.
|
984
1242
|
*/
|
985
1243
|
|
1244
|
+
|
1245
|
+
int family, bind_size;
|
1246
|
+
struct sockaddr *bind_here = name2address (server, port, &family, &bind_size);
|
1247
|
+
if (!bind_here)
|
1248
|
+
return NULL;
|
1249
|
+
|
986
1250
|
const char *output_binding = NULL;
|
987
1251
|
|
988
|
-
struct sockaddr_in sin;
|
1252
|
+
//struct sockaddr_in sin;
|
989
1253
|
|
990
|
-
int sd_accept = socket (
|
1254
|
+
int sd_accept = socket (family, SOCK_STREAM, 0);
|
991
1255
|
if (sd_accept == INVALID_SOCKET) {
|
992
1256
|
goto fail;
|
993
1257
|
}
|
994
1258
|
|
1259
|
+
/*
|
995
1260
|
memset (&sin, 0, sizeof(sin));
|
996
1261
|
sin.sin_family = AF_INET;
|
997
1262
|
sin.sin_addr.s_addr = INADDR_ANY;
|
@@ -1008,6 +1273,7 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
|
|
1008
1273
|
sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr;
|
1009
1274
|
}
|
1010
1275
|
}
|
1276
|
+
*/
|
1011
1277
|
|
1012
1278
|
{ // set reuseaddr to improve performance on restarts.
|
1013
1279
|
int oval = 1;
|
@@ -1027,7 +1293,8 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
|
|
1027
1293
|
}
|
1028
1294
|
|
1029
1295
|
|
1030
|
-
if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
|
1296
|
+
//if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) {
|
1297
|
+
if (bind (sd_accept, bind_here, bind_size)) {
|
1031
1298
|
//__warning ("binding failed");
|
1032
1299
|
goto fail;
|
1033
1300
|
}
|
@@ -1139,6 +1406,42 @@ void EventMachine_t::Add (EventableDescriptor *ed)
|
|
1139
1406
|
}
|
1140
1407
|
|
1141
1408
|
|
1409
|
+
/*******************************
|
1410
|
+
EventMachine_t::ArmKqueueWriter
|
1411
|
+
*******************************/
|
1412
|
+
|
1413
|
+
void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
|
1414
|
+
{
|
1415
|
+
#ifdef HAVE_KQUEUE
|
1416
|
+
if (bKqueue) {
|
1417
|
+
if (!ed)
|
1418
|
+
throw std::runtime_error ("added bad descriptor");
|
1419
|
+
struct kevent k;
|
1420
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, ed);
|
1421
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
1422
|
+
assert (t == 0);
|
1423
|
+
}
|
1424
|
+
#endif
|
1425
|
+
}
|
1426
|
+
|
1427
|
+
/*******************************
|
1428
|
+
EventMachine_t::ArmKqueueReader
|
1429
|
+
*******************************/
|
1430
|
+
|
1431
|
+
void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
|
1432
|
+
{
|
1433
|
+
#ifdef HAVE_KQUEUE
|
1434
|
+
if (bKqueue) {
|
1435
|
+
if (!ed)
|
1436
|
+
throw std::runtime_error ("added bad descriptor");
|
1437
|
+
struct kevent k;
|
1438
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
|
1439
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
1440
|
+
assert (t == 0);
|
1441
|
+
}
|
1442
|
+
#endif
|
1443
|
+
}
|
1444
|
+
|
1142
1445
|
/**********************************
|
1143
1446
|
EventMachine_t::_AddNewDescriptors
|
1144
1447
|
**********************************/
|
@@ -1173,6 +1476,19 @@ void EventMachine_t::_AddNewDescriptors()
|
|
1173
1476
|
}
|
1174
1477
|
#endif
|
1175
1478
|
|
1479
|
+
#if HAVE_KQUEUE
|
1480
|
+
/*
|
1481
|
+
if (bKqueue) {
|
1482
|
+
// INCOMPLETE. Some descriptors don't want to be readable.
|
1483
|
+
assert (kqfd != -1);
|
1484
|
+
struct kevent k;
|
1485
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
|
1486
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
1487
|
+
assert (t == 0);
|
1488
|
+
}
|
1489
|
+
*/
|
1490
|
+
#endif
|
1491
|
+
|
1176
1492
|
Descriptors.push_back (ed);
|
1177
1493
|
}
|
1178
1494
|
NewDescriptors.clear();
|