eventmachine 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/ed.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.h 318 2007-05-22 22:06:24Z blackhedd $
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
- virtual void StartTls() {}
67
+ virtual void StartTls() {}
66
68
 
67
- // Properties: return 0/1 to signify T/F, and handle the values
68
- // through arguments.
69
- virtual int GetCommInactivityTimeout (int *value) {return 0;}
70
- virtual int SetCommInactivityTimeout (int *value) {return 0;}
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
- int MySocket;
83
- bool bCloseNow;
84
- bool bCloseAfterWriting;
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
- virtual int GetCommInactivityTimeout (int *value);
122
- virtual int SetCommInactivityTimeout (int *value);
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*, int);
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
- EventMachine_t *MyEventMachine;
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 318 2007-05-22 22:06:24Z blackhedd $
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 us
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
- if (FD_ISSET (LoopBreakerReader, &fdreads))
319
- _ReadLoopBreaker();
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)("", EventMachine_t::LOOPBREAK_SIGNAL, "", 0);
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) ("", TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
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 (this, sd_accept);
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 (this, sd_accept);
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