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/EPOLL ADDED
@@ -0,0 +1,142 @@
1
+ EventMachine now supports epoll, bringing large increases in performance and scalability to Ruby programs.
2
+
3
+ Epoll(7) is a alternative mechanism for multiplexed I/O that is available in Linux 2.6 kernels.
4
+ It features significantly greater performance than the standard select(2) mechanism, when used in
5
+ applications that require very large numbers of open I/O descriptors.
6
+
7
+ EventMachine has always used select(2) because its behavior is well standardized and broadly supported.
8
+ But select becomes unreasonably slow when a program has a
9
+ very large number of file descriptors or sockets. Ruby's version of select hardcodes a limit
10
+ of 1024 descriptors per process, but heavily loaded processes will start to show performance
11
+ degradation even after only a few hundred descriptors are in use.
12
+
13
+ Epoll is an extended version of the poll(2) call, and it solves the problems with select. Programs
14
+ based on epoll can easily scale past Ruby's 1024-descriptor limit, potentially to tens of thousands
15
+ of connectors, with no significant impact on performance.
16
+
17
+ (Another alternative which is very similar to epoll in principle is kqueue, supplied on BSD and its
18
+ variants.)
19
+
20
+
21
+
22
+ This note shows you how to use epoll in your programs.
23
+
24
+ === Compiling EventMachine to use epoll.
25
+
26
+ You don't have to do anything to get epoll support in EventMachine.
27
+ When you compile EventMachine on a platform that supports epoll, EM will
28
+ automatically generate a Makefile that includes epoll. (At this writing, this will only work
29
+ on Linux 2.6 kernels.) If you compile EM on a platform without epoll, then epoll support will
30
+ be omitted from the Makefile, and EM will work just as it always has.
31
+
32
+ === Using epoll in your programs.
33
+
34
+ First, you need to tell EventMachine to use epoll instead of select (but see below, as this requirement
35
+ will be removed in a future EventMachine version). Second, you need to prepare your program to use
36
+ more than 1024 descriptors, an operation that generally requires superuser privileges. Third, you will probably
37
+ want your process to drop the superuser privileges after you increase your process's descriptor limit.
38
+
39
+ === Using EventMachine#epoll
40
+
41
+ Call the method EventMachine#epoll anytime before you call EventMachine#run, and your program will
42
+ automatically use epoll, if available. It's safe to call EventMachine#epoll on any platform because
43
+
44
+ it compiles to a no-op on platforms that don't support epoll.
45
+
46
+ require 'rubygems'
47
+ require 'eventmachine'
48
+
49
+ EM.epoll
50
+ EM.run {
51
+ ...
52
+ }
53
+
54
+
55
+ EventMachine#epoll was included in this initial release only to avoid changing the behavior of existing
56
+ programs. However, it's expected that a future release of EM will convert EventMachine#epoll to a no-op,
57
+ and run epoll by default on platforms that support it.
58
+
59
+ === Using EventMachine#set_descriptor_table_size
60
+
61
+ In Linux (as in every Unix-like platform), every process has a internal table that determines the maximum
62
+ number of file and socket descriptors you may have open at any given time. The size of this table is
63
+ generally fixed at 1024, although it may be increased within certain system-defined hard and soft limits.
64
+
65
+ If you want your EventMachine program to support more than 1024 total descriptors, you must use
66
+ EventMachine#set_descriptor_table_size, as follows:
67
+
68
+ require 'rubygems'
69
+ require 'eventmachine'
70
+
71
+ new_size = EM.set_descriptor_table_size( 60000 )
72
+ $>.puts "New descriptor-table size is #{new_size}"
73
+
74
+ EM.run {
75
+ ...
76
+ }
77
+
78
+ If successful, this example will increase the maximum number of descriptors that epoll can use to 60,000.
79
+ Call EventMachine#set_descriptor_table_size without an argument at any time to find out the current
80
+ size of the descriptor table.
81
+
82
+ Using EventMachine#set_descriptor_table_size ONLY affects the number of descriptors that can be used
83
+ by epoll. It has no useful effect on platforms that don't support epoll, and it does NOT increase the
84
+ number of descriptors that Ruby's own I/O functions can use.
85
+
86
+ #set_descriptor_table_size can fail if your process is not running as superuser, or if you try to set a
87
+ table size that exceeds the hard limits imposed by your system. In the latter case, try a smaller number.
88
+
89
+
90
+ === Using EventMachine#set_effective_user
91
+
92
+ In general, you must run your program with elevated or superuser privileges if you want to increase
93
+ your descriptor-table size beyond 1024 descriptors. This is easy enough to verify. Try running the
94
+ sample program given above, that increases the descriptor limit to 60,000. You will probably find that
95
+ the table size will not be increased if you don't run your program as root or with elevated privileges.
96
+
97
+ But of course network servers, especially long-running ones, should not run with elevated privileges.
98
+ You will want to drop superuser privileges as soon as possible after initialization. To do this,
99
+ use EventMachine#set_effective_user:
100
+
101
+ require 'rubygems'
102
+ require 'eventmachine'
103
+
104
+ # (Here, program is running as superuser)
105
+
106
+ EM.set_descriptor_table_size( 60000 )
107
+ EM.set_effective_user( "nobody" )
108
+ # (Here, program is running as nobody)
109
+
110
+ EM.run {
111
+ ...
112
+ }
113
+
114
+ Of course, you will need to replace "nobody" in the example with the name of an unprivileged user
115
+ that is valid on your system. What if you want to drop privileges after opening a server socket
116
+ on a privileged (low-numbered) port? Easy, just call #set_effective_user after opening your sockets:
117
+
118
+ require 'rubygems'
119
+ require 'eventmachine'
120
+
121
+ # (Here, program is running as superuser)
122
+
123
+ EM.set_descriptor_table_size( 60000 )
124
+
125
+ EM.run {
126
+ EM.start_server( "0.0.0.0", 80, MyHttpServer )
127
+ EM.start_server( "0.0.0.0", 443, MyEncryptedHttpServer )
128
+
129
+ EM.set_effective_user( "nobody" )
130
+ # (Here, program is running as nobody)
131
+
132
+ ...
133
+ }
134
+
135
+
136
+ Because EventMachine#set_effective_user is used to enforce security
137
+ requirements, it has no nonfatal errors. If you try to set a nonexistent or invalid effective user,
138
+ #set_effective_user will abort your program, rather than continue to run with elevated privileges.
139
+
140
+ EventMachine#set_effective_user is a silent no-op on platforms that don't support it, such as Windows.
141
+
142
+
data/RELEASE_NOTES CHANGED
@@ -1,7 +1,20 @@
1
- $Id: RELEASE_NOTES 286 2006-11-28 03:18:18Z blackhedd $
1
+ $Id: RELEASE_NOTES 388 2007-06-25 02:06:05Z blackhedd $
2
2
 
