eventmachine 0.10.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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();
|