eventmachine 0.7.2 → 0.8.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/EPOLL +142 -0
- data/RELEASE_NOTES +14 -1
- data/ext/cmain.cpp +70 -1
- data/ext/ed.cpp +180 -65
- data/ext/ed.h +95 -17
- data/ext/em.cpp +400 -20
- data/ext/em.h +25 -2
- data/ext/emwin.cpp +2 -2
- data/ext/epoll.cpp +26 -0
- data/ext/epoll.h +25 -0
- data/ext/eventmachine.h +19 -1
- data/ext/extconf.rb +42 -1
- data/ext/files.cpp +2 -2
- data/ext/files.h +1 -1
- data/ext/pipe.cpp +307 -0
- data/ext/project.h +11 -1
- data/ext/rubymain.cpp +97 -5
- data/lib/em/messages.rb +66 -0
- data/lib/eventmachine.rb +522 -397
- data/lib/eventmachine_version.rb +2 -2
- data/lib/protocols/buftok.rb +6 -0
- data/lib/protocols/httpclient.rb +3 -2
- data/lib/protocols/line_and_text.rb +4 -2
- data/tests/test_epoll.rb +154 -0
- data/tests/test_eventables.rb +6 -3
- data/tests/test_httpclient.rb +3 -1
- data/tests/test_ltp.rb +3 -4
- data/tests/test_next_tick.rb +57 -0
- metadata +11 -3
data/ext/ed.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: ed.h
|
3
|
+
$Id: ed.h 400 2007-07-11 12:10:33Z blackhedd $
|
4
4
|
|
5
5
|
File: ed.h
|
6
6
|
Date: 06Apr06
|
@@ -36,7 +36,7 @@ class EventableDescriptor
|
|
36
36
|
class EventableDescriptor: public Bindable_t
|
37
37
|
{
|
38
38
|
public:
|
39
|
-
EventableDescriptor (int);
|
39
|
+
EventableDescriptor (int, EventMachine_t*);
|
40
40
|
virtual ~EventableDescriptor();
|
41
41
|
|
42
42
|
int GetSocket() {return MySocket;}
|
@@ -57,17 +57,28 @@ class EventableDescriptor: public Bindable_t
|
|
57
57
|
virtual int GetOutboundDataSize() {return 0;}
|
58
58
|
|
59
59
|
void ScheduleClose (bool after_writing);
|
60
|
+
bool IsCloseScheduled();
|
60
61
|
|
61
62
|
void SetEventCallback (void (*cb)(const char*, int, const char*, int));
|
62
63
|
|
63
64
|
virtual bool GetPeername (struct sockaddr*) {return false;}
|
65
|
+
virtual bool GetSubprocessPid (pid_t*) {return false;}
|
64
66
|
|
65
|
-
|
67
|
+
virtual void StartTls() {}
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
// Properties: return 0/1 to signify T/F, and handle the values
|
70
|
+
// through arguments.
|
71
|
+
virtual int GetCommInactivityTimeout (int *value) {return 0;}
|
72
|
+
virtual int SetCommInactivityTimeout (int *value) {return 0;}
|
73
|
+
|
74
|
+
#ifdef HAVE_EPOLL
|
75
|
+
struct epoll_event *GetEpollEvent() { return &EpollEvent; }
|
76
|
+
#endif
|
77
|
+
|
78
|
+
private:
|
79
|
+
bool bCloseNow;
|
80
|
+
bool bCloseAfterWriting;
|
81
|
+
int MySocket;
|
71
82
|
|
72
83
|
protected:
|
73
84
|
enum {
|
@@ -79,13 +90,36 @@ class EventableDescriptor: public Bindable_t
|
|
79
90
|
time_t CreatedAt;
|
80
91
|
time_t LastRead;
|
81
92
|
time_t LastWritten;
|
82
|
-
|
83
|
-
|
84
|
-
|
93
|
+
bool bCallbackUnbind;
|
94
|
+
|
95
|
+
#ifdef HAVE_EPOLL
|
96
|
+
struct epoll_event EpollEvent;
|
97
|
+
#endif
|
98
|
+
|
99
|
+
EventMachine_t *MyEventMachine;
|
85
100
|
};
|
86
101
|
|
87
102
|
|
88
103
|
|
104
|
+
/*************************
|
105
|
+
class LoopbreakDescriptor
|
106
|
+
*************************/
|
107
|
+
|
108
|
+
class LoopbreakDescriptor: public EventableDescriptor
|
109
|
+
{
|
110
|
+
public:
|
111
|
+
LoopbreakDescriptor (int, EventMachine_t*);
|
112
|
+
virtual ~LoopbreakDescriptor() {}
|
113
|
+
|
114
|
+
virtual void Read();
|
115
|
+
virtual void Write();
|
116
|
+
virtual void Heartbeat() {}
|
117
|
+
|
118
|
+
virtual bool SelectForRead() {return true;}
|
119
|
+
virtual bool SelectForWrite() {return false;}
|
120
|
+
};
|
121
|
+
|
122
|
+
|
89
123
|
/**************************
|
90
124
|
class ConnectionDescriptor
|
91
125
|
**************************/
|
@@ -93,7 +127,7 @@ class ConnectionDescriptor
|
|
93
127
|
class ConnectionDescriptor: public EventableDescriptor
|
94
128
|
{
|
95
129
|
public:
|
96
|
-
ConnectionDescriptor (int);
|
130
|
+
ConnectionDescriptor (int, EventMachine_t*);
|
97
131
|
virtual ~ConnectionDescriptor();
|
98
132
|
|
99
133
|
static int SendDataToConnection (const char*, const char*, int);
|
@@ -118,8 +152,8 @@ class ConnectionDescriptor: public EventableDescriptor
|
|
118
152
|
|
119
153
|
virtual bool GetPeername (struct sockaddr*);
|
120
154
|
|
121
|
-
|
122
|
-
|
155
|
+
virtual int GetCommInactivityTimeout (int *value);
|
156
|
+
virtual int SetCommInactivityTimeout (int *value);
|
123
157
|
|
124
158
|
protected:
|
125
159
|
struct OutboundPage {
|
@@ -161,7 +195,7 @@ class DatagramDescriptor
|
|
161
195
|
class DatagramDescriptor: public EventableDescriptor
|
162
196
|
{
|
163
197
|
public:
|
164
|
-
DatagramDescriptor (int);
|
198
|
+
DatagramDescriptor (int, EventMachine_t*);
|
165
199
|
virtual ~DatagramDescriptor();
|
166
200
|
|
167
201
|
virtual void Read();
|
@@ -212,7 +246,7 @@ class AcceptorDescriptor
|
|
212
246
|
class AcceptorDescriptor: public EventableDescriptor
|
213
247
|
{
|
214
248
|
public:
|
215
|
-
AcceptorDescriptor (EventMachine_t
|
249
|
+
AcceptorDescriptor (int, EventMachine_t*);
|
216
250
|
virtual ~AcceptorDescriptor();
|
217
251
|
|
218
252
|
virtual void Read();
|
@@ -221,11 +255,55 @@ class AcceptorDescriptor: public EventableDescriptor
|
|
221
255
|
|
222
256
|
virtual bool SelectForRead() {return true;}
|
223
257
|
virtual bool SelectForWrite() {return false;}
|
258
|
+
};
|
259
|
+
|
260
|
+
/********************
|
261
|
+
class PipeDescriptor
|
262
|
+
********************/
|
263
|
+
|
264
|
+
#ifdef OS_UNIX
|
265
|
+
class PipeDescriptor: public EventableDescriptor
|
266
|
+
{
|
267
|
+
public:
|
268
|
+
PipeDescriptor (FILE*, pid_t, EventMachine_t*);
|
269
|
+
virtual ~PipeDescriptor();
|
270
|
+
|
271
|
+
virtual void Read();
|
272
|
+
virtual void Write();
|
273
|
+
virtual void Heartbeat();
|
274
|
+
|
275
|
+
virtual bool SelectForRead();
|
276
|
+
virtual bool SelectForWrite();
|
277
|
+
|
278
|
+
int SendOutboundData (const char*, int);
|
279
|
+
virtual int GetOutboundDataSize() {return OutboundDataSize;}
|
280
|
+
|
281
|
+
virtual bool GetSubprocessPid (pid_t*);
|
224
282
|
|
225
283
|
protected:
|
226
|
-
|
227
|
-
}
|
284
|
+
struct OutboundPage {
|
285
|
+
OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
|
286
|
+
void Free() {if (Buffer) free ((char*)Buffer); }
|
287
|
+
const char *Buffer;
|
288
|
+
int Length;
|
289
|
+
int Offset;
|
290
|
+
};
|
291
|
+
|
292
|
+
protected:
|
293
|
+
bool bReadAttemptedAfterClose;
|
294
|
+
time_t LastIo;
|
295
|
+
int InactivityTimeout;
|
296
|
+
//FILE *MyStream;
|
297
|
+
|
298
|
+
deque<OutboundPage> OutboundPages;
|
299
|
+
int OutboundDataSize;
|
228
300
|
|
301
|
+
pid_t SubprocessPid;
|
302
|
+
|
303
|
+
private:
|
304
|
+
void _DispatchInboundData (const char *buffer, int size);
|
305
|
+
};
|
306
|
+
#endif // OS_UNIX
|
229
307
|
|
230
308
|
|
231
309
|
#endif // __EventableDescriptor__H_
|
data/ext/em.cpp
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*****************************************************************************
|
2
2
|
|
3
|
-
$Id: em.cpp
|
3
|
+
$Id: em.cpp 386 2007-06-20 00:08:19Z blackhedd $
|
4
4
|
|
5
5
|
File: em.cpp
|
6
6
|
Date: 06Apr06
|
@@ -42,7 +42,9 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const c
|
|
42
42
|
EventCallback (event_callback),
|
43
43
|
NextHeartbeatTime (0),
|
44
44
|
LoopBreakerReader (-1),
|
45
|
-
LoopBreakerWriter (-1)
|
45
|
+
LoopBreakerWriter (-1),
|
46
|
+
bEpoll (false),
|
47
|
+
epfd (-1)
|
46
48
|
{
|
47
49
|
// Default time-slice is just smaller than one hundred mills.
|
48
50
|
Quantum.tv_sec = 0;
|
@@ -53,6 +55,17 @@ EventMachine_t::EventMachine_t (void (*event_callback)(const char*, int, const c
|
|
53
55
|
// objects before we start running.
|
54
56
|
gCurrentLoopTime = time(NULL);
|
55
57
|
|
58
|
+
/* We initialize the network library here (only on Windows of course)
|
59
|
+
* and initialize "loop breakers." Our destructor also does some network-level
|
60
|
+
* cleanup. There's thus an implicit assumption that any given instance of EventMachine_t
|
61
|
+
* will only call ::Run once. Is that a good assumption? Should we move some of these
|
62
|
+
* inits and de-inits into ::Run?
|
63
|
+
*/
|
64
|
+
#ifdef OS_WIN32
|
65
|
+
WSADATA w;
|
66
|
+
WSAStartup (MAKEWORD (1, 1), &w);
|
67
|
+
#endif
|
68
|
+
|
56
69
|
_InitializeLoopBreaker();
|
57
70
|
}
|
58
71
|
|
@@ -72,9 +85,30 @@ EventMachine_t::~EventMachine_t()
|
|
72
85
|
|
73
86
|
close (LoopBreakerReader);
|
74
87
|
close (LoopBreakerWriter);
|
88
|
+
|
89
|
+
if (epfd != -1)
|
90
|
+
close (epfd);
|
75
91
|
}
|
76
92
|
|
77
93
|
|
94
|
+
/*************************
|
95
|
+
EventMachine_t::_UseEpoll
|
96
|
+
*************************/
|
97
|
+
|
98
|
+
void EventMachine_t::_UseEpoll()
|
99
|
+
{
|
100
|
+
/* Temporary.
|
101
|
+
* Use an internal flag to switch in epoll-based functionality until we determine
|
102
|
+
* how it should be integrated properly and the extent of the required changes.
|
103
|
+
* A permanent solution needs to allow the integration of additional technologies,
|
104
|
+
* like kqueue and Solaris's events.
|
105
|
+
*/
|
106
|
+
|
107
|
+
#ifdef HAVE_EPOLL
|
108
|
+
bEpoll = true;
|
109
|
+
#endif
|
110
|
+
}
|
111
|
+
|
78
112
|
|
79
113
|
/****************************
|
80
114
|
EventMachine_t::ScheduleHalt
|
@@ -127,9 +161,15 @@ void EventMachine_t::SetuidString (const char *username)
|
|
127
161
|
/* This method takes a caller-supplied username and tries to setuid
|
128
162
|
* to that user. There is no meaningful implementation (and no error)
|
129
163
|
* on Windows. On Unix, a failure to setuid the caller-supplied string
|
130
|
-
* causes a fatal abort, because presumably the program is calling
|
164
|
+
* causes a fatal abort, because presumably the program is calling here
|
131
165
|
* in order to fulfill a security requirement. If we fail silently,
|
132
166
|
* the user may continue to run with too much privilege.
|
167
|
+
*
|
168
|
+
* TODO, we need to decide on and document a way of generating C++ level errors
|
169
|
+
* that can be wrapped in documented Ruby exceptions, so users can catch
|
170
|
+
* and handle them. And distinguish it from errors that we WON'T let the Ruby
|
171
|
+
* user catch (like security-violations and resource-overallocation).
|
172
|
+
* A setuid failure here would be in the latter category.
|
133
173
|
*/
|
134
174
|
|
135
175
|
#ifdef OS_UNIX
|
@@ -148,6 +188,34 @@ void EventMachine_t::SetuidString (const char *username)
|
|
148
188
|
}
|
149
189
|
|
150
190
|
|
191
|
+
/****************************************
|
192
|
+
(STATIC) EventMachine_t::SetRlimitNofile
|
193
|
+
****************************************/
|
194
|
+
|
195
|
+
int EventMachine_t::SetRlimitNofile (int nofiles)
|
196
|
+
{
|
197
|
+
#ifdef OS_UNIX
|
198
|
+
struct rlimit rlim;
|
199
|
+
getrlimit (RLIMIT_NOFILE, &rlim);
|
200
|
+
if (nofiles >= 0) {
|
201
|
+
rlim.rlim_cur = nofiles;
|
202
|
+
if (nofiles > rlim.rlim_max)
|
203
|
+
rlim.rlim_max = nofiles;
|
204
|
+
setrlimit (RLIMIT_NOFILE, &rlim);
|
205
|
+
// ignore the error return, for now at least.
|
206
|
+
// TODO, emit an error message someday when we have proper debug levels.
|
207
|
+
}
|
208
|
+
getrlimit (RLIMIT_NOFILE, &rlim);
|
209
|
+
return rlim.rlim_cur;
|
210
|
+
#endif
|
211
|
+
|
212
|
+
#ifdef OS_WIN32
|
213
|
+
// No meaningful implementation on Windows.
|
214
|
+
return 0;
|
215
|
+
#endif
|
216
|
+
}
|
217
|
+
|
218
|
+
|
151
219
|
/*********************************
|
152
220
|
EventMachine_t::SignalLoopBreaker
|
153
221
|
*********************************/
|
@@ -219,16 +287,41 @@ EventMachine_t::Run
|
|
219
287
|
void EventMachine_t::Run()
|
220
288
|
{
|
221
289
|
#ifdef OS_WIN32
|
222
|
-
WSADATA w;
|
223
|
-
WSAStartup (MAKEWORD (1, 1), &w);
|
224
290
|
HookControlC (true);
|
225
291
|
#endif
|
226
292
|
|
293
|
+
#ifdef HAVE_EPOLL
|
294
|
+
if (bEpoll) {
|
295
|
+
epfd = epoll_create (MaxEpollDescriptors);
|
296
|
+
if (epfd == -1) {
|
297
|
+
char buf[200];
|
298
|
+
snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno));
|
299
|
+
throw std::runtime_error (buf);
|
300
|
+
}
|
301
|
+
int cloexec = fcntl (epfd, F_GETFD, 0);
|
302
|
+
assert (cloexec >= 0);
|
303
|
+
cloexec |= FD_CLOEXEC;
|
304
|
+
fcntl (epfd, F_SETFD, cloexec);
|
305
|
+
|
306
|
+
assert (LoopBreakerReader >= 0);
|
307
|
+
LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
|
308
|
+
assert (ld);
|
309
|
+
Add (ld);
|
310
|
+
}
|
311
|
+
#endif
|
312
|
+
|
227
313
|
while (true) {
|
228
314
|
gCurrentLoopTime = time(NULL);
|
229
315
|
if (!_RunTimers())
|
230
316
|
break;
|
317
|
+
|
318
|
+
/* _Add must precede _Modify because the same descriptor might
|
319
|
+
* be on both lists during the same pass through the machine,
|
320
|
+
* and to modify a descriptor before adding it would fail.
|
321
|
+
*/
|
231
322
|
_AddNewDescriptors();
|
323
|
+
_ModifyDescriptors();
|
324
|
+
|
232
325
|
if (!_RunOnce())
|
233
326
|
break;
|
234
327
|
if (gTerminateSignalReceived)
|
@@ -241,12 +334,123 @@ void EventMachine_t::Run()
|
|
241
334
|
}
|
242
335
|
|
243
336
|
|
244
|
-
|
245
337
|
/************************
|
246
338
|
EventMachine_t::_RunOnce
|
247
339
|
************************/
|
248
340
|
|
249
341
|
bool EventMachine_t::_RunOnce()
|
342
|
+
{
|
343
|
+
if (bEpoll)
|
344
|
+
return _RunEpollOnce();
|
345
|
+
else
|
346
|
+
return _RunSelectOnce();
|
347
|
+
}
|
348
|
+
|
349
|
+
|
350
|
+
|
351
|
+
/*****************************
|
352
|
+
EventMachine_t::_RunEpollOnce
|
353
|
+
*****************************/
|
354
|
+
|
355
|
+
bool EventMachine_t::_RunEpollOnce()
|
356
|
+
{
|
357
|
+
#ifdef HAVE_EPOLL
|
358
|
+
assert (epfd != -1);
|
359
|
+
struct epoll_event ev [MaxEpollDescriptors];
|
360
|
+
int s = epoll_wait (epfd, ev, MaxEpollDescriptors, 10);
|
361
|
+
if (s > 0) {
|
362
|
+
for (int i=0; i < s; i++) {
|
363
|
+
EventableDescriptor *ed = (EventableDescriptor*) ev[i].data.ptr;
|
364
|
+
|
365
|
+
if (ev[i].events & (EPOLLERR | EPOLLHUP))
|
366
|
+
ed->ScheduleClose (false);
|
367
|
+
if (ev[i].events & EPOLLIN)
|
368
|
+
ed->Read();
|
369
|
+
if (ev[i].events & EPOLLOUT) {
|
370
|
+
ed->Write();
|
371
|
+
epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent());
|
372
|
+
// Ignoring return value
|
373
|
+
}
|
374
|
+
}
|
375
|
+
}
|
376
|
+
else if (s < 0) {
|
377
|
+
// epoll_wait can fail on error in a handful of ways.
|
378
|
+
// If this happens, then wait for a little while to avoid busy-looping.
|
379
|
+
// If the error was EINTR, we probably caught SIGCHLD or something,
|
380
|
+
// so keep the wait short.
|
381
|
+
timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
|
382
|
+
EmSelect (0, NULL, NULL, NULL, &tv);
|
383
|
+
}
|
384
|
+
|
385
|
+
{ // cleanup dying sockets
|
386
|
+
// vector::pop_back works in constant time.
|
387
|
+
// TODO, rip this out and only delete the descriptors we know have died,
|
388
|
+
// rather than traversing the whole list.
|
389
|
+
int i, j;
|
390
|
+
int nSockets = Descriptors.size();
|
391
|
+
for (i=0, j=0; i < nSockets; i++) {
|
392
|
+
EventableDescriptor *ed = Descriptors[i];
|
393
|
+
assert (ed);
|
394
|
+
if (ed->ShouldDelete()) {
|
395
|
+
assert (bEpoll); // wouldn't be in this method otherwise.
|
396
|
+
assert (epfd != -1);
|
397
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
398
|
+
// ENOENT is not an error because the socket may be already closed when we get here.
|
399
|
+
if (e && (e != ENOENT)) {
|
400
|
+
char buf [200];
|
401
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
402
|
+
throw std::runtime_error (buf);
|
403
|
+
}
|
404
|
+
|
405
|
+
ModifiedDescriptors.erase (ed);
|
406
|
+
delete ed;
|
407
|
+
}
|
408
|
+
else
|
409
|
+
Descriptors [j++] = ed;
|
410
|
+
}
|
411
|
+
while ((size_t)j < Descriptors.size())
|
412
|
+
Descriptors.pop_back();
|
413
|
+
|
414
|
+
}
|
415
|
+
|
416
|
+
// TODO, heartbeats.
|
417
|
+
|
418
|
+
timeval tv = {0,0};
|
419
|
+
EmSelect (0, NULL, NULL, NULL, &tv);
|
420
|
+
|
421
|
+
return true;
|
422
|
+
#else
|
423
|
+
throw std::runtime_error ("epoll is not implemented on this platform");
|
424
|
+
#endif
|
425
|
+
}
|
426
|
+
|
427
|
+
|
428
|
+
/*********************************
|
429
|
+
EventMachine_t::_ModifyEpollEvent
|
430
|
+
*********************************/
|
431
|
+
|
432
|
+
void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
|
433
|
+
{
|
434
|
+
#ifdef HAVE_EPOLL
|
435
|
+
if (bEpoll) {
|
436
|
+
assert (epfd != -1);
|
437
|
+
assert (ed);
|
438
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent());
|
439
|
+
if (e) {
|
440
|
+
char buf [200];
|
441
|
+
snprintf (buf, sizeof(buf)-1, "unable to modify epoll event: %s", strerror(errno));
|
442
|
+
throw std::runtime_error (buf);
|
443
|
+
}
|
444
|
+
}
|
445
|
+
#endif
|
446
|
+
}
|
447
|
+
|
448
|
+
|
449
|
+
/******************************
|
450
|
+
EventMachine_t::_RunSelectOnce
|
451
|
+
******************************/
|
452
|
+
|
453
|
+
bool EventMachine_t::_RunSelectOnce()
|
250
454
|
{
|
251
455
|
// Crank the event machine once.
|
252
456
|
// If there are no descriptors to process, then sleep
|
@@ -315,9 +519,16 @@ bool EventMachine_t::_RunOnce()
|
|
315
519
|
timeval tv = Quantum;
|
316
520
|
int s = EmSelect (maxsocket+1, &fdreads, &fdwrites, NULL, &tv);
|
317
521
|
if (s > 0) {
|
318
|
-
|
319
|
-
|
320
|
-
|
522
|
+
/* Changed 01Jun07. We used to handle the Loop-breaker right here.
|
523
|
+
* Now we do it AFTER all the regular descriptors. There's an
|
524
|
+
* incredibly important and subtle reason for this. Code on
|
525
|
+
* loop breakers is sometimes used to cause the reactor core to
|
526
|
+
* cycle (for example, to allow outbound network buffers to drain).
|
527
|
+
* If a loop-breaker handler reschedules itself (say, after determining
|
528
|
+
* that the write buffers are still too full), then it will execute
|
529
|
+
* IMMEDIATELY if _ReadLoopBreaker is done here instead of after
|
530
|
+
* the other descriptors are processed. That defeats the whole purpose.
|
531
|
+
*/
|
321
532
|
for (i=0; i < Descriptors.size(); i++) {
|
322
533
|
EventableDescriptor *ed = Descriptors[i];
|
323
534
|
assert (ed);
|
@@ -329,6 +540,9 @@ bool EventMachine_t::_RunOnce()
|
|
329
540
|
if (FD_ISSET (sd, &fdreads))
|
330
541
|
ed->Read();
|
331
542
|
}
|
543
|
+
|
544
|
+
if (FD_ISSET (LoopBreakerReader, &fdreads))
|
545
|
+
_ReadLoopBreaker();
|
332
546
|
}
|
333
547
|
else if (s < 0) {
|
334
548
|
// select can fail on error in a handful of ways.
|
@@ -387,7 +601,7 @@ void EventMachine_t::_ReadLoopBreaker()
|
|
387
601
|
char buffer [1024];
|
388
602
|
read (LoopBreakerReader, buffer, sizeof(buffer));
|
389
603
|
if (EventCallback)
|
390
|
-
(*EventCallback)("",
|
604
|
+
(*EventCallback)("", EM_LOOPBREAK_SIGNAL, "", 0);
|
391
605
|
}
|
392
606
|
|
393
607
|
|
@@ -425,7 +639,7 @@ bool EventMachine_t::_RunTimers()
|
|
425
639
|
if (i->first > now)
|
426
640
|
break;
|
427
641
|
if (EventCallback)
|
428
|
-
(*EventCallback) ("",
|
642
|
+
(*EventCallback) ("", EM_TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
|
429
643
|
Timers.erase (i);
|
430
644
|
}
|
431
645
|
return true;
|
@@ -556,7 +770,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
556
770
|
* some needed initialization will happen in the ConnectionDescriptor.
|
557
771
|
* (To wit, the ConnectionCompleted event gets sent to the client.)
|
558
772
|
*/
|
559
|
-
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
773
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
560
774
|
if (!cd)
|
561
775
|
throw std::runtime_error ("no connection allocated");
|
562
776
|
cd->SetConnectPending (true);
|
@@ -574,7 +788,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
574
788
|
// Here, there's no disposition.
|
575
789
|
// Put the connection on the stack and wait for it to complete
|
576
790
|
// or time out.
|
577
|
-
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
791
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
578
792
|
if (!cd)
|
579
793
|
throw std::runtime_error ("no connection allocated");
|
580
794
|
cd->SetConnectPending (true);
|
@@ -594,7 +808,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
594
808
|
* revised behavior, in case it causes problems like making it hard
|
595
809
|
* for people to know that a failure occurred.
|
596
810
|
*/
|
597
|
-
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
811
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
598
812
|
if (!cd)
|
599
813
|
throw std::runtime_error ("no connection allocated");
|
600
814
|
cd->ScheduleClose (false);
|
@@ -621,7 +835,7 @@ const char *EventMachine_t::ConnectToServer (const char *server, int port)
|
|
621
835
|
// such stuff at this point.
|
622
836
|
// Put the connection on the stack and wait for it to complete
|
623
837
|
// or time out.
|
624
|
-
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
838
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
625
839
|
if (!cd)
|
626
840
|
throw std::runtime_error ("no connection allocated");
|
627
841
|
cd->SetConnectPending (true);
|
@@ -698,7 +912,7 @@ const char *EventMachine_t::ConnectToUnixServer (const char *server)
|
|
698
912
|
// Observe, even though we know the connection status is connect-success,
|
699
913
|
// we still set the "pending" flag, so some needed initializations take
|
700
914
|
// place.
|
701
|
-
ConnectionDescriptor *cd = new ConnectionDescriptor (fd);
|
915
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
|
702
916
|
if (!cd)
|
703
917
|
throw std::runtime_error ("no connection allocated");
|
704
918
|
cd->SetConnectPending (true);
|
@@ -790,7 +1004,7 @@ const char *EventMachine_t::CreateTcpServer (const char *server, int port)
|
|
790
1004
|
}
|
791
1005
|
|
792
1006
|
{ // Looking good.
|
793
|
-
AcceptorDescriptor *ad = new AcceptorDescriptor (
|
1007
|
+
AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this);
|
794
1008
|
if (!ad)
|
795
1009
|
throw std::runtime_error ("unable to allocate acceptor");
|
796
1010
|
Add (ad);
|
@@ -851,7 +1065,7 @@ const char *EventMachine_t::OpenDatagramSocket (const char *address, int port)
|
|
851
1065
|
goto fail;
|
852
1066
|
|
853
1067
|
{ // Looking good.
|
854
|
-
DatagramDescriptor *ds = new DatagramDescriptor (sd);
|
1068
|
+
DatagramDescriptor *ds = new DatagramDescriptor (sd, this);
|
855
1069
|
if (!ds)
|
856
1070
|
throw std::runtime_error ("unable to allocate datagram-socket");
|
857
1071
|
Add (ds);
|
@@ -902,12 +1116,75 @@ void EventMachine_t::_AddNewDescriptors()
|
|
902
1116
|
EventableDescriptor *ed = NewDescriptors[i];
|
903
1117
|
if (ed == NULL)
|
904
1118
|
throw std::runtime_error ("adding bad descriptor");
|
1119
|
+
|
1120
|
+
#if HAVE_EPOLL
|
1121
|
+
if (bEpoll) {
|
1122
|
+
assert (epfd != -1);
|
1123
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_ADD, ed->GetSocket(), ed->GetEpollEvent());
|
1124
|
+
if (e) {
|
1125
|
+
char buf [200];
|
1126
|
+
snprintf (buf, sizeof(buf)-1, "unable to add new descriptor: %s", strerror(errno));
|
1127
|
+
throw std::runtime_error (buf);
|
1128
|
+
}
|
1129
|
+
}
|
1130
|
+
#endif
|
1131
|
+
|
905
1132
|
Descriptors.push_back (ed);
|
906
1133
|
}
|
907
1134
|
NewDescriptors.clear();
|
908
1135
|
}
|
909
1136
|
|
910
1137
|
|
1138
|
+
/**********************************
|
1139
|
+
EventMachine_t::_ModifyDescriptors
|
1140
|
+
**********************************/
|
1141
|
+
|
1142
|
+
void EventMachine_t::_ModifyDescriptors()
|
1143
|
+
{
|
1144
|
+
/* For implementations which don't level check every descriptor on
|
1145
|
+
* every pass through the machine, as select does.
|
1146
|
+
* If we're not selecting, then descriptors need a way to signal to the
|
1147
|
+
* machine that their readable or writable status has changed.
|
1148
|
+
* That's what the ::Modify call is for. We do it this way to avoid
|
1149
|
+
* modifying descriptors during the loop traversal, where it can easily
|
1150
|
+
* happen that an object (like a UDP socket) gets data written on it by
|
1151
|
+
* the application during #post_init. That would take place BEFORE the
|
1152
|
+
* descriptor even gets added to the epoll descriptor, so the modify
|
1153
|
+
* operation will crash messily.
|
1154
|
+
* Another really messy possibility is for a descriptor to put itself
|
1155
|
+
* on the Modified list, and then get deleted before we get here.
|
1156
|
+
* Remember, deletes happen after the I/O traversal and before the
|
1157
|
+
* next pass through here. So we have to make sure when we delete a
|
1158
|
+
* descriptor to remove it from the Modified list.
|
1159
|
+
*/
|
1160
|
+
|
1161
|
+
#ifdef HAVE_EPOLL
|
1162
|
+
if (bEpoll) {
|
1163
|
+
set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
|
1164
|
+
while (i != ModifiedDescriptors.end()) {
|
1165
|
+
assert (*i);
|
1166
|
+
_ModifyEpollEvent (*i);
|
1167
|
+
++i;
|
1168
|
+
}
|
1169
|
+
}
|
1170
|
+
#endif
|
1171
|
+
|
1172
|
+
ModifiedDescriptors.clear();
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
|
1176
|
+
/**********************
|
1177
|
+
EventMachine_t::Modify
|
1178
|
+
**********************/
|
1179
|
+
|
1180
|
+
void EventMachine_t::Modify (EventableDescriptor *ed)
|
1181
|
+
{
|
1182
|
+
if (!ed)
|
1183
|
+
throw std::runtime_error ("modified bad descriptor");
|
1184
|
+
ModifiedDescriptors.insert (ed);
|
1185
|
+
}
|
1186
|
+
|
1187
|
+
|
911
1188
|
/***********************************
|
912
1189
|
EventMachine_t::_OpenFileForWriting
|
913
1190
|
***********************************/
|
@@ -924,7 +1201,7 @@ const char *EventMachine_t::_OpenFileForWriting (const char *filename)
|
|
924
1201
|
|
925
1202
|
int fd = open (filename, O_CREAT|O_TRUNC|O_WRONLY|O_NONBLOCK, 0644);
|
926
1203
|
|
927
|
-
FileStreamDescriptor *fsd = new FileStreamDescriptor (fd);
|
1204
|
+
FileStreamDescriptor *fsd = new FileStreamDescriptor (fd, this);
|
928
1205
|
if (!fsd)
|
929
1206
|
throw std::runtime_error ("no file-stream allocated");
|
930
1207
|
Add (fsd);
|
@@ -1001,7 +1278,7 @@ const char *EventMachine_t::CreateUnixDomainServer (const char *filename)
|
|
1001
1278
|
}
|
1002
1279
|
|
1003
1280
|
{ // Looking good.
|
1004
|
-
AcceptorDescriptor *ad = new AcceptorDescriptor (
|
1281
|
+
AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this);
|
1005
1282
|
if (!ad)
|
1006
1283
|
throw std::runtime_error ("unable to allocate acceptor");
|
1007
1284
|
Add (ad);
|
@@ -1018,5 +1295,108 @@ const char *EventMachine_t::CreateUnixDomainServer (const char *filename)
|
|
1018
1295
|
}
|
1019
1296
|
|
1020
1297
|
|
1298
|
+
/*********************
|
1299
|
+
EventMachine_t::Popen
|
1300
|
+
*********************/
|
1301
|
+
#if OBSOLETE
|
1302
|
+
const char *EventMachine_t::Popen (const char *cmd, const char *mode)
|
1303
|
+
{
|
1304
|
+
#ifdef OS_WIN32
|
1305
|
+
throw std::runtime_error ("popen is currently unavailable on this platform");
|
1306
|
+
#endif
|
1307
|
+
|
1308
|
+
// The whole rest of this function is only compiled on Unix systems.
|
1309
|
+
// Eventually we need this functionality (or a full-duplex equivalent) on Windows.
|
1310
|
+
#ifdef OS_UNIX
|
1311
|
+
const char *output_binding = NULL;
|
1312
|
+
|
1313
|
+
FILE *fp = popen (cmd, mode);
|
1314
|
+
if (!fp)
|
1315
|
+
return NULL;
|
1316
|
+
|
1317
|
+
// From here, all early returns must pclose the stream.
|
1318
|
+
|
1319
|
+
// According to the pipe(2) manpage, descriptors returned from pipe have both
|
1320
|
+
// CLOEXEC and NONBLOCK clear. Do NOT set CLOEXEC. DO set nonblocking.
|
1321
|
+
if (!SetSocketNonblocking (fileno (fp))) {
|
1322
|
+
pclose (fp);
|
1323
|
+
return NULL;
|
1324
|
+
}
|
1325
|
+
|
1326
|
+
{ // Looking good.
|
1327
|
+
PipeDescriptor *pd = new PipeDescriptor (fp, this);
|
1328
|
+
if (!pd)
|
1329
|
+
throw std::runtime_error ("unable to allocate pipe");
|
1330
|
+
Add (pd);
|
1331
|
+
output_binding = pd->GetBinding().c_str();
|
1332
|
+
}
|
1333
|
+
|
1334
|
+
return output_binding;
|
1335
|
+
#endif
|
1336
|
+
}
|
1337
|
+
#endif // OBSOLETE
|
1338
|
+
|
1339
|
+
/**************************
|
1340
|
+
EventMachine_t::Socketpair
|
1341
|
+
**************************/
|
1342
|
+
|
1343
|
+
const char *EventMachine_t::Socketpair (char * const*cmd_strings)
|
1344
|
+
{
|
1345
|
+
#ifdef OS_WIN32
|
1346
|
+
throw std::runtime_error ("socketpair is currently unavailable on this platform");
|
1347
|
+
#endif
|
1348
|
+
|
1349
|
+
// The whole rest of this function is only compiled on Unix systems.
|
1350
|
+
// Eventually we need this functionality (or a full-duplex equivalent) on Windows.
|
1351
|
+
#ifdef OS_UNIX
|
1352
|
+
// Make sure the incoming array of command strings is sane.
|
1353
|
+
if (!cmd_strings)
|
1354
|
+
return NULL;
|
1355
|
+
int j;
|
1356
|
+
for (j=0; j < 100 && cmd_strings[j]; j++)
|
1357
|
+
;
|
1358
|
+
if ((j==0) || (j==100))
|
1359
|
+
return NULL;
|
1360
|
+
|
1361
|
+
const char *output_binding = NULL;
|
1362
|
+
|
1363
|
+
int sv[2];
|
1364
|
+
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
|
1365
|
+
return NULL;
|
1366
|
+
// from here, all early returns must close the pair of sockets.
|
1367
|
+
|
1368
|
+
// Set the socketpair nonblocking. Obviously DON'T set CLOEXEC.
|
1369
|
+
if (!SetSocketNonblocking (sv[0]) || !SetSocketNonblocking (sv[1])) {
|
1370
|
+
close (sv[0]);
|
1371
|
+
close (sv[1]);
|
1372
|
+
return NULL;
|
1373
|
+
}
|
1374
|
+
|
1375
|
+
pid_t f = fork();
|
1376
|
+
if (f > 0) {
|
1377
|
+
close (sv[1]);
|
1378
|
+
PipeDescriptor *pd = new PipeDescriptor (fdopen(sv[0], "r+"), f, this);
|
1379
|
+
if (!pd)
|
1380
|
+
throw std::runtime_error ("unable to allocate pipe");
|
1381
|
+
Add (pd);
|
1382
|
+
output_binding = pd->GetBinding().c_str();
|
1383
|
+
}
|
1384
|
+
else if (f == 0) {
|
1385
|
+
close (sv[0]);
|
1386
|
+
dup2 (sv[1], STDIN_FILENO);
|
1387
|
+
close (sv[1]);
|
1388
|
+
dup2 (STDIN_FILENO, STDOUT_FILENO);
|
1389
|
+
execvp (cmd_strings[0], cmd_strings+1);
|
1390
|
+
exit (-1); // end the child process if the exec doesn't work.
|
1391
|
+
}
|
1392
|
+
else
|
1393
|
+
throw std::runtime_error ("no fork");
|
1394
|
+
|
1395
|
+
return output_binding;
|
1396
|
+
#endif
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
|
1400
|
+
|
1021
1401
|
//#endif // OS_UNIX
|
1022
1402
|
|