3
3
  RUBY/EventMachine RELEASE NOTES
4
4
 
5
+ --------------------------------------------------
6
+ Version: 0.9.0, released xxXXX07
7
+ Added Erlang-like distributed-computing features
8
+
9
+ --------------------------------------------------
10
+ Version: 0.8.0, released 23Jun07
11
+ Added an epoll implementation for Linux 2.6 kernels.
12
+ Added evented #popen.
13
+
14
+ --------------------------------------------------
15
+ Version: 0.7.3, released 22May07
16
+ Added a large variety of small features. See the ChangeLog.
17
+
5
18
  --------------------------------------------------
6
19
  Version: 0.7.1, released xxNov06
7
20
  Added protocol handlers for line-oriented protocols.
data/ext/cmain.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: cmain.cpp 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: cmain.cpp 377 2007-06-13 01:05:56Z blackhedd $
4
4
 
5
5
  File: cmain.cpp
6
6
  Date: 06Apr06
@@ -21,6 +21,7 @@ See the file COPYING for complete licensing information.
21
21
 
22
22
 
23
23
  static EventMachine_t *EventMachine;
24
+ static int bUseEpoll = 0;
24
25
 
25
26
 
26
27
  /***********************
@@ -35,6 +36,8 @@ extern "C" void evma_initialize_library (void(*cb)(const char*, int, const char*
35
36
  if (EventMachine)
36
37
  throw std::runtime_error ("already initialized");
37
38
  EventMachine = new EventMachine_t (cb);
39
+ if (bUseEpoll)
40
+ EventMachine->_UseEpoll();
38
41
  }
39
42
 
40
43
 
@@ -210,6 +213,22 @@ extern "C" int evma_get_peername (const char *binding, struct sockaddr *sa)
210
213
  return 0;
211
214
  }
212
215
 
216
+ /***********************
217
+ evma_get_subprocess_pid
218
+ ***********************/
219
+
220
+ extern "C" int evma_get_subprocess_pid (const char *binding, pid_t *pid)
221
+ {
222
+ if (!EventMachine)
223
+ throw std::runtime_error ("not initialized");
224
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
225
+ if (ed) {
226
+ return ed->GetSubprocessPid (pid) ? 1 : 0;
227
+ }
228
+ else
229
+ return 0;
230
+ }
231
+
213
232
 
