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/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
|
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
|
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
|
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
|
-
|
51
|
-
|
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(),
|
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 (
|
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
|
-
|
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(),
|
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(),
|
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(),
|
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 (
|
505
|
+
int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
|
469
506
|
#endif
|
470
507
|
#ifdef OS_WIN32
|
471
|
-
int o = getsockopt (
|
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(),
|
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
|
-
|
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
|
-
|
704
|
+
ScheduleClose (false);
|
705
|
+
//bCloseNow = true;
|
657
706
|
}
|
658
707
|
else {
|
659
708
|
if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
|
660
|
-
|
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
|
670
|
-
|
671
|
-
MyEventMachine (parent_em)
|
763
|
+
AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em):
|
764
|
+
EventableDescriptor (sd, parent_em)
|
672
765
|
{
|
673
|
-
|
674
|
-
|
675
|
-
|
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(),
|
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
|
-
|
814
|
-
|
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(),
|
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 (
|
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 (
|
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
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
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
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
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
|
-
|
1208
|
+
return out;
|
1094
1209
|
}
|
1095
1210
|
|
1096
1211
|
/*******************************
|