214
233
  /*********************
215
234
  evma_signal_loopbreak
@@ -290,3 +309,53 @@ extern "C" void evma_setuid_string (const char *username)
290
309
  EventMachine_t::SetuidString (username);
291
310
  }
292
311
 
312
+
313
+ /**********
314
+ evma_popen
315
+ **********/
316
+
317
+ extern "C" const char *evma_popen (char * const*cmd_strings)
318
+ {
319
+ if (!EventMachine)
320
+ throw std::runtime_error ("not initialized");
321
+ return EventMachine->Socketpair (cmd_strings);
322
+ }
323
+
324
+
325
+ /***************************
326
+ evma_get_outbound_data_size
327
+ ***************************/
328
+
329
+ extern "C" int evma_get_outbound_data_size (const char *binding)
330
+ {
331
+ if (!EventMachine)
332
+ throw std::runtime_error ("not initialized");
333
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
334
+ return ed ? ed->GetOutboundDataSize() : 0;
335
+ }
336
+
337
+
338
+ /***********
339
+ evma__epoll
340
+ ***********/
341
+
342
+ extern "C" void evma__epoll()
343
+ {
344
+ bUseEpoll = 1;
345
+ }
346
+
347
+
348
+ /**********************
349
+ evma_set_rlimit_nofile
350
+ **********************/
351
+
352
+ extern "C" int evma_set_rlimit_nofile (int nofiles)
353
+ {
354
+ return EventMachine_t::SetRlimitNofile (nofiles);
355
+ }
356
+
357
+
358
+
359
+
360
+
361
+
data/ext/ed.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.cpp 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: ed.cpp 400 2007-07-11 12:10:33Z blackhedd $
4
4
 
5
5
  File: ed.cpp
6
6
  Date: 06Apr06
@@ -43,13 +43,15 @@ bool SetSocketNonblocking (SOCKET sd)
43
43
  EventableDescriptor::EventableDescriptor
44
44
  ****************************************/
45
45
 
46
- EventableDescriptor::EventableDescriptor (int sd):
46
+ EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em):
47
+ bCloseNow (false),
48
+ bCloseAfterWriting (false),
49
+ MySocket (sd),
47
50
  EventCallback (NULL),
48
51
  LastRead (0),
49
52
  LastWritten (0),
50
- MySocket (sd),
51
- bCloseNow (false),
52
- bCloseAfterWriting (false)
53
+ bCallbackUnbind (true),
54
+ MyEventMachine (em)
53
55
  {
54
56
  /* There are three ways to close a socket, all of which should
55
57
  * automatically signal to the event machine that this object
@@ -74,7 +76,13 @@ EventableDescriptor::EventableDescriptor (int sd):
74
76
 
75
77
  if (sd == INVALID_SOCKET)
76
78
  throw std::runtime_error ("bad eventable descriptor");
79
+ if (MyEventMachine == NULL)
80
+ throw std::runtime_error ("bad em in eventable descriptor");
77
81
  CreatedAt = gCurrentLoopTime;
82
+
83
+ #ifdef HAVE_EPOLL
84
+ EpollEvent.data.ptr = this;
85
+ #endif
78
86
  }
79
87
 
80
88
 
@@ -84,8 +92,8 @@ EventableDescriptor::~EventableDescriptor
84
92
 
85
93
  EventableDescriptor::~EventableDescriptor()
86
94
  {
87
- if (EventCallback)
88
- (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_UNBOUND, NULL, 0);
95
+ if (EventCallback && bCallbackUnbind)
96
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_UNBOUND, NULL, 0);
89
97
  Close();
90
98
  }
91
99
 
@@ -140,6 +148,7 @@ EventableDescriptor::ScheduleClose
140
148
 
141
149
  void EventableDescriptor::ScheduleClose (bool after_writing)
142
150
  {
151
+ // KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled.
143
152
  if (after_writing)
144
153
  bCloseAfterWriting = true;
145
154
  else
@@ -147,12 +156,23 @@ void EventableDescriptor::ScheduleClose (bool after_writing)
147
156
  }
148
157
 
149
158
 
159
+ /*************************************
160
+ EventableDescriptor::IsCloseScheduled
161
+ *************************************/
162
+
163
+ bool EventableDescriptor::IsCloseScheduled()
164
+ {
165
+ // KEEP THIS SYNCHRONIZED WITH ::ScheduleClose.
166
+ return (bCloseNow || bCloseAfterWriting);
167
+ }
168
+
169
+
150
170
  /******************************************
151
171
  ConnectionDescriptor::ConnectionDescriptor
152
172
  ******************************************/
153
173
 
154
- ConnectionDescriptor::ConnectionDescriptor (int sd):
155
- EventableDescriptor (sd),
174
+ ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em):
175
+ EventableDescriptor (sd, em),
156
176
  bConnectPending (false),
157
177
  bReadAttemptedAfterClose (false),
158
178
  OutboundDataSize (0),
@@ -163,6 +183,9 @@ ConnectionDescriptor::ConnectionDescriptor (int sd):
163
183
  LastIo (gCurrentLoopTime),
164
184
  InactivityTimeout (0)
165
185
  {
186
+ #ifdef HAVE_EPOLL
187
+ EpollEvent.events = EPOLLOUT;
188
+ #endif
166
189
  }
167
190
 
168
191
 
@@ -190,12 +213,19 @@ STATIC: ConnectionDescriptor::SendDataToConnection
190
213
  int ConnectionDescriptor::SendDataToConnection (const char *binding, const char *data, int data_length)
191
214
  {
192
215
  // TODO: This is something of a hack, or at least it's a static method of the wrong class.
216
+ // TODO: Poor polymorphism here. We should be calling one virtual method
217
+ // instead of hacking out the runtime information of the target object.
193
218
  ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
194
219
  if (cd)
195
220
  return cd->SendOutboundData (data, data_length);
196
221
  DatagramDescriptor *ds = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
197
222
  if (ds)
198
223
  return ds->SendOutboundData (data, data_length);
224
+ #ifdef OS_UNIX
225
+ PipeDescriptor *ps = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
226
+ if (ps)
227
+ return ps->SendOutboundData (data, data_length);
228
+ #endif
199
229
  return -1;
200
230
  }
201
231
 
@@ -257,7 +287,8 @@ int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
257
287
  // and not the whole process), and no coalescing of small pages.
258
288
  // (Well, not so bad, small pages are coalesced in ::Write)
259
289
 
260
- if (bCloseNow || bCloseAfterWriting)
290
+ if (IsCloseScheduled())
291
+ //if (bCloseNow || bCloseAfterWriting)
261
292
  return 0;
262
293
 
263
294
  if (!data && (length > 0))
@@ -269,6 +300,11 @@ int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length)
269
300
  buffer [length] = 0;
270
301
  OutboundPages.push_back (OutboundPage (buffer, length));
271
302
  OutboundDataSize += length;
303
+ #ifdef HAVE_EPOLL
304
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
305
+ assert (MyEventMachine);
306
+ MyEventMachine->Modify (this);
307
+ #endif
272
308
  return length;
273
309
  }
274
310
 
@@ -399,7 +435,8 @@ void ConnectionDescriptor::Read()
399
435
  if (total_bytes_read == 0) {
400
436
  // If we read no data on a socket that selected readable,
401
437
  // it generally means the other end closed the connection gracefully.
402
- bCloseNow = true;
438
+ ScheduleClose (false);
439
+ //bCloseNow = true;
403
440
  }
404
441
 
405
442
  }
@@ -421,20 +458,20 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, int size)
421
458
  while ((s = SslBox->GetPlaintext (B, sizeof(B) - 1)) > 0) {
422
459
  B [s] = 0;
423
460
  if (EventCallback)
424
- (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, B, s);
461
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, B, s);
425
462
  }
426
463
  // INCOMPLETE, s may indicate an SSL error that would force the connection down.
427
464
  _DispatchCiphertext();
428
465
  }
429
466
  else {
430
467
  if (EventCallback)
431
- (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, buffer, size);
468
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, buffer, size);
432
469
  }
433
470
  #endif
434
471
 
435
472
  #ifdef WITHOUT_SSL
436
473
  if (EventCallback)
437
- (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, buffer, size);
474
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, buffer, size);
438
475
  #endif
439
476
  }
440
477
 
@@ -465,18 +502,23 @@ void ConnectionDescriptor::Write()
465
502
  socklen_t len;
466
503
  len = sizeof(error);
467
504
  #ifdef OS_UNIX
468
- int o = getsockopt (MySocket, SOL_SOCKET, SO_ERROR, &error, &len);
505
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
469
506
  #endif
470
507
  #ifdef OS_WIN32
471
- int o = getsockopt (MySocket, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
508
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
472
509
  #endif
473
510
  if ((o == 0) && (error == 0)) {
474
511
  if (EventCallback)
475
- (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_COMPLETED, "", 0);
512
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_COMPLETED, "", 0);
476
513
  bConnectPending = false;
514
+ #ifdef HAVE_EPOLL
515
+ // The callback may have scheduled outbound data.
516
+ EpollEvent.events = EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0);
517
+ #endif
477
518
  }
478
519
  else
479
- bCloseNow = true;
520
+ ScheduleClose (false);
521
+ //bCloseNow = true;
480
522
  }
481
523
  else {
482
524
  _WriteOutboundData();
@@ -540,6 +582,12 @@ void ConnectionDescriptor::_WriteOutboundData()
540
582
  buffer [len] = 0;
541
583
  OutboundPages.push_front (OutboundPage (buffer, len));
542
584
  }
585
+
586
+ #ifdef HAVE_EPOLL
587
+ EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
588
+ assert (MyEventMachine);
589
+ MyEventMachine->Modify (this);
590
+ #endif
543
591
  }
544
592
  else {
545
593
  #ifdef OS_UNIX
@@ -653,30 +701,71 @@ void ConnectionDescriptor::Heartbeat()
653
701
 
654
702
  if (bConnectPending) {
655
703
  if ((gCurrentLoopTime - CreatedAt) >= PendingConnectTimeout)
656
- bCloseNow = true;
704
+ ScheduleClose (false);
705
+ //bCloseNow = true;
657
706
  }
658
707
  else {
659
708
  if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
660
- bCloseNow = true;
709
+ ScheduleClose (false);
710
+ //bCloseNow = true;
661
711
  }
662
712
  }
663
713
 
664
714
 
715
+ /****************************************
716
+ LoopbreakDescriptor::LoopbreakDescriptor
717
+ ****************************************/
718
+
719
+ LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em):
720
+ EventableDescriptor (sd, parent_em)
721
+ {
722
+ /* This is really bad and ugly. Change someday if possible.
723
+ * We have to know about an event-machine (probably the one that owns us),
724
+ * so we can pass newly-created connections to it.
725
+ */
726
+
727
+ bCallbackUnbind = false;
728
+
729
+ #ifdef HAVE_EPOLL
730
+ EpollEvent.events = EPOLLIN;
731
+ #endif
732
+ }
733
+
734
+
735
+
736
+
737
+ /*************************
738
+ LoopbreakDescriptor::Read
739
+ *************************/
740
+
741
+ void LoopbreakDescriptor::Read()
742
+ {
743
+ // TODO, refactor, this code is probably in the wrong place.
744
+ assert (MyEventMachine);
745
+ MyEventMachine->_ReadLoopBreaker();
746
+ }
747
+
748
+
749
+ /**************************
750
+ LoopbreakDescriptor::Write
751
+ **************************/
752
+
753
+ void LoopbreakDescriptor::Write()
754
+ {
755
+ // Why are we here?
756
+ throw std::runtime_error ("bad code path in loopbreak");
757
+ }
758
+
665
759
  /**************************************
666
760
  AcceptorDescriptor::AcceptorDescriptor
667
761
  **************************************/
668
762
 
669
- AcceptorDescriptor::AcceptorDescriptor (EventMachine_t *parent_em, int sd):
670
- EventableDescriptor (sd),
671
- MyEventMachine (parent_em)
763
+ AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
764
+ EventableDescriptor (sd, parent_em)
672
765
  {
673
- /* This is really bad and ugly. Change someday if possible.
674
- * We have to know about an event-machine (probably the one that owns us),
675
- * so we can pass newly-created connections to it.
676
- */
677
-
678
- if (!MyEventMachine)
679
- throw std::runtime_error ("bad event-machine passed to acceptor");
766
+ #ifdef HAVE_EPOLL
767
+ EpollEvent.events = EPOLLIN;
768
+ #endif
680
769
  }
681
770
 
682
771
 
@@ -739,13 +828,16 @@ void AcceptorDescriptor::Read()
739
828
  setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
740
829
 
741
830
 
742
- ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
831
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, MyEventMachine);
743
832
  if (!cd)
744
833
  throw std::runtime_error ("no newly accepted connection");
745
834
  cd->SetServerMode();
746
835
  if (EventCallback) {
747
- (*EventCallback) (GetBinding().c_str(), EventMachine_t::CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size());
836
+ (*EventCallback) (GetBinding().c_str(), EM_CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size());
748
837
  }
838
+ #ifdef HAVE_EPOLL
839
+ cd->GetEpollEvent()->events = EPOLLIN | (cd->SelectForWrite() ? EPOLLOUT : 0);
840
+ #endif
749
841
  assert (MyEventMachine);
750
842
  MyEventMachine->Add (cd);
751
843
  }
@@ -780,13 +872,17 @@ void AcceptorDescriptor::Heartbeat()
780
872
  DatagramDescriptor::DatagramDescriptor
781
873
  **************************************/
782
874
 
783
- DatagramDescriptor::DatagramDescriptor (int sd):
784
- EventableDescriptor (sd),
875
+ DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em):
876
+ EventableDescriptor (sd, parent_em),
785
877
  OutboundDataSize (0),
786
878
  LastIo (gCurrentLoopTime),
787
879
  InactivityTimeout (0)
788
880
  {
789
881
  memset (&ReturnAddress, 0, sizeof(ReturnAddress));
882
+
883
+ #ifdef HAVE_EPOLL
884
+ EpollEvent.events = EPOLLIN;
885
+ #endif
790
886
  }
791
887
 
792
888
 
@@ -810,8 +906,9 @@ void DatagramDescriptor::Heartbeat()
810
906
  {
811
907
  // Close it if its inactivity timer has expired.
812
908
 
813
- if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
814
- bCloseNow = true;
909
+ if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
910
+ ScheduleClose (false);
911
+ //bCloseNow = true;
815
912
  }
816
913
 
817
914
 
@@ -869,7 +966,7 @@ void DatagramDescriptor::Read()
869
966
  memcpy (&ReturnAddress, &sin, slen);
870
967
 
871
968
  if (EventCallback)
872
- (*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, readbuffer, r);
969
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r);
873
970
 
874
971
  }
875
972
  else {
@@ -932,6 +1029,12 @@ void DatagramDescriptor::Write()
932
1029
  }
933
1030
  }
934
1031
  }
1032
+
1033
+ #ifdef HAVE_EPOLL
1034
+ EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
1035
+ assert (MyEventMachine);
1036
+ MyEventMachine->Modify (this);
1037
+ #endif
935
1038
  }
936
1039
 
937
1040
 
@@ -954,7 +1057,8 @@ int DatagramDescriptor::SendOutboundData (const char *data, int length)
954
1057
  // This is an exact clone of ConnectionDescriptor::SendOutboundData.
955
1058
  // That means it needs to move to a common ancestor.
956
1059
 
957
- if (bCloseNow || bCloseAfterWriting)
1060
+ if (IsCloseScheduled())
1061
+ //if (bCloseNow || bCloseAfterWriting)
958
1062
  return 0;
959
1063
 
960
1064
  if (!data && (length > 0))
@@ -966,6 +1070,11 @@ int DatagramDescriptor::SendOutboundData (const char *data, int length)
966
1070
  buffer [length] = 0;
967
1071
  OutboundPages.push_back (OutboundPage (buffer, length, ReturnAddress));
968
1072
  OutboundDataSize += length;
1073
+ #ifdef HAVE_EPOLL
1074
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
1075
+ assert (MyEventMachine);
1076
+ MyEventMachine->Modify (this);
1077
+ #endif
969
1078
  return length;
970
1079
  }
971
1080
 
@@ -980,7 +1089,8 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, cons
980
1089
  // That means it needs to move to a common ancestor.
981
1090
  // TODO: Refactor this so there's no overlap with SendOutboundData.
982
1091
 
983
- if (bCloseNow || bCloseAfterWriting)
1092
+ if (IsCloseScheduled())
1093
+ //if (bCloseNow || bCloseAfterWriting)
984
1094
  return 0;
985
1095
 
986
1096
  if (!address || !*address || !port)
@@ -1014,6 +1124,11 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, cons
1014
1124
  buffer [length] = 0;
1015
1125
  OutboundPages.push_back (OutboundPage (buffer, length, pin));
1016
1126
  OutboundDataSize += length;
1127
+ #ifdef HAVE_EPOLL
1128
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
1129
+ assert (MyEventMachine);
1130
+ MyEventMachine->Modify (this);
1131
+ #endif
1017
1132
  return length;
1018
1133
  }
1019
1134
 
@@ -1055,14 +1170,14 @@ ConnectionDescriptor::GetCommInactivityTimeout
1055
1170
 
1056
1171
  int ConnectionDescriptor::GetCommInactivityTimeout (int *value)
1057
1172
  {
1058
- if (value) {
1059
- *value = InactivityTimeout;
1060
- return 1;
1061
- }
1062
- else {
1063
- // TODO, extended logging, got bad parameter.
1064
- return 0;
1065
- }
1173
+ if (value) {
1174
+ *value = InactivityTimeout;
1175
+ return 1;
1176
+ }
1177
+ else {
1178
+ // TODO, extended logging, got bad parameter.
1179
+ return 0;
1180
+ }
1066
1181
  }
1067
1182
 
1068
1183
 
@@ -1072,25 +1187,25 @@ ConnectionDescriptor::SetCommInactivityTimeout
1072
1187
 
1073
1188
  int ConnectionDescriptor::SetCommInactivityTimeout (int *value)
1074
1189
  {
1075
- int out = 0;
1076
-
1077
- if (value) {
1078
- if ((*value==0) || (*value >= 2)) {
1079
- // Replace the value and send the old one back to the caller.
1080
- int v = *value;
1081
- *value = InactivityTimeout;
1082
- InactivityTimeout = v;
1083
- out = 1;
1084
- }
1085
- else {
1086
- // TODO, extended logging, got bad value.
1087
- }
1088
- }
1089
- else {
1090
- // TODO, extended logging, got bad parameter.
1091
- }
1190
+ int out = 0;
1191
+
1192
+ if (value) {
1193
+ if ((*value==0) || (*value >= 2)) {
1194
+ // Replace the value and send the old one back to the caller.
1195
+ int v = *value;
1196
+ *value = InactivityTimeout;
1197
+ InactivityTimeout = v;
1198
+ out = 1;
1199
+ }
1200
+ else {
1201
+ // TODO, extended logging, got bad value.
1202
+ }
1203
+ }
1204
+ else {
1205
+ // TODO, extended logging, got bad parameter.
1206
+ }
1092
1207
 
1093
- return out;
1208
+ return out;
1094
1209
  }
1095
1210
 
1096
1211
  /*******************************