wj_eventmachine 1.3.0.dev.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +179 -0
- data/GNU +281 -0
- data/LICENSE +60 -0
- data/README.md +110 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +520 -0
- data/docs/old/ChangeLog +211 -0
- data/docs/old/DEFERRABLES +246 -0
- data/docs/old/EPOLL +141 -0
- data/docs/old/INSTALL +13 -0
- data/docs/old/KEYBOARD +42 -0
- data/docs/old/LEGAL +25 -0
- data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
- data/docs/old/PURE_RUBY +75 -0
- data/docs/old/RELEASE_NOTES +94 -0
- data/docs/old/SMTP +4 -0
- data/docs/old/SPAWNED_PROCESSES +148 -0
- data/docs/old/TODO +8 -0
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/old/ex_channel.rb +43 -0
- data/examples/old/ex_queue.rb +2 -0
- data/examples/old/ex_tick_loop_array.rb +15 -0
- data/examples/old/ex_tick_loop_counter.rb +32 -0
- data/examples/old/helper.rb +2 -0
- data/ext/binder.cpp +124 -0
- data/ext/binder.h +52 -0
- data/ext/cmain.cpp +1046 -0
- data/ext/ed.cpp +2238 -0
- data/ext/ed.h +460 -0
- data/ext/em.cpp +2378 -0
- data/ext/em.h +266 -0
- data/ext/eventmachine.h +152 -0
- data/ext/extconf.rb +285 -0
- data/ext/fastfilereader/extconf.rb +120 -0
- data/ext/fastfilereader/mapper.cpp +214 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +126 -0
- data/ext/kb.cpp +79 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +354 -0
- data/ext/project.h +174 -0
- data/ext/rubymain.cpp +1610 -0
- data/ext/ssl.cpp +627 -0
- data/ext/ssl.h +103 -0
- data/ext/wait_for_single_fd.h +36 -0
- data/java/.classpath +8 -0
- data/java/.project +17 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
- data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
- data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
- data/lib/em/buftok.rb +59 -0
- data/lib/em/callback.rb +58 -0
- data/lib/em/channel.rb +69 -0
- data/lib/em/completion.rb +307 -0
- data/lib/em/connection.rb +776 -0
- data/lib/em/deferrable.rb +210 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +73 -0
- data/lib/em/future.rb +61 -0
- data/lib/em/io_streamer.rb +68 -0
- data/lib/em/iterator.rb +252 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/pool.rb +151 -0
- data/lib/em/process_watch.rb +45 -0
- data/lib/em/processes.rb +123 -0
- data/lib/em/protocols.rb +37 -0
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +303 -0
- data/lib/em/protocols/httpclient2.rb +602 -0
- data/lib/em/protocols/line_and_text.rb +125 -0
- data/lib/em/protocols/line_protocol.rb +33 -0
- data/lib/em/protocols/linetext2.rb +179 -0
- data/lib/em/protocols/memcache.rb +331 -0
- data/lib/em/protocols/object_protocol.rb +46 -0
- data/lib/em/protocols/postgres3.rb +246 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +394 -0
- data/lib/em/protocols/smtpserver.rb +666 -0
- data/lib/em/protocols/socks4.rb +66 -0
- data/lib/em/protocols/stomp.rb +205 -0
- data/lib/em/protocols/tcptest.rb +54 -0
- data/lib/em/pure_ruby.rb +1299 -0
- data/lib/em/queue.rb +80 -0
- data/lib/em/resolver.rb +232 -0
- data/lib/em/spawnable.rb +84 -0
- data/lib/em/streamer.rb +118 -0
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +61 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1602 -0
- data/lib/jeventmachine.rb +318 -0
- data/rakelib/package.rake +120 -0
- data/rakelib/test.rake +6 -0
- data/rakelib/test_pure.rake +11 -0
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/dhparam.pem +13 -0
- data/tests/em_ssl_handlers.rb +153 -0
- data/tests/em_test_helper.rb +198 -0
- data/tests/jruby/test_jeventmachine.rb +38 -0
- data/tests/test_attach.rb +199 -0
- data/tests/test_basic.rb +321 -0
- data/tests/test_channel.rb +75 -0
- data/tests/test_completion.rb +178 -0
- data/tests/test_connection_count.rb +83 -0
- data/tests/test_connection_write.rb +35 -0
- data/tests/test_defer.rb +35 -0
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_epoll.rb +141 -0
- data/tests/test_error_handler.rb +38 -0
- data/tests/test_exc.rb +37 -0
- data/tests/test_file_watch.rb +86 -0
- data/tests/test_fork.rb +75 -0
- data/tests/test_futures.rb +170 -0
- data/tests/test_handler_check.rb +35 -0
- data/tests/test_hc.rb +155 -0
- data/tests/test_httpclient.rb +238 -0
- data/tests/test_httpclient2.rb +132 -0
- data/tests/test_idle_connection.rb +31 -0
- data/tests/test_inactivity_timeout.rb +102 -0
- data/tests/test_io_streamer.rb +47 -0
- data/tests/test_ipv4.rb +96 -0
- data/tests/test_ipv6.rb +107 -0
- data/tests/test_iterator.rb +122 -0
- data/tests/test_kb.rb +28 -0
- data/tests/test_keepalive.rb +113 -0
- data/tests/test_line_protocol.rb +33 -0
- data/tests/test_ltp.rb +155 -0
- data/tests/test_ltp2.rb +332 -0
- data/tests/test_many_fds.rb +21 -0
- data/tests/test_next_tick.rb +104 -0
- data/tests/test_object_protocol.rb +36 -0
- data/tests/test_pause.rb +109 -0
- data/tests/test_pending_connect_timeout.rb +52 -0
- data/tests/test_pool.rb +196 -0
- data/tests/test_process_watch.rb +50 -0
- data/tests/test_processes.rb +128 -0
- data/tests/test_proxy_connection.rb +180 -0
- data/tests/test_pure.rb +156 -0
- data/tests/test_queue.rb +64 -0
- data/tests/test_resolver.rb +129 -0
- data/tests/test_running.rb +14 -0
- data/tests/test_sasl.rb +46 -0
- data/tests/test_send_file.rb +217 -0
- data/tests/test_servers.rb +32 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_smtpclient.rb +75 -0
- data/tests/test_smtpserver.rb +90 -0
- data/tests/test_sock_opt.rb +53 -0
- data/tests/test_spawn.rb +290 -0
- data/tests/test_ssl_args.rb +41 -0
- data/tests/test_ssl_dhparam.rb +57 -0
- data/tests/test_ssl_ecdh_curve.rb +57 -0
- data/tests/test_ssl_extensions.rb +24 -0
- data/tests/test_ssl_methods.rb +31 -0
- data/tests/test_ssl_protocols.rb +190 -0
- data/tests/test_ssl_verify.rb +52 -0
- data/tests/test_stomp.rb +38 -0
- data/tests/test_system.rb +46 -0
- data/tests/test_threaded_resource.rb +68 -0
- data/tests/test_tick_loop.rb +58 -0
- data/tests/test_timers.rb +150 -0
- data/tests/test_ud.rb +8 -0
- data/tests/test_unbind_reason.rb +40 -0
- metadata +384 -0
data/ext/em.cpp
ADDED
|
@@ -0,0 +1,2378 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
|
|
3
|
+
$Id$
|
|
4
|
+
|
|
5
|
+
File: em.cpp
|
|
6
|
+
Date: 06Apr06
|
|
7
|
+
|
|
8
|
+
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
|
9
|
+
Gmail: blackhedd
|
|
10
|
+
|
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
|
12
|
+
it under the terms of either: 1) the GNU General Public License
|
|
13
|
+
as published by the Free Software Foundation; either version 2 of the
|
|
14
|
+
License, or (at your option) any later version; or 2) Ruby's License.
|
|
15
|
+
|
|
16
|
+
See the file COPYING for complete licensing information.
|
|
17
|
+
|
|
18
|
+
*****************************************************************************/
|
|
19
|
+
|
|
20
|
+
// THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY.
|
|
21
|
+
//#ifdef OS_UNIX
|
|
22
|
+
|
|
23
|
+
#include "project.h"
|
|
24
|
+
|
|
25
|
+
/* The numer of max outstanding timers was once a const enum defined in em.h.
|
|
26
|
+
* Now we define it here so that users can change its value if necessary.
|
|
27
|
+
*/
|
|
28
|
+
static unsigned int MaxOutstandingTimers = 100000;
|
|
29
|
+
|
|
30
|
+
/* The number of accept() done at once in a single tick when the acceptor
|
|
31
|
+
* socket becomes readable.
|
|
32
|
+
*/
|
|
33
|
+
static unsigned int SimultaneousAcceptCount = 10;
|
|
34
|
+
|
|
35
|
+
/* Internal helper to create a socket with SOCK_CLOEXEC set, and fall
|
|
36
|
+
* back to fcntl'ing it if the headers/runtime don't support it.
|
|
37
|
+
*/
|
|
38
|
+
SOCKET EmSocket (int domain, int type, int protocol)
|
|
39
|
+
{
|
|
40
|
+
SOCKET sd;
|
|
41
|
+
#ifdef HAVE_SOCKET_CLOEXEC
|
|
42
|
+
sd = socket (domain, type | SOCK_CLOEXEC, protocol);
|
|
43
|
+
if (sd == INVALID_SOCKET) {
|
|
44
|
+
sd = socket (domain, type, protocol);
|
|
45
|
+
if (sd < 0) {
|
|
46
|
+
return sd;
|
|
47
|
+
}
|
|
48
|
+
SetFdCloexec(sd);
|
|
49
|
+
}
|
|
50
|
+
#else
|
|
51
|
+
sd = socket (domain, type, protocol);
|
|
52
|
+
if (sd == INVALID_SOCKET) {
|
|
53
|
+
return sd;
|
|
54
|
+
}
|
|
55
|
+
SetFdCloexec(sd);
|
|
56
|
+
#endif
|
|
57
|
+
return sd;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/***************************************
|
|
62
|
+
STATIC EventMachine_t::GetMaxTimerCount
|
|
63
|
+
***************************************/
|
|
64
|
+
|
|
65
|
+
int EventMachine_t::GetMaxTimerCount()
|
|
66
|
+
{
|
|
67
|
+
return MaxOutstandingTimers;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/***************************************
|
|
72
|
+
STATIC EventMachine_t::SetMaxTimerCount
|
|
73
|
+
***************************************/
|
|
74
|
+
|
|
75
|
+
void EventMachine_t::SetMaxTimerCount (int count)
|
|
76
|
+
{
|
|
77
|
+
/* Allow a user to increase the maximum number of outstanding timers.
|
|
78
|
+
* If this gets "too high" (a metric that is of course platform dependent),
|
|
79
|
+
* bad things will happen like performance problems and possible overuse
|
|
80
|
+
* of memory.
|
|
81
|
+
* The actual timer mechanism is very efficient so it's hard to know what
|
|
82
|
+
* the practical max, but 100,000 shouldn't be too problematical.
|
|
83
|
+
*/
|
|
84
|
+
if (count < 100)
|
|
85
|
+
count = 100;
|
|
86
|
+
MaxOutstandingTimers = count;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
int EventMachine_t::GetSimultaneousAcceptCount()
|
|
90
|
+
{
|
|
91
|
+
return SimultaneousAcceptCount;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void EventMachine_t::SetSimultaneousAcceptCount (int count)
|
|
95
|
+
{
|
|
96
|
+
if (count < 1)
|
|
97
|
+
count = 1;
|
|
98
|
+
SimultaneousAcceptCount = count;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
/******************************
|
|
103
|
+
EventMachine_t::EventMachine_t
|
|
104
|
+
******************************/
|
|
105
|
+
|
|
106
|
+
EventMachine_t::EventMachine_t (EMCallback event_callback, Poller_t poller):
|
|
107
|
+
NumCloseScheduled (0),
|
|
108
|
+
HeartbeatInterval(2000000),
|
|
109
|
+
EventCallback (event_callback),
|
|
110
|
+
LoopBreakerReader (INVALID_SOCKET),
|
|
111
|
+
LoopBreakerWriter (INVALID_SOCKET),
|
|
112
|
+
bTerminateSignalReceived (false),
|
|
113
|
+
Poller (poller),
|
|
114
|
+
epfd (-1),
|
|
115
|
+
kqfd (-1)
|
|
116
|
+
#ifdef HAVE_INOTIFY
|
|
117
|
+
, inotify (NULL)
|
|
118
|
+
#endif
|
|
119
|
+
{
|
|
120
|
+
// Default time-slice is just smaller than one hundred mills.
|
|
121
|
+
Quantum.tv_sec = 0;
|
|
122
|
+
Quantum.tv_usec = 90000;
|
|
123
|
+
|
|
124
|
+
// Override the requested poller back to default if needed.
|
|
125
|
+
#if !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
|
|
126
|
+
Poller = Poller_Default;
|
|
127
|
+
#endif
|
|
128
|
+
|
|
129
|
+
/* Initialize monotonic timekeeping on OS X before the first call to GetRealTime */
|
|
130
|
+
#ifdef OS_DARWIN
|
|
131
|
+
(void) mach_timebase_info(&mach_timebase);
|
|
132
|
+
#endif
|
|
133
|
+
|
|
134
|
+
#ifdef OS_WIN32
|
|
135
|
+
TickCountTickover = 0;
|
|
136
|
+
LastTickCount = 0;
|
|
137
|
+
#endif
|
|
138
|
+
|
|
139
|
+
// Make sure the current loop time is sane, in case we do any initializations of
|
|
140
|
+
// objects before we start running.
|
|
141
|
+
_UpdateTime();
|
|
142
|
+
|
|
143
|
+
/* We initialize the network library here (only on Windows of course)
|
|
144
|
+
* and initialize "loop breakers." Our destructor also does some network-level
|
|
145
|
+
* cleanup. There's thus an implicit assumption that any given instance of EventMachine_t
|
|
146
|
+
* will only call ::Run once. Is that a good assumption? Should we move some of these
|
|
147
|
+
* inits and de-inits into ::Run?
|
|
148
|
+
*/
|
|
149
|
+
#ifdef OS_WIN32
|
|
150
|
+
WSADATA w;
|
|
151
|
+
WSAStartup (MAKEWORD (1, 1), &w);
|
|
152
|
+
#endif
|
|
153
|
+
|
|
154
|
+
_InitializeLoopBreaker();
|
|
155
|
+
SelectData = new SelectData_t();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/*******************************
|
|
160
|
+
EventMachine_t::~EventMachine_t
|
|
161
|
+
*******************************/
|
|
162
|
+
|
|
163
|
+
EventMachine_t::~EventMachine_t()
|
|
164
|
+
{
|
|
165
|
+
// Run down descriptors
|
|
166
|
+
size_t i;
|
|
167
|
+
for (i = 0; i < NewDescriptors.size(); i++)
|
|
168
|
+
delete NewDescriptors[i];
|
|
169
|
+
for (i = 0; i < Descriptors.size(); i++)
|
|
170
|
+
delete Descriptors[i];
|
|
171
|
+
|
|
172
|
+
close (LoopBreakerReader);
|
|
173
|
+
close (LoopBreakerWriter);
|
|
174
|
+
|
|
175
|
+
// Remove any file watch descriptors
|
|
176
|
+
while(!Files.empty()) {
|
|
177
|
+
std::map<int, Bindable_t*>::iterator f = Files.begin();
|
|
178
|
+
UnwatchFile (f->first);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (epfd != -1)
|
|
182
|
+
close (epfd);
|
|
183
|
+
if (kqfd != -1)
|
|
184
|
+
close (kqfd);
|
|
185
|
+
|
|
186
|
+
delete SelectData;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
/****************************
|
|
191
|
+
EventMachine_t::ScheduleHalt
|
|
192
|
+
****************************/
|
|
193
|
+
|
|
194
|
+
void EventMachine_t::ScheduleHalt()
|
|
195
|
+
{
|
|
196
|
+
/* This is how we stop the machine.
|
|
197
|
+
* This can be called by clients. Signal handlers will probably
|
|
198
|
+
* set the global flag.
|
|
199
|
+
* For now this means there can only be one EventMachine ever running at a time.
|
|
200
|
+
*
|
|
201
|
+
* IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here,
|
|
202
|
+
* because it may be called from signal handlers invoked from code that we don't
|
|
203
|
+
* control. At this writing (20Sep06), EM does NOT install any signal handlers of
|
|
204
|
+
* its own.
|
|
205
|
+
*
|
|
206
|
+
* We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens?
|
|
207
|
+
* The answer is to call evma_stop_machine, which calls here, from a SIGINT handler.
|
|
208
|
+
*/
|
|
209
|
+
bTerminateSignalReceived = true;
|
|
210
|
+
|
|
211
|
+
/* Signal the loopbreaker so we break out of long-running select/epoll/kqueue and
|
|
212
|
+
* notice the halt boolean is set. Signalling the loopbreaker also uses a single
|
|
213
|
+
* signal-safe syscall.
|
|
214
|
+
*/
|
|
215
|
+
SignalLoopBreaker();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
bool EventMachine_t::Stopping()
|
|
219
|
+
{
|
|
220
|
+
return bTerminateSignalReceived;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/*******************************
|
|
224
|
+
EventMachine_t::GetTimerQuantum
|
|
225
|
+
*******************************/
|
|
226
|
+
|
|
227
|
+
uint64_t EventMachine_t::GetTimerQuantum()
|
|
228
|
+
{
|
|
229
|
+
/* Convert timer-quantum to microseconds */
|
|
230
|
+
return (Quantum.tv_sec * 1000 + (Quantum.tv_usec + 500) / 1000) * 1000;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/*******************************
|
|
234
|
+
EventMachine_t::SetTimerQuantum
|
|
235
|
+
*******************************/
|
|
236
|
+
|
|
237
|
+
void EventMachine_t::SetTimerQuantum (int interval)
|
|
238
|
+
{
|
|
239
|
+
/* We get a timer-quantum expressed in milliseconds.
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
if ((interval < 5) || (interval > 5*60*1000))
|
|
243
|
+
throw std::runtime_error ("invalid timer-quantum");
|
|
244
|
+
|
|
245
|
+
Quantum.tv_sec = interval / 1000;
|
|
246
|
+
Quantum.tv_usec = (interval % 1000) * 1000;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
/*************************************
|
|
251
|
+
(STATIC) EventMachine_t::SetuidString
|
|
252
|
+
*************************************/
|
|
253
|
+
|
|
254
|
+
#ifdef OS_UNIX
|
|
255
|
+
void EventMachine_t::SetuidString (const char *username)
|
|
256
|
+
{
|
|
257
|
+
/* This method takes a caller-supplied username and tries to setuid
|
|
258
|
+
* to that user. There is no meaningful implementation (and no error)
|
|
259
|
+
* on Windows. On Unix, a failure to setuid the caller-supplied string
|
|
260
|
+
* causes a fatal abort, because presumably the program is calling here
|
|
261
|
+
* in order to fulfill a security requirement. If we fail silently,
|
|
262
|
+
* the user may continue to run with too much privilege.
|
|
263
|
+
*
|
|
264
|
+
* TODO, we need to decide on and document a way of generating C++ level errors
|
|
265
|
+
* that can be wrapped in documented Ruby exceptions, so users can catch
|
|
266
|
+
* and handle them. And distinguish it from errors that we WON'T let the Ruby
|
|
267
|
+
* user catch (like security-violations and resource-overallocation).
|
|
268
|
+
* A setuid failure here would be in the latter category.
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
if (!username || !*username)
|
|
272
|
+
throw std::runtime_error ("setuid_string failed: no username specified");
|
|
273
|
+
|
|
274
|
+
errno = 0;
|
|
275
|
+
struct passwd *p = getpwnam (username);
|
|
276
|
+
if (!p) {
|
|
277
|
+
if (errno) {
|
|
278
|
+
char buf[200];
|
|
279
|
+
snprintf (buf, sizeof(buf)-1, "setuid_string failed: %s", strerror(errno));
|
|
280
|
+
throw std::runtime_error (buf);
|
|
281
|
+
} else {
|
|
282
|
+
throw std::runtime_error ("setuid_string failed: unknown username");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (setuid (p->pw_uid) != 0)
|
|
287
|
+
throw std::runtime_error ("setuid_string failed: no setuid");
|
|
288
|
+
|
|
289
|
+
// Success.
|
|
290
|
+
}
|
|
291
|
+
#else
|
|
292
|
+
void EventMachine_t::SetuidString (const char *username UNUSED) { }
|
|
293
|
+
#endif
|
|
294
|
+
|
|
295
|
+
/****************************************
|
|
296
|
+
(STATIC) EventMachine_t::SetRlimitNofile
|
|
297
|
+
****************************************/
|
|
298
|
+
|
|
299
|
+
#ifdef OS_UNIX
|
|
300
|
+
int EventMachine_t::SetRlimitNofile (int nofiles)
|
|
301
|
+
{
|
|
302
|
+
struct rlimit rlim;
|
|
303
|
+
getrlimit (RLIMIT_NOFILE, &rlim);
|
|
304
|
+
if (nofiles >= 0) {
|
|
305
|
+
rlim.rlim_cur = nofiles;
|
|
306
|
+
if ((unsigned int)nofiles > rlim.rlim_max)
|
|
307
|
+
rlim.rlim_max = nofiles;
|
|
308
|
+
setrlimit (RLIMIT_NOFILE, &rlim);
|
|
309
|
+
// ignore the error return, for now at least.
|
|
310
|
+
// TODO, emit an error message someday when we have proper debug levels.
|
|
311
|
+
}
|
|
312
|
+
getrlimit (RLIMIT_NOFILE, &rlim);
|
|
313
|
+
return rlim.rlim_cur;
|
|
314
|
+
}
|
|
315
|
+
#else
|
|
316
|
+
int EventMachine_t::SetRlimitNofile (int nofiles UNUSED) { return 0; }
|
|
317
|
+
#endif
|
|
318
|
+
|
|
319
|
+
/*********************************
|
|
320
|
+
EventMachine_t::SignalLoopBreaker
|
|
321
|
+
*********************************/
|
|
322
|
+
|
|
323
|
+
void EventMachine_t::SignalLoopBreaker()
|
|
324
|
+
{
|
|
325
|
+
#ifdef OS_UNIX
|
|
326
|
+
(void)write (LoopBreakerWriter, "", 1);
|
|
327
|
+
#endif
|
|
328
|
+
#ifdef OS_WIN32
|
|
329
|
+
sendto (LoopBreakerReader, "", 0, 0, (struct sockaddr*)&(LoopBreakerTarget), sizeof(LoopBreakerTarget));
|
|
330
|
+
#endif
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
/**************************************
|
|
335
|
+
EventMachine_t::_InitializeLoopBreaker
|
|
336
|
+
**************************************/
|
|
337
|
+
|
|
338
|
+
void EventMachine_t::_InitializeLoopBreaker()
|
|
339
|
+
{
|
|
340
|
+
/* A "loop-breaker" is a socket-descriptor that we can write to in order
|
|
341
|
+
* to break the main select loop. Primarily useful for things running on
|
|
342
|
+
* threads other than the main EM thread, so they can trigger processing
|
|
343
|
+
* of events that arise exogenously to the EM.
|
|
344
|
+
* Keep the loop-breaker pipe out of the main descriptor set, otherwise
|
|
345
|
+
* its events will get passed on to user code.
|
|
346
|
+
*/
|
|
347
|
+
|
|
348
|
+
#ifdef OS_UNIX
|
|
349
|
+
int fd[2];
|
|
350
|
+
#if defined (HAVE_CLOEXEC) && defined (HAVE_PIPE2)
|
|
351
|
+
int pipestatus = pipe2(fd, O_CLOEXEC);
|
|
352
|
+
if (pipestatus < 0) {
|
|
353
|
+
if (pipe(fd))
|
|
354
|
+
throw std::runtime_error (strerror(errno));
|
|
355
|
+
}
|
|
356
|
+
#else
|
|
357
|
+
if (pipe (fd))
|
|
358
|
+
throw std::runtime_error (strerror(errno));
|
|
359
|
+
#endif
|
|
360
|
+
if (!SetFdCloexec(fd[0]) || !SetFdCloexec(fd[1]))
|
|
361
|
+
throw std::runtime_error (strerror(errno));
|
|
362
|
+
|
|
363
|
+
LoopBreakerWriter = fd[1];
|
|
364
|
+
LoopBreakerReader = fd[0];
|
|
365
|
+
|
|
366
|
+
/* 16Jan11: Make sure the pipe is non-blocking, so more than 65k loopbreaks
|
|
367
|
+
* in one tick do not fill up the pipe and block the process on write() */
|
|
368
|
+
SetSocketNonblocking (LoopBreakerWriter);
|
|
369
|
+
#endif
|
|
370
|
+
|
|
371
|
+
#ifdef OS_WIN32
|
|
372
|
+
SOCKET sd = EmSocket (AF_INET, SOCK_DGRAM, 0);
|
|
373
|
+
if (sd == INVALID_SOCKET)
|
|
374
|
+
throw std::runtime_error ("no loop breaker socket");
|
|
375
|
+
SetSocketNonblocking (sd);
|
|
376
|
+
|
|
377
|
+
memset (&LoopBreakerTarget, 0, sizeof(LoopBreakerTarget));
|
|
378
|
+
LoopBreakerTarget.sin_family = AF_INET;
|
|
379
|
+
LoopBreakerTarget.sin_addr.s_addr = inet_addr ("127.0.0.1");
|
|
380
|
+
|
|
381
|
+
srand ((int)time(NULL));
|
|
382
|
+
int i;
|
|
383
|
+
for (i=0; i < 100; i++) {
|
|
384
|
+
int r = (rand() % 10000) + 20000;
|
|
385
|
+
LoopBreakerTarget.sin_port = htons (r);
|
|
386
|
+
if (bind (sd, (struct sockaddr*)&LoopBreakerTarget, sizeof(LoopBreakerTarget)) == 0)
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (i == 100)
|
|
391
|
+
throw std::runtime_error ("no loop breaker");
|
|
392
|
+
LoopBreakerReader = sd;
|
|
393
|
+
#endif
|
|
394
|
+
|
|
395
|
+
#ifdef HAVE_EPOLL
|
|
396
|
+
if (Poller == Poller_Epoll) {
|
|
397
|
+
epfd = epoll_create1(EPOLL_CLOEXEC);
|
|
398
|
+
if (epfd == -1) {
|
|
399
|
+
char buf[200];
|
|
400
|
+
snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno));
|
|
401
|
+
throw std::runtime_error (buf);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
assert (LoopBreakerReader >= 0);
|
|
405
|
+
LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
|
|
406
|
+
assert (ld);
|
|
407
|
+
Add (ld);
|
|
408
|
+
}
|
|
409
|
+
#endif
|
|
410
|
+
|
|
411
|
+
#ifdef HAVE_KQUEUE
|
|
412
|
+
if (Poller == Poller_Kqueue) {
|
|
413
|
+
kqfd = kqueue();
|
|
414
|
+
if (kqfd == -1) {
|
|
415
|
+
char buf[200];
|
|
416
|
+
snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
|
|
417
|
+
throw std::runtime_error (buf);
|
|
418
|
+
}
|
|
419
|
+
// cloexec not needed. By definition, kqueues are not carried across forks.
|
|
420
|
+
|
|
421
|
+
assert (LoopBreakerReader >= 0);
|
|
422
|
+
LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
|
|
423
|
+
assert (ld);
|
|
424
|
+
Add (ld);
|
|
425
|
+
}
|
|
426
|
+
#endif
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/***************************
|
|
430
|
+
EventMachine_t::_UpdateTime
|
|
431
|
+
***************************/
|
|
432
|
+
|
|
433
|
+
void EventMachine_t::_UpdateTime()
|
|
434
|
+
{
|
|
435
|
+
MyCurrentLoopTime = GetRealTime();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/***************************
|
|
439
|
+
EventMachine_t::GetRealTime
|
|
440
|
+
***************************/
|
|
441
|
+
|
|
442
|
+
// Two great writeups of cross-platform monotonic time are at:
|
|
443
|
+
// http://www.python.org/dev/peps/pep-0418
|
|
444
|
+
// http://nadeausoftware.com/articles/2012/04/c_c_tip_how_measure_elapsed_real_time_benchmarking
|
|
445
|
+
// Uncomment the #pragma messages to confirm which compile-time option was used
|
|
446
|
+
uint64_t EventMachine_t::GetRealTime()
|
|
447
|
+
{
|
|
448
|
+
uint64_t current_time;
|
|
449
|
+
|
|
450
|
+
#if defined(HAVE_CONST_CLOCK_MONOTONIC_RAW)
|
|
451
|
+
// #pragma message "GetRealTime: clock_gettime CLOCK_MONOTONIC_RAW"
|
|
452
|
+
// Linux 2.6.28 and above
|
|
453
|
+
struct timespec tv;
|
|
454
|
+
clock_gettime (CLOCK_MONOTONIC_RAW, &tv);
|
|
455
|
+
current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)((tv.tv_nsec)/1000));
|
|
456
|
+
|
|
457
|
+
#elif defined(HAVE_CONST_CLOCK_MONOTONIC)
|
|
458
|
+
// #pragma message "GetRealTime: clock_gettime CLOCK_MONOTONIC"
|
|
459
|
+
// Linux, FreeBSD 5.0 and above, Solaris 8 and above, OpenBSD, NetBSD, DragonflyBSD
|
|
460
|
+
struct timespec tv;
|
|
461
|
+
clock_gettime (CLOCK_MONOTONIC, &tv);
|
|
462
|
+
current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)((tv.tv_nsec)/1000));
|
|
463
|
+
|
|
464
|
+
#elif defined(HAVE_GETHRTIME)
|
|
465
|
+
// #pragma message "GetRealTime: gethrtime"
|
|
466
|
+
// Solaris and HP-UX
|
|
467
|
+
current_time = (uint64_t)gethrtime() / 1000;
|
|
468
|
+
|
|
469
|
+
#elif defined(OS_DARWIN)
|
|
470
|
+
// #pragma message "GetRealTime: mach_absolute_time"
|
|
471
|
+
// Mac OS X
|
|
472
|
+
// https://developer.apple.com/library/mac/qa/qa1398/_index.html
|
|
473
|
+
current_time = mach_absolute_time() * mach_timebase.numer / mach_timebase.denom / 1000;
|
|
474
|
+
|
|
475
|
+
#elif defined(OS_UNIX)
|
|
476
|
+
// #pragma message "GetRealTime: gettimeofday"
|
|
477
|
+
// Unix fallback
|
|
478
|
+
struct timeval tv;
|
|
479
|
+
gettimeofday (&tv, NULL);
|
|
480
|
+
current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)(tv.tv_usec));
|
|
481
|
+
|
|
482
|
+
#elif defined(OS_WIN32)
|
|
483
|
+
// #pragma message "GetRealTime: GetTickCount"
|
|
484
|
+
// Future improvement: use GetTickCount64 in Windows Vista / Server 2008
|
|
485
|
+
unsigned tick = GetTickCount();
|
|
486
|
+
if (tick < LastTickCount)
|
|
487
|
+
TickCountTickover += 1;
|
|
488
|
+
LastTickCount = tick;
|
|
489
|
+
current_time = ((uint64_t)TickCountTickover << 32) + (uint64_t)tick;
|
|
490
|
+
current_time *= 1000; // convert to microseconds
|
|
491
|
+
|
|
492
|
+
#else
|
|
493
|
+
// #pragma message "GetRealTime: time"
|
|
494
|
+
// Universal fallback
|
|
495
|
+
current_time = (uint64_t)time(NULL) * 1000000LL;
|
|
496
|
+
#endif
|
|
497
|
+
|
|
498
|
+
return current_time;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/***********************************
|
|
502
|
+
EventMachine_t::_DispatchHeartbeats
|
|
503
|
+
***********************************/
|
|
504
|
+
|
|
505
|
+
void EventMachine_t::_DispatchHeartbeats()
|
|
506
|
+
{
|
|
507
|
+
// Store the first processed heartbeat descriptor and bail out if
|
|
508
|
+
// we see it again. This fixes an infinite loop in case the system time
|
|
509
|
+
// is changed out from underneath MyCurrentLoopTime.
|
|
510
|
+
const EventableDescriptor *head = NULL;
|
|
511
|
+
|
|
512
|
+
while (true) {
|
|
513
|
+
std::multimap<uint64_t,EventableDescriptor*>::iterator i = Heartbeats.begin();
|
|
514
|
+
if (i == Heartbeats.end())
|
|
515
|
+
break;
|
|
516
|
+
if (i->first > MyCurrentLoopTime)
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
EventableDescriptor *ed = i->second;
|
|
520
|
+
if (ed == head)
|
|
521
|
+
break;
|
|
522
|
+
|
|
523
|
+
ed->Heartbeat();
|
|
524
|
+
QueueHeartbeat(ed);
|
|
525
|
+
|
|
526
|
+
if (head == NULL)
|
|
527
|
+
head = ed;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/******************************
|
|
532
|
+
EventMachine_t::QueueHeartbeat
|
|
533
|
+
******************************/
|
|
534
|
+
|
|
535
|
+
void EventMachine_t::QueueHeartbeat(EventableDescriptor *ed)
|
|
536
|
+
{
|
|
537
|
+
uint64_t heartbeat = ed->GetNextHeartbeat();
|
|
538
|
+
|
|
539
|
+
if (heartbeat) {
|
|
540
|
+
#ifndef HAVE_MAKE_PAIR
|
|
541
|
+
Heartbeats.insert (std::multimap<uint64_t,EventableDescriptor*>::value_type (heartbeat, ed));
|
|
542
|
+
#else
|
|
543
|
+
Heartbeats.insert (std::make_pair (heartbeat, ed));
|
|
544
|
+
#endif
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/******************************
|
|
549
|
+
EventMachine_t::ClearHeartbeat
|
|
550
|
+
******************************/
|
|
551
|
+
|
|
552
|
+
void EventMachine_t::ClearHeartbeat(uint64_t key, EventableDescriptor* ed)
|
|
553
|
+
{
|
|
554
|
+
std::multimap<uint64_t,EventableDescriptor*>::iterator it;
|
|
555
|
+
std::pair<std::multimap<uint64_t,EventableDescriptor*>::iterator,std::multimap<uint64_t,EventableDescriptor*>::iterator> ret;
|
|
556
|
+
ret = Heartbeats.equal_range (key);
|
|
557
|
+
for (it = ret.first; it != ret.second; ++it) {
|
|
558
|
+
if (it->second == ed) {
|
|
559
|
+
Heartbeats.erase (it);
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/*******************
|
|
566
|
+
EventMachine_t::Run
|
|
567
|
+
*******************/
|
|
568
|
+
|
|
569
|
+
void EventMachine_t::Run()
|
|
570
|
+
{
|
|
571
|
+
while (RunOnce()) ;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/***********************
|
|
575
|
+
EventMachine_t::RunOnce
|
|
576
|
+
***********************/
|
|
577
|
+
|
|
578
|
+
bool EventMachine_t::RunOnce()
|
|
579
|
+
{
|
|
580
|
+
_UpdateTime();
|
|
581
|
+
_RunTimers();
|
|
582
|
+
|
|
583
|
+
/* _Add must precede _Modify because the same descriptor might
|
|
584
|
+
* be on both lists during the same pass through the machine,
|
|
585
|
+
* and to modify a descriptor before adding it would fail.
|
|
586
|
+
*/
|
|
587
|
+
_AddNewDescriptors();
|
|
588
|
+
_ModifyDescriptors();
|
|
589
|
+
|
|
590
|
+
switch (Poller) {
|
|
591
|
+
case Poller_Epoll:
|
|
592
|
+
_RunEpollOnce();
|
|
593
|
+
break;
|
|
594
|
+
case Poller_Kqueue:
|
|
595
|
+
_RunKqueueOnce();
|
|
596
|
+
break;
|
|
597
|
+
case Poller_Default:
|
|
598
|
+
_RunSelectOnce();
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
_DispatchHeartbeats();
|
|
603
|
+
_CleanupSockets();
|
|
604
|
+
|
|
605
|
+
if (bTerminateSignalReceived)
|
|
606
|
+
return false;
|
|
607
|
+
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
#ifdef HAVE_EPOLL
|
|
613
|
+
typedef struct {
|
|
614
|
+
int epfd;
|
|
615
|
+
struct epoll_event *events;
|
|
616
|
+
int maxevents;
|
|
617
|
+
int timeout;
|
|
618
|
+
} epoll_args_t;
|
|
619
|
+
|
|
620
|
+
static void *nogvl_epoll_wait(void *args)
|
|
621
|
+
{
|
|
622
|
+
epoll_args_t *a = (epoll_args_t *)args;
|
|
623
|
+
return (void *) (uintptr_t) epoll_wait (a->epfd, a->events, a->maxevents, a->timeout);
|
|
624
|
+
}
|
|
625
|
+
#endif
|
|
626
|
+
|
|
627
|
+
/*****************************
|
|
628
|
+
EventMachine_t::_RunEpollOnce
|
|
629
|
+
*****************************/
|
|
630
|
+
|
|
631
|
+
void EventMachine_t::_RunEpollOnce()
|
|
632
|
+
{
|
|
633
|
+
#ifdef HAVE_EPOLL
|
|
634
|
+
assert (epfd != -1);
|
|
635
|
+
int s;
|
|
636
|
+
|
|
637
|
+
timeval tv = _TimeTilNextEvent();
|
|
638
|
+
|
|
639
|
+
#ifdef BUILD_FOR_RUBY
|
|
640
|
+
int ret = 0;
|
|
641
|
+
|
|
642
|
+
if ((ret = rb_wait_for_single_fd(epfd, RB_WAITFD_IN|RB_WAITFD_PRI, &tv)) < 1) {
|
|
643
|
+
if (ret == -1) {
|
|
644
|
+
assert(errno != EINVAL);
|
|
645
|
+
assert(errno != EBADF);
|
|
646
|
+
}
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
epoll_args_t epoll_args = { epfd, epoll_events, MaxEvents, 0 };
|
|
651
|
+
s = (uintptr_t) rb_thread_call_without_gvl (nogvl_epoll_wait, &epoll_args, RUBY_UBF_IO, 0);
|
|
652
|
+
#else
|
|
653
|
+
int duration = 0;
|
|
654
|
+
duration = duration + (tv.tv_sec * 1000);
|
|
655
|
+
duration = duration + (tv.tv_usec / 1000);
|
|
656
|
+
s = epoll_wait (epfd, epoll_events, MaxEvents, duration);
|
|
657
|
+
#endif
|
|
658
|
+
|
|
659
|
+
if (s > 0) {
|
|
660
|
+
for (int i=0; i < s; i++) {
|
|
661
|
+
EventableDescriptor *ed = (EventableDescriptor*) epoll_events[i].data.ptr;
|
|
662
|
+
|
|
663
|
+
if (ed->IsWatchOnly() && ed->GetSocket() == INVALID_SOCKET)
|
|
664
|
+
continue;
|
|
665
|
+
|
|
666
|
+
assert(ed->GetSocket() != INVALID_SOCKET);
|
|
667
|
+
|
|
668
|
+
if (epoll_events[i].events & EPOLLIN)
|
|
669
|
+
ed->Read();
|
|
670
|
+
if (epoll_events[i].events & EPOLLOUT)
|
|
671
|
+
ed->Write();
|
|
672
|
+
if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
|
|
673
|
+
ed->HandleError();
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
else if (s < 0) {
|
|
677
|
+
// epoll_wait can fail on error in a handful of ways.
|
|
678
|
+
// If this happens, then wait for a little while to avoid busy-looping.
|
|
679
|
+
// If the error was EINTR, we probably caught SIGCHLD or something,
|
|
680
|
+
// so keep the wait short.
|
|
681
|
+
timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
|
|
682
|
+
EmSelect (0, NULL, NULL, NULL, &tv);
|
|
683
|
+
}
|
|
684
|
+
#else
|
|
685
|
+
throw std::runtime_error ("epoll is not implemented on this platform");
|
|
686
|
+
#endif
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
#ifdef HAVE_KQUEUE
|
|
691
|
+
typedef struct {
|
|
692
|
+
int kqfd;
|
|
693
|
+
const struct kevent *changelist;
|
|
694
|
+
int nchanges;
|
|
695
|
+
struct kevent *eventlist;
|
|
696
|
+
int nevents;
|
|
697
|
+
const struct timespec *timeout;
|
|
698
|
+
} kevent_args_t;
|
|
699
|
+
|
|
700
|
+
static void *nogvl_kevent(void *args)
|
|
701
|
+
{
|
|
702
|
+
kevent_args_t *a = (kevent_args_t *)args;
|
|
703
|
+
return (void *) (uintptr_t) kevent (a->kqfd, a->changelist, a->nchanges, a->eventlist, a->nevents, a->timeout);
|
|
704
|
+
}
|
|
705
|
+
#endif
|
|
706
|
+
|
|
707
|
+
/******************************
|
|
708
|
+
EventMachine_t::_RunKqueueOnce
|
|
709
|
+
******************************/
|
|
710
|
+
|
|
711
|
+
#ifdef HAVE_KQUEUE
|
|
712
|
+
void EventMachine_t::_RunKqueueOnce()
|
|
713
|
+
{
|
|
714
|
+
assert (kqfd != -1);
|
|
715
|
+
int k;
|
|
716
|
+
|
|
717
|
+
timeval tv = _TimeTilNextEvent();
|
|
718
|
+
|
|
719
|
+
struct timespec ts;
|
|
720
|
+
ts.tv_sec = tv.tv_sec;
|
|
721
|
+
ts.tv_nsec = tv.tv_usec * 1000;
|
|
722
|
+
|
|
723
|
+
#ifdef BUILD_FOR_RUBY
|
|
724
|
+
int ret = 0;
|
|
725
|
+
|
|
726
|
+
if ((ret = rb_wait_for_single_fd(kqfd, RB_WAITFD_IN|RB_WAITFD_PRI, &tv)) < 1) {
|
|
727
|
+
if (ret == -1) {
|
|
728
|
+
assert(errno != EINVAL);
|
|
729
|
+
assert(errno != EBADF);
|
|
730
|
+
}
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
ts.tv_sec = ts.tv_nsec = 0;
|
|
735
|
+
kevent_args_t kevent_args = { kqfd, NULL, 0, Karray, MaxEvents, &ts };
|
|
736
|
+
k = (uintptr_t) rb_thread_call_without_gvl (nogvl_kevent, &kevent_args, RUBY_UBF_IO, 0);
|
|
737
|
+
#else
|
|
738
|
+
k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
|
|
739
|
+
#endif
|
|
740
|
+
|
|
741
|
+
struct kevent *ke = Karray;
|
|
742
|
+
while (k > 0) {
|
|
743
|
+
switch (ke->filter)
|
|
744
|
+
{
|
|
745
|
+
case EVFILT_VNODE:
|
|
746
|
+
_HandleKqueueFileEvent (ke);
|
|
747
|
+
break;
|
|
748
|
+
|
|
749
|
+
case EVFILT_PROC:
|
|
750
|
+
_HandleKqueuePidEvent (ke);
|
|
751
|
+
break;
|
|
752
|
+
|
|
753
|
+
case EVFILT_READ:
|
|
754
|
+
case EVFILT_WRITE:
|
|
755
|
+
EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
|
|
756
|
+
assert (ed);
|
|
757
|
+
|
|
758
|
+
if (ed->IsWatchOnly() && ed->GetSocket() == INVALID_SOCKET)
|
|
759
|
+
break;
|
|
760
|
+
|
|
761
|
+
if (ke->filter == EVFILT_READ)
|
|
762
|
+
ed->Read();
|
|
763
|
+
else if (ke->filter == EVFILT_WRITE)
|
|
764
|
+
ed->Write();
|
|
765
|
+
else
|
|
766
|
+
std::cerr << "Discarding unknown kqueue event " << ke->filter << std::endl;
|
|
767
|
+
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
--k;
|
|
772
|
+
++ke;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
#ifdef BUILD_FOR_RUBY
|
|
776
|
+
if (!rb_thread_alone()) {
|
|
777
|
+
rb_thread_schedule();
|
|
778
|
+
}
|
|
779
|
+
#endif
|
|
780
|
+
}
|
|
781
|
+
#else
|
|
782
|
+
void EventMachine_t::_RunKqueueOnce()
|
|
783
|
+
{
|
|
784
|
+
throw std::runtime_error ("kqueue is not implemented on this platform");
|
|
785
|
+
}
|
|
786
|
+
#endif
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
/*********************************
|
|
790
|
+
EventMachine_t::_TimeTilNextEvent
|
|
791
|
+
*********************************/
|
|
792
|
+
|
|
793
|
+
timeval EventMachine_t::_TimeTilNextEvent()
|
|
794
|
+
{
|
|
795
|
+
// 29jul11: Changed calculation base from MyCurrentLoopTime to the
|
|
796
|
+
// real time. As MyCurrentLoopTime is set at the beginning of an
|
|
797
|
+
// iteration and this calculation is done at the end, evenmachine
|
|
798
|
+
// will potentially oversleep by the amount of time the iteration
|
|
799
|
+
// took to execute.
|
|
800
|
+
uint64_t next_event = 0;
|
|
801
|
+
uint64_t current_time = GetRealTime();
|
|
802
|
+
|
|
803
|
+
if (!Heartbeats.empty()) {
|
|
804
|
+
std::multimap<uint64_t,EventableDescriptor*>::iterator heartbeats = Heartbeats.begin();
|
|
805
|
+
next_event = heartbeats->first;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (!Timers.empty()) {
|
|
809
|
+
std::multimap<uint64_t,Timer_t>::iterator timers = Timers.begin();
|
|
810
|
+
if (next_event == 0 || timers->first < next_event)
|
|
811
|
+
next_event = timers->first;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (!NewDescriptors.empty() || !ModifiedDescriptors.empty()) {
|
|
815
|
+
next_event = current_time;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
timeval tv;
|
|
819
|
+
|
|
820
|
+
if (NumCloseScheduled > 0 || bTerminateSignalReceived) {
|
|
821
|
+
tv.tv_sec = tv.tv_usec = 0;
|
|
822
|
+
} else if (next_event == 0) {
|
|
823
|
+
tv = Quantum;
|
|
824
|
+
} else {
|
|
825
|
+
if (next_event > current_time) {
|
|
826
|
+
uint64_t duration = next_event - current_time;
|
|
827
|
+
tv.tv_sec = duration / 1000000;
|
|
828
|
+
tv.tv_usec = duration % 1000000;
|
|
829
|
+
} else {
|
|
830
|
+
tv.tv_sec = tv.tv_usec = 0;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return tv;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/*******************************
|
|
838
|
+
EventMachine_t::_CleanupSockets
|
|
839
|
+
*******************************/
|
|
840
|
+
|
|
841
|
+
void EventMachine_t::_CleanupSockets()
|
|
842
|
+
{
|
|
843
|
+
// TODO, rip this out and only delete the descriptors we know have died,
|
|
844
|
+
// rather than traversing the whole list.
|
|
845
|
+
// Modified 05Jan08 per suggestions by Chris Heath. It's possible that
|
|
846
|
+
// an EventableDescriptor will have a descriptor value of -1. That will
|
|
847
|
+
// happen if EventableDescriptor::Close was called on it. In that case,
|
|
848
|
+
// don't call epoll_ctl to remove the socket's filters from the epoll set.
|
|
849
|
+
// According to the epoll docs, this happens automatically when the
|
|
850
|
+
// descriptor is closed anyway. This is different from the case where
|
|
851
|
+
// the socket has already been closed but the descriptor in the ED object
|
|
852
|
+
// hasn't yet been set to INVALID_SOCKET.
|
|
853
|
+
// In kqueue, closing a descriptor automatically removes its event filters.
|
|
854
|
+
int i, j;
|
|
855
|
+
int nSockets = Descriptors.size();
|
|
856
|
+
for (i=0, j=0; i < nSockets; i++) {
|
|
857
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
858
|
+
assert (ed);
|
|
859
|
+
if (ed->ShouldDelete()) {
|
|
860
|
+
#ifdef HAVE_EPOLL
|
|
861
|
+
if (Poller == Poller_Epoll) {
|
|
862
|
+
assert (epfd != -1);
|
|
863
|
+
if (ed->GetSocket() != INVALID_SOCKET) {
|
|
864
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
|
865
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
|
866
|
+
if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
|
|
867
|
+
char buf [200];
|
|
868
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
|
869
|
+
throw std::runtime_error (buf);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
ModifiedDescriptors.erase(ed);
|
|
873
|
+
}
|
|
874
|
+
#endif
|
|
875
|
+
delete ed;
|
|
876
|
+
}
|
|
877
|
+
else
|
|
878
|
+
Descriptors [j++] = ed;
|
|
879
|
+
}
|
|
880
|
+
while ((size_t)j < Descriptors.size())
|
|
881
|
+
Descriptors.pop_back();
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/*********************************
|
|
885
|
+
EventMachine_t::_ModifyEpollEvent
|
|
886
|
+
*********************************/
|
|
887
|
+
|
|
888
|
+
#ifdef HAVE_EPOLL
|
|
889
|
+
void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
|
|
890
|
+
{
|
|
891
|
+
if (Poller == Poller_Epoll) {
|
|
892
|
+
assert (epfd != -1);
|
|
893
|
+
assert (ed);
|
|
894
|
+
assert (ed->GetSocket() != INVALID_SOCKET);
|
|
895
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent());
|
|
896
|
+
if (e) {
|
|
897
|
+
char buf [200];
|
|
898
|
+
snprintf (buf, sizeof(buf)-1, "unable to modify epoll event: %s", strerror(errno));
|
|
899
|
+
throw std::runtime_error (buf);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
#else
|
|
904
|
+
void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed UNUSED) { }
|
|
905
|
+
#endif
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
/**************************
|
|
909
|
+
SelectData_t::SelectData_t
|
|
910
|
+
**************************/
|
|
911
|
+
|
|
912
|
+
SelectData_t::SelectData_t()
|
|
913
|
+
{
|
|
914
|
+
maxsocket = 0;
|
|
915
|
+
rb_fd_init (&fdreads);
|
|
916
|
+
rb_fd_init (&fdwrites);
|
|
917
|
+
rb_fd_init (&fderrors);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
SelectData_t::~SelectData_t()
|
|
921
|
+
{
|
|
922
|
+
rb_fd_term (&fdreads);
|
|
923
|
+
rb_fd_term (&fdwrites);
|
|
924
|
+
rb_fd_term (&fderrors);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/*********************
|
|
928
|
+
SelectData_t::_Select
|
|
929
|
+
*********************/
|
|
930
|
+
|
|
931
|
+
int SelectData_t::_Select()
|
|
932
|
+
{
|
|
933
|
+
return EmSelect (maxsocket + 1, &fdreads, &fdwrites, &fderrors, &tv);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
void SelectData_t::_Clear()
|
|
937
|
+
{
|
|
938
|
+
maxsocket = 0;
|
|
939
|
+
rb_fd_zero (&fdreads);
|
|
940
|
+
rb_fd_zero (&fdwrites);
|
|
941
|
+
rb_fd_zero (&fderrors);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/******************************
|
|
945
|
+
EventMachine_t::_RunSelectOnce
|
|
946
|
+
******************************/
|
|
947
|
+
|
|
948
|
+
void EventMachine_t::_RunSelectOnce()
|
|
949
|
+
{
|
|
950
|
+
// Crank the event machine once.
|
|
951
|
+
// If there are no descriptors to process, then sleep
|
|
952
|
+
// for a few hundred mills to avoid busy-looping.
|
|
953
|
+
// This is based on a select loop. Alternately provide epoll
|
|
954
|
+
// if we know we're running on a 2.6 kernel.
|
|
955
|
+
// epoll will be effective if we provide it as an alternative,
|
|
956
|
+
// however it has the same problem interoperating with Ruby
|
|
957
|
+
// threads that select does.
|
|
958
|
+
|
|
959
|
+
// Get ready for select()
|
|
960
|
+
SelectData->_Clear();
|
|
961
|
+
|
|
962
|
+
// Always read the loop-breaker reader.
|
|
963
|
+
// Changed 23Aug06, provisionally implemented for Windows with a UDP socket
|
|
964
|
+
// running on localhost with a randomly-chosen port. (*Puke*)
|
|
965
|
+
// Windows has a version of the Unix pipe() library function, but it doesn't
|
|
966
|
+
// give you back descriptors that are selectable.
|
|
967
|
+
rb_fd_set (LoopBreakerReader, &(SelectData->fdreads));
|
|
968
|
+
if (SelectData->maxsocket < LoopBreakerReader)
|
|
969
|
+
SelectData->maxsocket = LoopBreakerReader;
|
|
970
|
+
|
|
971
|
+
// prepare the sockets for reading and writing
|
|
972
|
+
size_t i;
|
|
973
|
+
for (i = 0; i < Descriptors.size(); i++) {
|
|
974
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
975
|
+
assert (ed);
|
|
976
|
+
SOCKET sd = ed->GetSocket();
|
|
977
|
+
if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
|
|
978
|
+
continue;
|
|
979
|
+
assert (sd != INVALID_SOCKET);
|
|
980
|
+
|
|
981
|
+
if (ed->SelectForRead())
|
|
982
|
+
rb_fd_set (sd, &(SelectData->fdreads));
|
|
983
|
+
if (ed->SelectForWrite())
|
|
984
|
+
rb_fd_set (sd, &(SelectData->fdwrites));
|
|
985
|
+
|
|
986
|
+
#ifdef OS_WIN32
|
|
987
|
+
/* 21Sep09: on windows, a non-blocking connect() that fails does not come up as writable.
|
|
988
|
+
Instead, it is added to the error set. See http://www.mail-archive.com/openssl-users@openssl.org/msg58500.html
|
|
989
|
+
*/
|
|
990
|
+
if (ed->IsConnectPending())
|
|
991
|
+
rb_fd_set (sd, &(SelectData->fderrors));
|
|
992
|
+
#endif
|
|
993
|
+
|
|
994
|
+
if (SelectData->maxsocket < sd)
|
|
995
|
+
SelectData->maxsocket = sd;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
{ // read and write the sockets
|
|
1000
|
+
SelectData->tv = _TimeTilNextEvent();
|
|
1001
|
+
int s = SelectData->_Select();
|
|
1002
|
+
if (s > 0) {
|
|
1003
|
+
/* Changed 01Jun07. We used to handle the Loop-breaker right here.
|
|
1004
|
+
* Now we do it AFTER all the regular descriptors. There's an
|
|
1005
|
+
* incredibly important and subtle reason for this. Code on
|
|
1006
|
+
* loop breakers is sometimes used to cause the reactor core to
|
|
1007
|
+
* cycle (for example, to allow outbound network buffers to drain).
|
|
1008
|
+
* If a loop-breaker handler reschedules itself (say, after determining
|
|
1009
|
+
* that the write buffers are still too full), then it will execute
|
|
1010
|
+
* IMMEDIATELY if _ReadLoopBreaker is done here instead of after
|
|
1011
|
+
* the other descriptors are processed. That defeats the whole purpose.
|
|
1012
|
+
*/
|
|
1013
|
+
for (i=0; i < Descriptors.size(); i++) {
|
|
1014
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
1015
|
+
assert (ed);
|
|
1016
|
+
SOCKET sd = ed->GetSocket();
|
|
1017
|
+
if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
|
|
1018
|
+
continue;
|
|
1019
|
+
assert (sd != INVALID_SOCKET);
|
|
1020
|
+
|
|
1021
|
+
if (rb_fd_isset (sd, &(SelectData->fdwrites))) {
|
|
1022
|
+
// Double-check SelectForWrite() still returns true. If not, one of the callbacks must have
|
|
1023
|
+
// modified some value since we checked SelectForWrite() earlier in this method.
|
|
1024
|
+
if (ed->SelectForWrite())
|
|
1025
|
+
ed->Write();
|
|
1026
|
+
}
|
|
1027
|
+
if (rb_fd_isset (sd, &(SelectData->fdreads)))
|
|
1028
|
+
ed->Read();
|
|
1029
|
+
if (rb_fd_isset (sd, &(SelectData->fderrors)))
|
|
1030
|
+
ed->HandleError();
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (rb_fd_isset (LoopBreakerReader, &(SelectData->fdreads)))
|
|
1034
|
+
_ReadLoopBreaker();
|
|
1035
|
+
}
|
|
1036
|
+
else if (s < 0) {
|
|
1037
|
+
switch (errno) {
|
|
1038
|
+
case EBADF:
|
|
1039
|
+
_CleanBadDescriptors();
|
|
1040
|
+
break;
|
|
1041
|
+
case EINVAL:
|
|
1042
|
+
throw std::runtime_error ("Somehow EM passed an invalid nfds or invalid timeout to select(2), please report this!");
|
|
1043
|
+
break;
|
|
1044
|
+
default:
|
|
1045
|
+
// select can fail on error in a handful of ways.
|
|
1046
|
+
// If this happens, then wait for a little while to avoid busy-looping.
|
|
1047
|
+
// If the error was EINTR, we probably caught SIGCHLD or something,
|
|
1048
|
+
// so keep the wait short.
|
|
1049
|
+
timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
|
|
1050
|
+
EmSelect (0, NULL, NULL, NULL, &tv);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
void EventMachine_t::_CleanBadDescriptors()
|
|
1057
|
+
{
|
|
1058
|
+
size_t i;
|
|
1059
|
+
|
|
1060
|
+
for (i = 0; i < Descriptors.size(); i++) {
|
|
1061
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
1062
|
+
if (ed->ShouldDelete())
|
|
1063
|
+
continue;
|
|
1064
|
+
|
|
1065
|
+
if (rb_wait_for_single_fd(ed->GetSocket(), RB_WAITFD_PRI, NULL) < 0) {
|
|
1066
|
+
if (errno == EBADF)
|
|
1067
|
+
ed->ScheduleClose(false);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/********************************
|
|
1073
|
+
EventMachine_t::_ReadLoopBreaker
|
|
1074
|
+
********************************/
|
|
1075
|
+
|
|
1076
|
+
void EventMachine_t::_ReadLoopBreaker()
|
|
1077
|
+
{
|
|
1078
|
+
/* The loop breaker has selected readable.
|
|
1079
|
+
* Read it ONCE (it may block if we try to read it twice)
|
|
1080
|
+
* and send a loop-break event back to user code.
|
|
1081
|
+
*/
|
|
1082
|
+
char buffer [1024];
|
|
1083
|
+
(void)read (LoopBreakerReader, buffer, sizeof(buffer));
|
|
1084
|
+
if (EventCallback)
|
|
1085
|
+
(*EventCallback)(0, EM_LOOPBREAK_SIGNAL, "", 0);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
/**************************
|
|
1090
|
+
EventMachine_t::_RunTimers
|
|
1091
|
+
**************************/
|
|
1092
|
+
|
|
1093
|
+
void EventMachine_t::_RunTimers()
|
|
1094
|
+
{
|
|
1095
|
+
// These are caller-defined timer handlers.
|
|
1096
|
+
// We rely on the fact that multimaps sort by their keys to avoid
|
|
1097
|
+
// inspecting the whole list every time we come here.
|
|
1098
|
+
// Just keep inspecting and processing the list head until we hit
|
|
1099
|
+
// one that hasn't expired yet.
|
|
1100
|
+
|
|
1101
|
+
while (true) {
|
|
1102
|
+
std::multimap<uint64_t,Timer_t>::iterator i = Timers.begin();
|
|
1103
|
+
if (i == Timers.end())
|
|
1104
|
+
break;
|
|
1105
|
+
if (i->first > MyCurrentLoopTime)
|
|
1106
|
+
break;
|
|
1107
|
+
if (EventCallback)
|
|
1108
|
+
(*EventCallback) (0, EM_TIMER_FIRED, NULL, i->second.GetBinding());
|
|
1109
|
+
Timers.erase (i);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/*********************************************
|
|
1114
|
+
EventMachine_t::_GetNumberOfOutstandingTimers
|
|
1115
|
+
*********************************************/
|
|
1116
|
+
|
|
1117
|
+
size_t EventMachine_t::GetTimerCount()
|
|
1118
|
+
{
|
|
1119
|
+
return Timers.size();
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
/***********************************
|
|
1124
|
+
EventMachine_t::InstallOneshotTimer
|
|
1125
|
+
***********************************/
|
|
1126
|
+
|
|
1127
|
+
const uintptr_t EventMachine_t::InstallOneshotTimer (uint64_t milliseconds)
|
|
1128
|
+
{
|
|
1129
|
+
if (Timers.size() > MaxOutstandingTimers)
|
|
1130
|
+
return false;
|
|
1131
|
+
|
|
1132
|
+
uint64_t fire_at = GetRealTime();
|
|
1133
|
+
fire_at += ((uint64_t)milliseconds) * 1000LL;
|
|
1134
|
+
|
|
1135
|
+
Timer_t t;
|
|
1136
|
+
#ifndef HAVE_MAKE_PAIR
|
|
1137
|
+
std::multimap<uint64_t,Timer_t>::iterator i = Timers.insert (std::multimap<uint64_t,Timer_t>::value_type (fire_at, t));
|
|
1138
|
+
#else
|
|
1139
|
+
std::multimap<uint64_t,Timer_t>::iterator i = Timers.insert (std::make_pair (fire_at, t));
|
|
1140
|
+
#endif
|
|
1141
|
+
return i->second.GetBinding();
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
/*******************************
|
|
1146
|
+
EventMachine_t::ConnectToServer
|
|
1147
|
+
*******************************/
|
|
1148
|
+
|
|
1149
|
+
const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind_port, const char *server, int port)
|
|
1150
|
+
{
|
|
1151
|
+
/* We want to spend no more than a few seconds waiting for a connection
|
|
1152
|
+
* to a remote host. So we use a nonblocking connect.
|
|
1153
|
+
* Linux disobeys the usual rules for nonblocking connects.
|
|
1154
|
+
* Per Stevens (UNP p.410), you expect a nonblocking connect to select
|
|
1155
|
+
* both readable and writable on error, and not to return EINPROGRESS
|
|
1156
|
+
* if the connect can be fulfilled immediately. Linux violates both
|
|
1157
|
+
* of these expectations.
|
|
1158
|
+
* Any kind of nonblocking connect on Linux returns EINPROGRESS.
|
|
1159
|
+
* The socket will then return writable when the disposition of the
|
|
1160
|
+
* connect is known, but it will not also be readable in case of
|
|
1161
|
+
* error! Weirdly, it will be readable in case there is data to read!!!
|
|
1162
|
+
* (Which can happen with protocols like SSH and SMTP.)
|
|
1163
|
+
* I suppose if you were so inclined you could consider this logical,
|
|
1164
|
+
* but it's not the way Unix has historically done it.
|
|
1165
|
+
* So we ignore the readable flag and read getsockopt to see if there
|
|
1166
|
+
* was an error connecting. A select timeout works as expected.
|
|
1167
|
+
* In regard to getsockopt: Linux does the Berkeley-style thing,
|
|
1168
|
+
* not the Solaris-style, and returns zero with the error code in
|
|
1169
|
+
* the error parameter.
|
|
1170
|
+
* Return the binding-text of the newly-created pending connection,
|
|
1171
|
+
* or NULL if there was a problem.
|
|
1172
|
+
*/
|
|
1173
|
+
|
|
1174
|
+
if (!server || !*server || !port)
|
|
1175
|
+
throw std::runtime_error ("invalid server or port");
|
|
1176
|
+
|
|
1177
|
+
struct sockaddr_storage bind_as;
|
|
1178
|
+
size_t bind_as_len = sizeof bind_as;
|
|
1179
|
+
int gai = name2address (server, port, SOCK_STREAM, (struct sockaddr *)&bind_as, &bind_as_len);
|
|
1180
|
+
if (gai != 0) {
|
|
1181
|
+
char buf [200];
|
|
1182
|
+
snprintf (buf, sizeof(buf)-1, "unable to resolve address: %s", gai_strerror(gai));
|
|
1183
|
+
throw std::runtime_error (buf);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
SOCKET sd = EmSocket (bind_as.ss_family, SOCK_STREAM, 0);
|
|
1187
|
+
if (sd == INVALID_SOCKET) {
|
|
1188
|
+
char buf [200];
|
|
1189
|
+
snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno));
|
|
1190
|
+
throw std::runtime_error (buf);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// From here on, ALL error returns must close the socket.
|
|
1194
|
+
// Set the new socket nonblocking.
|
|
1195
|
+
if (!SetSocketNonblocking (sd)) {
|
|
1196
|
+
close (sd);
|
|
1197
|
+
throw std::runtime_error ("unable to set socket as non-blocking");
|
|
1198
|
+
}
|
|
1199
|
+
// Disable slow-start (Nagle algorithm).
|
|
1200
|
+
int one = 1;
|
|
1201
|
+
setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
|
|
1202
|
+
// Set reuseaddr to improve performance on restarts
|
|
1203
|
+
setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one));
|
|
1204
|
+
|
|
1205
|
+
if (bind_addr) {
|
|
1206
|
+
struct sockaddr_storage bind_to;
|
|
1207
|
+
size_t bind_to_len = sizeof bind_to;
|
|
1208
|
+
gai = name2address (bind_addr, bind_port, SOCK_STREAM, (struct sockaddr *)&bind_to, &bind_to_len);
|
|
1209
|
+
if (gai != 0) {
|
|
1210
|
+
close (sd);
|
|
1211
|
+
char buf [200];
|
|
1212
|
+
snprintf (buf, sizeof(buf)-1, "invalid bind address: %s", gai_strerror(gai));
|
|
1213
|
+
throw std::runtime_error (buf);
|
|
1214
|
+
}
|
|
1215
|
+
if (bind (sd, (struct sockaddr *)&bind_to, bind_to_len) < 0) {
|
|
1216
|
+
close (sd);
|
|
1217
|
+
throw std::runtime_error ("couldn't bind to address");
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
uintptr_t out = 0;
|
|
1222
|
+
|
|
1223
|
+
#ifdef OS_UNIX
|
|
1224
|
+
int e_reason = 0;
|
|
1225
|
+
if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) {
|
|
1226
|
+
// This is a connect success, which Linux appears
|
|
1227
|
+
// never to give when the socket is nonblocking,
|
|
1228
|
+
// even if the connection is intramachine or to
|
|
1229
|
+
// localhost.
|
|
1230
|
+
|
|
1231
|
+
/* Changed this branch 08Aug06. Evidently some kernels
|
|
1232
|
+
* (FreeBSD for example) will actually return success from
|
|
1233
|
+
* a nonblocking connect. This is a pretty simple case,
|
|
1234
|
+
* just set up the new connection and clear the pending flag.
|
|
1235
|
+
* Thanks to Chris Ochs for helping track this down.
|
|
1236
|
+
* This branch never gets taken on Linux or (oddly) OSX.
|
|
1237
|
+
* The original behavior was to throw an unimplemented,
|
|
1238
|
+
* which the user saw as a fatal exception. Very unfriendly.
|
|
1239
|
+
*
|
|
1240
|
+
* Tweaked 10Aug06. Even though the connect disposition is
|
|
1241
|
+
* known, we still set the connect-pending flag. That way
|
|
1242
|
+
* some needed initialization will happen in the ConnectionDescriptor.
|
|
1243
|
+
* (To wit, the ConnectionCompleted event gets sent to the client.)
|
|
1244
|
+
*/
|
|
1245
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
|
1246
|
+
if (!cd)
|
|
1247
|
+
throw std::runtime_error ("no connection allocated");
|
|
1248
|
+
cd->SetConnectPending (true);
|
|
1249
|
+
Add (cd);
|
|
1250
|
+
out = cd->GetBinding();
|
|
1251
|
+
}
|
|
1252
|
+
else if (errno == EINPROGRESS) {
|
|
1253
|
+
// Errno will generally always be EINPROGRESS, but on Linux
|
|
1254
|
+
// we have to look at getsockopt to be sure what really happened.
|
|
1255
|
+
int error = 0;
|
|
1256
|
+
socklen_t len;
|
|
1257
|
+
len = sizeof(error);
|
|
1258
|
+
int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
|
|
1259
|
+
if ((o == 0) && (error == 0)) {
|
|
1260
|
+
// Here, there's no disposition.
|
|
1261
|
+
// Put the connection on the stack and wait for it to complete
|
|
1262
|
+
// or time out.
|
|
1263
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
|
1264
|
+
if (!cd)
|
|
1265
|
+
throw std::runtime_error ("no connection allocated");
|
|
1266
|
+
cd->SetConnectPending (true);
|
|
1267
|
+
Add (cd);
|
|
1268
|
+
out = cd->GetBinding();
|
|
1269
|
+
} else {
|
|
1270
|
+
// Fall through to the !out case below.
|
|
1271
|
+
e_reason = error;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
// The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
|
|
1276
|
+
// Fall through to the !out case below
|
|
1277
|
+
e_reason = errno;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
if (!out) {
|
|
1281
|
+
/* This could be connection refused or some such thing.
|
|
1282
|
+
* We will come here on Linux if a localhost connection fails.
|
|
1283
|
+
* Changed 16Jul06: Originally this branch was a no-op, and
|
|
1284
|
+
* we'd drop down to the end of the method, close the socket,
|
|
1285
|
+
* and return NULL, which would cause the caller to GET A
|
|
1286
|
+
* FATAL EXCEPTION. Now we keep the socket around but schedule an
|
|
1287
|
+
* immediate close on it, so the caller will get a close-event
|
|
1288
|
+
* scheduled on it. This was only an issue for localhost connections
|
|
1289
|
+
* to non-listening ports. We may eventually need to revise this
|
|
1290
|
+
* revised behavior, in case it causes problems like making it hard
|
|
1291
|
+
* for people to know that a failure occurred.
|
|
1292
|
+
*/
|
|
1293
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
|
1294
|
+
if (!cd)
|
|
1295
|
+
throw std::runtime_error ("no connection allocated");
|
|
1296
|
+
cd->SetUnbindReasonCode (e_reason);
|
|
1297
|
+
cd->ScheduleClose (false);
|
|
1298
|
+
Add (cd);
|
|
1299
|
+
out = cd->GetBinding();
|
|
1300
|
+
}
|
|
1301
|
+
#endif
|
|
1302
|
+
|
|
1303
|
+
#ifdef OS_WIN32
|
|
1304
|
+
if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) {
|
|
1305
|
+
// This is a connect success, which Windows appears
|
|
1306
|
+
// never to give when the socket is nonblocking,
|
|
1307
|
+
// even if the connection is intramachine or to
|
|
1308
|
+
// localhost.
|
|
1309
|
+
throw std::runtime_error ("unimplemented");
|
|
1310
|
+
}
|
|
1311
|
+
else if (WSAGetLastError() == WSAEWOULDBLOCK) {
|
|
1312
|
+
// Here, there's no disposition.
|
|
1313
|
+
// Windows appears not to surface refused connections or
|
|
1314
|
+
// such stuff at this point.
|
|
1315
|
+
// Put the connection on the stack and wait for it to complete
|
|
1316
|
+
// or time out.
|
|
1317
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
|
|
1318
|
+
if (!cd)
|
|
1319
|
+
throw std::runtime_error ("no connection allocated");
|
|
1320
|
+
cd->SetConnectPending (true);
|
|
1321
|
+
Add (cd);
|
|
1322
|
+
out = cd->GetBinding();
|
|
1323
|
+
}
|
|
1324
|
+
else {
|
|
1325
|
+
// The error from connect was something other then WSAEWOULDBLOCK.
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
#endif
|
|
1329
|
+
|
|
1330
|
+
if (!out)
|
|
1331
|
+
close (sd);
|
|
1332
|
+
return out;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
/***********************************
|
|
1336
|
+
EventMachine_t::ConnectToUnixServer
|
|
1337
|
+
***********************************/
|
|
1338
|
+
|
|
1339
|
+
#ifdef OS_UNIX
|
|
1340
|
+
const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server)
|
|
1341
|
+
{
|
|
1342
|
+
/* Connect to a Unix-domain server, which by definition is running
|
|
1343
|
+
* on the same host.
|
|
1344
|
+
* There is no meaningful implementation on Windows.
|
|
1345
|
+
* There's no need to do a nonblocking connect, since the connection
|
|
1346
|
+
* is always local and can always be fulfilled immediately.
|
|
1347
|
+
*/
|
|
1348
|
+
|
|
1349
|
+
uintptr_t out = 0;
|
|
1350
|
+
|
|
1351
|
+
if (!server || !*server)
|
|
1352
|
+
return 0;
|
|
1353
|
+
|
|
1354
|
+
sockaddr_un pun;
|
|
1355
|
+
memset (&pun, 0, sizeof(pun));
|
|
1356
|
+
pun.sun_family = AF_LOCAL;
|
|
1357
|
+
|
|
1358
|
+
// You ordinarily expect the server name field to be at least 1024 bytes long,
|
|
1359
|
+
// but on Linux it can be MUCH shorter.
|
|
1360
|
+
if (strlen(server) >= sizeof(pun.sun_path))
|
|
1361
|
+
throw std::runtime_error ("unix-domain server name is too long");
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
strcpy (pun.sun_path, server);
|
|
1365
|
+
|
|
1366
|
+
SOCKET fd = EmSocket (AF_LOCAL, SOCK_STREAM, 0);
|
|
1367
|
+
if (fd == INVALID_SOCKET)
|
|
1368
|
+
return 0;
|
|
1369
|
+
|
|
1370
|
+
// From here on, ALL error returns must close the socket.
|
|
1371
|
+
// NOTE: At this point, the socket is still a blocking socket.
|
|
1372
|
+
if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) {
|
|
1373
|
+
close (fd);
|
|
1374
|
+
return 0;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Set the newly-connected socket nonblocking.
|
|
1378
|
+
if (!SetSocketNonblocking (fd)) {
|
|
1379
|
+
close (fd);
|
|
1380
|
+
return 0;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Set up a connection descriptor and add it to the event-machine.
|
|
1384
|
+
// Observe, even though we know the connection status is connect-success,
|
|
1385
|
+
// we still set the "pending" flag, so some needed initializations take
|
|
1386
|
+
// place.
|
|
1387
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
|
|
1388
|
+
if (!cd)
|
|
1389
|
+
throw std::runtime_error ("no connection allocated");
|
|
1390
|
+
cd->SetConnectPending (true);
|
|
1391
|
+
Add (cd);
|
|
1392
|
+
out = cd->GetBinding();
|
|
1393
|
+
|
|
1394
|
+
if (!out)
|
|
1395
|
+
close (fd);
|
|
1396
|
+
|
|
1397
|
+
return out;
|
|
1398
|
+
}
|
|
1399
|
+
#else
|
|
1400
|
+
const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server UNUSED)
|
|
1401
|
+
{
|
|
1402
|
+
throw std::runtime_error ("unix-domain connection unavailable on this platform");
|
|
1403
|
+
}
|
|
1404
|
+
#endif
|
|
1405
|
+
|
|
1406
|
+
/************************
|
|
1407
|
+
EventMachine_t::AttachFD
|
|
1408
|
+
************************/
|
|
1409
|
+
|
|
1410
|
+
const uintptr_t EventMachine_t::AttachFD (SOCKET fd, bool watch_mode)
|
|
1411
|
+
{
|
|
1412
|
+
#ifdef OS_UNIX
|
|
1413
|
+
if (fcntl(fd, F_GETFL, 0) < 0) {
|
|
1414
|
+
if (errno) {
|
|
1415
|
+
throw std::runtime_error (strerror(errno));
|
|
1416
|
+
} else {
|
|
1417
|
+
throw std::runtime_error ("invalid file descriptor");
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
#endif
|
|
1421
|
+
|
|
1422
|
+
#ifdef OS_WIN32
|
|
1423
|
+
// TODO: add better check for invalid file descriptors (see ioctlsocket or getsockopt)
|
|
1424
|
+
if (fd == INVALID_SOCKET)
|
|
1425
|
+
throw std::runtime_error ("invalid file descriptor");
|
|
1426
|
+
#endif
|
|
1427
|
+
|
|
1428
|
+
{// Check for duplicate descriptors
|
|
1429
|
+
size_t i;
|
|
1430
|
+
for (i = 0; i < Descriptors.size(); i++) {
|
|
1431
|
+
EventableDescriptor *ed = Descriptors[i];
|
|
1432
|
+
assert (ed);
|
|
1433
|
+
if (ed->GetSocket() == fd)
|
|
1434
|
+
throw std::runtime_error ("adding existing descriptor");
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
for (i = 0; i < NewDescriptors.size(); i++) {
|
|
1438
|
+
EventableDescriptor *ed = NewDescriptors[i];
|
|
1439
|
+
assert (ed);
|
|
1440
|
+
if (ed->GetSocket() == fd)
|
|
1441
|
+
throw std::runtime_error ("adding existing new descriptor");
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
if (!watch_mode)
|
|
1446
|
+
SetSocketNonblocking(fd);
|
|
1447
|
+
|
|
1448
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
|
|
1449
|
+
if (!cd)
|
|
1450
|
+
throw std::runtime_error ("no connection allocated");
|
|
1451
|
+
|
|
1452
|
+
cd->SetAttached(true);
|
|
1453
|
+
cd->SetWatchOnly(watch_mode);
|
|
1454
|
+
cd->SetConnectPending (false);
|
|
1455
|
+
|
|
1456
|
+
Add (cd);
|
|
1457
|
+
|
|
1458
|
+
const uintptr_t out = cd->GetBinding();
|
|
1459
|
+
return out;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
/************************
|
|
1463
|
+
EventMachine_t::DetachFD
|
|
1464
|
+
************************/
|
|
1465
|
+
|
|
1466
|
+
int EventMachine_t::DetachFD (EventableDescriptor *ed)
|
|
1467
|
+
{
|
|
1468
|
+
if (!ed)
|
|
1469
|
+
throw std::runtime_error ("detaching bad descriptor");
|
|
1470
|
+
|
|
1471
|
+
SOCKET fd = ed->GetSocket();
|
|
1472
|
+
|
|
1473
|
+
#ifdef HAVE_EPOLL
|
|
1474
|
+
if (Poller == Poller_Epoll) {
|
|
1475
|
+
if (ed->GetSocket() != INVALID_SOCKET) {
|
|
1476
|
+
assert (epfd != -1);
|
|
1477
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
|
1478
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
|
1479
|
+
if (e && (errno != ENOENT) && (errno != EBADF)) {
|
|
1480
|
+
char buf [200];
|
|
1481
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
|
1482
|
+
throw std::runtime_error (buf);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
#endif
|
|
1487
|
+
|
|
1488
|
+
#ifdef HAVE_KQUEUE
|
|
1489
|
+
if (Poller == Poller_Kqueue) {
|
|
1490
|
+
// remove any read/write events for this fd
|
|
1491
|
+
struct kevent k;
|
|
1492
|
+
#ifdef __NetBSD__
|
|
1493
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, (intptr_t)ed);
|
|
1494
|
+
#else
|
|
1495
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, ed);
|
|
1496
|
+
#endif
|
|
1497
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
|
1498
|
+
if (t < 0 && (errno != ENOENT) && (errno != EBADF)) {
|
|
1499
|
+
char buf [200];
|
|
1500
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete kqueue event: %s", strerror(errno));
|
|
1501
|
+
throw std::runtime_error (buf);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
#endif
|
|
1505
|
+
|
|
1506
|
+
// Prevent the descriptor from being modified, in case DetachFD was called from a timer or next_tick
|
|
1507
|
+
ModifiedDescriptors.erase (ed);
|
|
1508
|
+
|
|
1509
|
+
// Prevent the descriptor from being added, in case DetachFD was called in the same tick as AttachFD
|
|
1510
|
+
for (size_t i = 0; i < NewDescriptors.size(); i++) {
|
|
1511
|
+
if (ed == NewDescriptors[i]) {
|
|
1512
|
+
NewDescriptors.erase(NewDescriptors.begin() + i);
|
|
1513
|
+
break;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// Set MySocket = INVALID_SOCKET so ShouldDelete() is true (and the descriptor gets deleted and removed),
|
|
1518
|
+
// and also to prevent anyone from calling close() on the detached fd
|
|
1519
|
+
ed->SetSocketInvalid();
|
|
1520
|
+
|
|
1521
|
+
return fd;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/************
|
|
1525
|
+
name2address
|
|
1526
|
+
************/
|
|
1527
|
+
|
|
1528
|
+
int EventMachine_t::name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len)
|
|
1529
|
+
{
|
|
1530
|
+
if (!server || !*server)
|
|
1531
|
+
server = "0.0.0.0";
|
|
1532
|
+
|
|
1533
|
+
struct addrinfo *ai;
|
|
1534
|
+
struct addrinfo hints;
|
|
1535
|
+
memset (&hints, 0, sizeof(hints));
|
|
1536
|
+
hints.ai_socktype = socktype;
|
|
1537
|
+
hints.ai_family = AF_UNSPEC;
|
|
1538
|
+
hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
|
|
1539
|
+
|
|
1540
|
+
char portstr[12];
|
|
1541
|
+
snprintf(portstr, sizeof(portstr), "%u", port);
|
|
1542
|
+
|
|
1543
|
+
int gai = getaddrinfo (server, portstr, &hints, &ai);
|
|
1544
|
+
if (gai == 0) {
|
|
1545
|
+
assert (ai->ai_addrlen <= *addr_len);
|
|
1546
|
+
memcpy (addr, ai->ai_addr, ai->ai_addrlen);
|
|
1547
|
+
*addr_len = ai->ai_addrlen;
|
|
1548
|
+
freeaddrinfo(ai);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
return gai;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
/*******************************
|
|
1556
|
+
EventMachine_t::CreateTcpServer
|
|
1557
|
+
*******************************/
|
|
1558
|
+
|
|
1559
|
+
const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port)
|
|
1560
|
+
{
|
|
1561
|
+
/* Create a TCP-acceptor (server) socket and add it to the event machine.
|
|
1562
|
+
* Return the binding of the new acceptor to the caller.
|
|
1563
|
+
* This binding will be referenced when the new acceptor sends events
|
|
1564
|
+
* to indicate accepted connections.
|
|
1565
|
+
*/
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
struct sockaddr_storage bind_here;
|
|
1569
|
+
size_t bind_here_len = sizeof bind_here;
|
|
1570
|
+
if (0 != name2address (server, port, SOCK_STREAM, (struct sockaddr *)&bind_here, &bind_here_len))
|
|
1571
|
+
return 0;
|
|
1572
|
+
|
|
1573
|
+
SOCKET sd_accept = EmSocket (bind_here.ss_family, SOCK_STREAM, 0);
|
|
1574
|
+
if (sd_accept == INVALID_SOCKET) {
|
|
1575
|
+
goto fail;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
{ // set reuseaddr to improve performance on restarts.
|
|
1579
|
+
int oval = 1;
|
|
1580
|
+
if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
|
|
1581
|
+
//__warning ("setsockopt failed while creating listener","");
|
|
1582
|
+
goto fail;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
{ // set CLOEXEC. Only makes sense on Unix
|
|
1587
|
+
#ifdef OS_UNIX
|
|
1588
|
+
int cloexec = fcntl (sd_accept, F_GETFD, 0);
|
|
1589
|
+
assert (cloexec >= 0);
|
|
1590
|
+
cloexec |= FD_CLOEXEC;
|
|
1591
|
+
fcntl (sd_accept, F_SETFD, cloexec);
|
|
1592
|
+
#endif
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
|
|
1596
|
+
if (bind (sd_accept, (struct sockaddr *)&bind_here, bind_here_len)) {
|
|
1597
|
+
//__warning ("binding failed");
|
|
1598
|
+
goto fail;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
if (listen (sd_accept, 100)) {
|
|
1602
|
+
//__warning ("listen failed");
|
|
1603
|
+
goto fail;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
return AttachSD(sd_accept);
|
|
1607
|
+
|
|
1608
|
+
fail:
|
|
1609
|
+
if (sd_accept != INVALID_SOCKET)
|
|
1610
|
+
close (sd_accept);
|
|
1611
|
+
return 0;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
|
|
1615
|
+
/**********************************
|
|
1616
|
+
EventMachine_t::OpenDatagramSocket
|
|
1617
|
+
**********************************/
|
|
1618
|
+
|
|
1619
|
+
const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int port)
|
|
1620
|
+
{
|
|
1621
|
+
uintptr_t output_binding = 0;
|
|
1622
|
+
|
|
1623
|
+
struct sockaddr_storage bind_here;
|
|
1624
|
+
size_t bind_here_len = sizeof bind_here;
|
|
1625
|
+
if (0 != name2address (address, port, SOCK_DGRAM, (struct sockaddr *)&bind_here, &bind_here_len))
|
|
1626
|
+
return 0;
|
|
1627
|
+
|
|
1628
|
+
// from here on, early returns must close the socket!
|
|
1629
|
+
SOCKET sd = EmSocket (bind_here.ss_family, SOCK_DGRAM, 0);
|
|
1630
|
+
if (sd == INVALID_SOCKET)
|
|
1631
|
+
goto fail;
|
|
1632
|
+
|
|
1633
|
+
{ // set the SO_REUSEADDR on the socket before we bind, otherwise it won't work for a second one
|
|
1634
|
+
int oval = 1;
|
|
1635
|
+
if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0)
|
|
1636
|
+
goto fail;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// Set the new socket nonblocking.
|
|
1640
|
+
if (!SetSocketNonblocking (sd))
|
|
1641
|
+
goto fail;
|
|
1642
|
+
|
|
1643
|
+
if (bind (sd, (struct sockaddr *)&bind_here, bind_here_len) != 0)
|
|
1644
|
+
goto fail;
|
|
1645
|
+
|
|
1646
|
+
{ // Looking good.
|
|
1647
|
+
DatagramDescriptor *ds = new DatagramDescriptor (sd, this);
|
|
1648
|
+
if (!ds)
|
|
1649
|
+
throw std::runtime_error ("unable to allocate datagram-socket");
|
|
1650
|
+
Add (ds);
|
|
1651
|
+
output_binding = ds->GetBinding();
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
return output_binding;
|
|
1655
|
+
|
|
1656
|
+
fail:
|
|
1657
|
+
if (sd != INVALID_SOCKET)
|
|
1658
|
+
close (sd);
|
|
1659
|
+
return 0;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
|
|
1664
|
+
/*******************
|
|
1665
|
+
EventMachine_t::Add
|
|
1666
|
+
*******************/
|
|
1667
|
+
|
|
1668
|
+
void EventMachine_t::Add (EventableDescriptor *ed)
|
|
1669
|
+
{
|
|
1670
|
+
if (!ed)
|
|
1671
|
+
throw std::runtime_error ("added bad descriptor");
|
|
1672
|
+
ed->SetEventCallback (EventCallback);
|
|
1673
|
+
NewDescriptors.push_back (ed);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
|
|
1677
|
+
/*******************************
|
|
1678
|
+
EventMachine_t::ArmKqueueWriter
|
|
1679
|
+
*******************************/
|
|
1680
|
+
|
|
1681
|
+
#ifdef HAVE_KQUEUE
|
|
1682
|
+
void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
|
|
1683
|
+
{
|
|
1684
|
+
if (Poller == Poller_Kqueue) {
|
|
1685
|
+
if (!ed)
|
|
1686
|
+
throw std::runtime_error ("added bad descriptor");
|
|
1687
|
+
struct kevent k;
|
|
1688
|
+
#ifdef __NetBSD__
|
|
1689
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, (intptr_t)ed);
|
|
1690
|
+
#else
|
|
1691
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, ed);
|
|
1692
|
+
#endif
|
|
1693
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
|
1694
|
+
if (t < 0) {
|
|
1695
|
+
char buf [200];
|
|
1696
|
+
snprintf (buf, sizeof(buf)-1, "arm kqueue writer failed on %d: %s", ed->GetSocket(), strerror(errno));
|
|
1697
|
+
throw std::runtime_error (buf);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
#else
|
|
1702
|
+
void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed UNUSED) { }
|
|
1703
|
+
#endif
|
|
1704
|
+
|
|
1705
|
+
/*******************************
|
|
1706
|
+
EventMachine_t::ArmKqueueReader
|
|
1707
|
+
*******************************/
|
|
1708
|
+
|
|
1709
|
+
#ifdef HAVE_KQUEUE
|
|
1710
|
+
void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
|
|
1711
|
+
{
|
|
1712
|
+
if (Poller == Poller_Kqueue) {
|
|
1713
|
+
if (!ed)
|
|
1714
|
+
throw std::runtime_error ("added bad descriptor");
|
|
1715
|
+
struct kevent k;
|
|
1716
|
+
#ifdef __NetBSD__
|
|
1717
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, (intptr_t)ed);
|
|
1718
|
+
#else
|
|
1719
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
|
|
1720
|
+
#endif
|
|
1721
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
|
1722
|
+
if (t < 0) {
|
|
1723
|
+
char buf [200];
|
|
1724
|
+
snprintf (buf, sizeof(buf)-1, "arm kqueue reader failed on %d: %s", ed->GetSocket(), strerror(errno));
|
|
1725
|
+
throw std::runtime_error (buf);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
#else
|
|
1730
|
+
void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed UNUSED) { }
|
|
1731
|
+
#endif
|
|
1732
|
+
|
|
1733
|
+
/**********************************
|
|
1734
|
+
EventMachine_t::_AddNewDescriptors
|
|
1735
|
+
**********************************/
|
|
1736
|
+
|
|
1737
|
+
void EventMachine_t::_AddNewDescriptors()
|
|
1738
|
+
{
|
|
1739
|
+
/* Avoid adding descriptors to the main descriptor list
|
|
1740
|
+
* while we're actually traversing the list.
|
|
1741
|
+
* Any descriptors that are added as a result of processing timers
|
|
1742
|
+
* or acceptors should go on a temporary queue and then added
|
|
1743
|
+
* while we're not traversing the main list.
|
|
1744
|
+
* Also, it (rarely) happens that a newly-created descriptor
|
|
1745
|
+
* is immediately scheduled to close. It might be a good
|
|
1746
|
+
* idea not to bother scheduling these for I/O but if
|
|
1747
|
+
* we do that, we might bypass some important processing.
|
|
1748
|
+
*/
|
|
1749
|
+
|
|
1750
|
+
for (size_t i = 0; i < NewDescriptors.size(); i++) {
|
|
1751
|
+
EventableDescriptor *ed = NewDescriptors[i];
|
|
1752
|
+
if (ed == NULL)
|
|
1753
|
+
throw std::runtime_error ("adding bad descriptor");
|
|
1754
|
+
|
|
1755
|
+
#if HAVE_EPOLL
|
|
1756
|
+
if (Poller == Poller_Epoll) {
|
|
1757
|
+
assert (epfd != -1);
|
|
1758
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_ADD, ed->GetSocket(), ed->GetEpollEvent());
|
|
1759
|
+
if (e) {
|
|
1760
|
+
char buf [200];
|
|
1761
|
+
snprintf (buf, sizeof(buf)-1, "unable to add new descriptor: %s", strerror(errno));
|
|
1762
|
+
throw std::runtime_error (buf);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
#endif
|
|
1766
|
+
|
|
1767
|
+
#if HAVE_KQUEUE
|
|
1768
|
+
/*
|
|
1769
|
+
if (Poller == Poller_Kqueue) {
|
|
1770
|
+
// INCOMPLETE. Some descriptors don't want to be readable.
|
|
1771
|
+
assert (kqfd != -1);
|
|
1772
|
+
struct kevent k;
|
|
1773
|
+
#ifdef __NetBSD__
|
|
1774
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, (intptr_t)ed);
|
|
1775
|
+
#else
|
|
1776
|
+
EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
|
|
1777
|
+
#endif
|
|
1778
|
+
int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
|
|
1779
|
+
assert (t == 0);
|
|
1780
|
+
}
|
|
1781
|
+
*/
|
|
1782
|
+
#endif
|
|
1783
|
+
|
|
1784
|
+
QueueHeartbeat(ed);
|
|
1785
|
+
Descriptors.push_back (ed);
|
|
1786
|
+
}
|
|
1787
|
+
NewDescriptors.clear();
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
|
|
1791
|
+
/**********************************
|
|
1792
|
+
EventMachine_t::_ModifyDescriptors
|
|
1793
|
+
**********************************/
|
|
1794
|
+
|
|
1795
|
+
void EventMachine_t::_ModifyDescriptors()
|
|
1796
|
+
{
|
|
1797
|
+
/* For implementations which don't level check every descriptor on
|
|
1798
|
+
* every pass through the machine, as select does.
|
|
1799
|
+
* If we're not selecting, then descriptors need a way to signal to the
|
|
1800
|
+
* machine that their readable or writable status has changed.
|
|
1801
|
+
* That's what the ::Modify call is for. We do it this way to avoid
|
|
1802
|
+
* modifying descriptors during the loop traversal, where it can easily
|
|
1803
|
+
* happen that an object (like a UDP socket) gets data written on it by
|
|
1804
|
+
* the application during #post_init. That would take place BEFORE the
|
|
1805
|
+
* descriptor even gets added to the epoll descriptor, so the modify
|
|
1806
|
+
* operation will crash messily.
|
|
1807
|
+
* Another really messy possibility is for a descriptor to put itself
|
|
1808
|
+
* on the Modified list, and then get deleted before we get here.
|
|
1809
|
+
* Remember, deletes happen after the I/O traversal and before the
|
|
1810
|
+
* next pass through here. So we have to make sure when we delete a
|
|
1811
|
+
* descriptor to remove it from the Modified list.
|
|
1812
|
+
*/
|
|
1813
|
+
|
|
1814
|
+
#ifdef HAVE_EPOLL
|
|
1815
|
+
if (Poller == Poller_Epoll) {
|
|
1816
|
+
std::set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
|
|
1817
|
+
while (i != ModifiedDescriptors.end()) {
|
|
1818
|
+
assert (*i);
|
|
1819
|
+
_ModifyEpollEvent (*i);
|
|
1820
|
+
++i;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
#endif
|
|
1824
|
+
|
|
1825
|
+
#ifdef HAVE_KQUEUE
|
|
1826
|
+
if (Poller == Poller_Kqueue) {
|
|
1827
|
+
std::set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
|
|
1828
|
+
while (i != ModifiedDescriptors.end()) {
|
|
1829
|
+
assert (*i);
|
|
1830
|
+
if ((*i)->GetKqueueArmWrite())
|
|
1831
|
+
ArmKqueueWriter (*i);
|
|
1832
|
+
++i;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
#endif
|
|
1836
|
+
|
|
1837
|
+
ModifiedDescriptors.clear();
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
|
|
1841
|
+
/**********************
|
|
1842
|
+
EventMachine_t::Modify
|
|
1843
|
+
**********************/
|
|
1844
|
+
|
|
1845
|
+
void EventMachine_t::Modify (EventableDescriptor *ed)
|
|
1846
|
+
{
|
|
1847
|
+
if (!ed)
|
|
1848
|
+
throw std::runtime_error ("modified bad descriptor");
|
|
1849
|
+
ModifiedDescriptors.insert (ed);
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
|
|
1853
|
+
/***********************
|
|
1854
|
+
EventMachine_t::Deregister
|
|
1855
|
+
***********************/
|
|
1856
|
+
|
|
1857
|
+
void EventMachine_t::Deregister (EventableDescriptor *ed)
|
|
1858
|
+
{
|
|
1859
|
+
if (!ed)
|
|
1860
|
+
throw std::runtime_error ("modified bad descriptor");
|
|
1861
|
+
#ifdef HAVE_EPOLL
|
|
1862
|
+
// cut/paste from _CleanupSockets(). The error handling could be
|
|
1863
|
+
// refactored out of there, but it is cut/paste all over the
|
|
1864
|
+
// file already.
|
|
1865
|
+
if (Poller == Poller_Epoll) {
|
|
1866
|
+
assert (epfd != -1);
|
|
1867
|
+
assert (ed->GetSocket() != INVALID_SOCKET);
|
|
1868
|
+
int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
|
|
1869
|
+
// ENOENT or EBADF are not errors because the socket may be already closed when we get here.
|
|
1870
|
+
if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
|
|
1871
|
+
char buf [200];
|
|
1872
|
+
snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
|
|
1873
|
+
throw std::runtime_error (buf);
|
|
1874
|
+
}
|
|
1875
|
+
ModifiedDescriptors.erase(ed);
|
|
1876
|
+
}
|
|
1877
|
+
#endif
|
|
1878
|
+
|
|
1879
|
+
#ifdef HAVE_KQUEUE
|
|
1880
|
+
if (Poller == Poller_Kqueue) {
|
|
1881
|
+
assert (ed->GetSocket() != INVALID_SOCKET);
|
|
1882
|
+
|
|
1883
|
+
ModifiedDescriptors.erase(ed);
|
|
1884
|
+
}
|
|
1885
|
+
#endif
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
|
|
1889
|
+
/**************************************
|
|
1890
|
+
EventMachine_t::CreateUnixDomainServer
|
|
1891
|
+
**************************************/
|
|
1892
|
+
|
|
1893
|
+
#ifdef OS_UNIX
|
|
1894
|
+
const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename)
|
|
1895
|
+
{
|
|
1896
|
+
/* Create a UNIX-domain acceptor (server) socket and add it to the event machine.
|
|
1897
|
+
* Return the binding of the new acceptor to the caller.
|
|
1898
|
+
* This binding will be referenced when the new acceptor sends events
|
|
1899
|
+
* to indicate accepted connections.
|
|
1900
|
+
* THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS.
|
|
1901
|
+
*/
|
|
1902
|
+
|
|
1903
|
+
struct sockaddr_un s_sun;
|
|
1904
|
+
|
|
1905
|
+
SOCKET sd_accept = EmSocket (AF_LOCAL, SOCK_STREAM, 0);
|
|
1906
|
+
if (sd_accept == INVALID_SOCKET) {
|
|
1907
|
+
goto fail;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
if (!filename || !*filename)
|
|
1911
|
+
goto fail;
|
|
1912
|
+
unlink (filename);
|
|
1913
|
+
|
|
1914
|
+
bzero (&s_sun, sizeof(s_sun));
|
|
1915
|
+
s_sun.sun_family = AF_LOCAL;
|
|
1916
|
+
strncpy (s_sun.sun_path, filename, sizeof(s_sun.sun_path)-1);
|
|
1917
|
+
|
|
1918
|
+
// don't bother with reuseaddr for a local socket.
|
|
1919
|
+
|
|
1920
|
+
{ // set CLOEXEC. Only makes sense on Unix
|
|
1921
|
+
#ifdef OS_UNIX
|
|
1922
|
+
int cloexec = fcntl (sd_accept, F_GETFD, 0);
|
|
1923
|
+
assert (cloexec >= 0);
|
|
1924
|
+
cloexec |= FD_CLOEXEC;
|
|
1925
|
+
fcntl (sd_accept, F_SETFD, cloexec);
|
|
1926
|
+
#endif
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
if (bind (sd_accept, (struct sockaddr*)&s_sun, sizeof(s_sun))) {
|
|
1930
|
+
//__warning ("binding failed");
|
|
1931
|
+
goto fail;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
if (listen (sd_accept, 100)) {
|
|
1935
|
+
//__warning ("listen failed");
|
|
1936
|
+
goto fail;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
return AttachSD(sd_accept);
|
|
1940
|
+
|
|
1941
|
+
fail:
|
|
1942
|
+
if (sd_accept != INVALID_SOCKET)
|
|
1943
|
+
close (sd_accept);
|
|
1944
|
+
return 0;
|
|
1945
|
+
}
|
|
1946
|
+
#else
|
|
1947
|
+
const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename UNUSED)
|
|
1948
|
+
{
|
|
1949
|
+
throw std::runtime_error ("unix-domain server unavailable on this platform");
|
|
1950
|
+
}
|
|
1951
|
+
#endif
|
|
1952
|
+
|
|
1953
|
+
|
|
1954
|
+
/**************************************
|
|
1955
|
+
EventMachine_t::AttachSD
|
|
1956
|
+
**************************************/
|
|
1957
|
+
|
|
1958
|
+
const uintptr_t EventMachine_t::AttachSD (SOCKET sd_accept)
|
|
1959
|
+
{
|
|
1960
|
+
uintptr_t output_binding = 0;
|
|
1961
|
+
|
|
1962
|
+
{
|
|
1963
|
+
// Set the acceptor non-blocking.
|
|
1964
|
+
// THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.
|
|
1965
|
+
if (!SetSocketNonblocking (sd_accept)) {
|
|
1966
|
+
//int val = fcntl (sd_accept, F_GETFL, 0);
|
|
1967
|
+
//if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) {
|
|
1968
|
+
goto fail;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
{ // Looking good.
|
|
1973
|
+
AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this);
|
|
1974
|
+
if (!ad)
|
|
1975
|
+
throw std::runtime_error ("unable to allocate acceptor");
|
|
1976
|
+
Add (ad);
|
|
1977
|
+
output_binding = ad->GetBinding();
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
return output_binding;
|
|
1981
|
+
|
|
1982
|
+
fail:
|
|
1983
|
+
if (sd_accept != INVALID_SOCKET)
|
|
1984
|
+
close (sd_accept);
|
|
1985
|
+
return 0;
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
|
|
1989
|
+
/**************************
|
|
1990
|
+
EventMachine_t::Socketpair
|
|
1991
|
+
**************************/
|
|
1992
|
+
|
|
1993
|
+
#ifdef OS_UNIX
|
|
1994
|
+
const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings)
|
|
1995
|
+
{
|
|
1996
|
+
// Make sure the incoming array of command strings is sane.
|
|
1997
|
+
if (!cmd_strings)
|
|
1998
|
+
return 0;
|
|
1999
|
+
int j;
|
|
2000
|
+
for (j=0; j < 2048 && cmd_strings[j]; j++)
|
|
2001
|
+
;
|
|
2002
|
+
if ((j==0) || (j==2048))
|
|
2003
|
+
return 0;
|
|
2004
|
+
|
|
2005
|
+
uintptr_t output_binding = 0;
|
|
2006
|
+
|
|
2007
|
+
int sv[2];
|
|
2008
|
+
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
|
|
2009
|
+
return 0;
|
|
2010
|
+
// from here, all early returns must close the pair of sockets.
|
|
2011
|
+
|
|
2012
|
+
// Set the parent side of the socketpair nonblocking.
|
|
2013
|
+
// We don't care about the child side, and most child processes will expect their
|
|
2014
|
+
// stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out.
|
|
2015
|
+
// Obviously DON'T set CLOEXEC.
|
|
2016
|
+
if (!SetSocketNonblocking (sv[0])) {
|
|
2017
|
+
close (sv[0]);
|
|
2018
|
+
close (sv[1]);
|
|
2019
|
+
return 0;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
pid_t f = fork();
|
|
2023
|
+
if (f > 0) {
|
|
2024
|
+
close (sv[1]);
|
|
2025
|
+
PipeDescriptor *pd = new PipeDescriptor (sv[0], f, this);
|
|
2026
|
+
if (!pd)
|
|
2027
|
+
throw std::runtime_error ("unable to allocate pipe");
|
|
2028
|
+
Add (pd);
|
|
2029
|
+
output_binding = pd->GetBinding();
|
|
2030
|
+
}
|
|
2031
|
+
else if (f == 0) {
|
|
2032
|
+
close (sv[0]);
|
|
2033
|
+
dup2 (sv[1], STDIN_FILENO);
|
|
2034
|
+
close (sv[1]);
|
|
2035
|
+
dup2 (STDIN_FILENO, STDOUT_FILENO);
|
|
2036
|
+
execvp (cmd_strings[0], cmd_strings+1);
|
|
2037
|
+
exit (-1); // end the child process if the exec doesn't work.
|
|
2038
|
+
}
|
|
2039
|
+
else
|
|
2040
|
+
throw std::runtime_error ("no fork");
|
|
2041
|
+
|
|
2042
|
+
return output_binding;
|
|
2043
|
+
}
|
|
2044
|
+
#else
|
|
2045
|
+
const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings UNUSED)
|
|
2046
|
+
{
|
|
2047
|
+
throw std::runtime_error ("socketpair is currently unavailable on this platform");
|
|
2048
|
+
}
|
|
2049
|
+
#endif
|
|
2050
|
+
|
|
2051
|
+
|
|
2052
|
+
|
|
2053
|
+
/****************************
|
|
2054
|
+
EventMachine_t::OpenKeyboard
|
|
2055
|
+
****************************/
|
|
2056
|
+
|
|
2057
|
+
const uintptr_t EventMachine_t::OpenKeyboard()
|
|
2058
|
+
{
|
|
2059
|
+
KeyboardDescriptor *kd = new KeyboardDescriptor (this);
|
|
2060
|
+
if (!kd)
|
|
2061
|
+
throw std::runtime_error ("no keyboard-object allocated");
|
|
2062
|
+
Add (kd);
|
|
2063
|
+
return kd->GetBinding();
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
|
|
2067
|
+
/**********************************
|
|
2068
|
+
EventMachine_t::GetConnectionCount
|
|
2069
|
+
**********************************/
|
|
2070
|
+
|
|
2071
|
+
int EventMachine_t::GetConnectionCount ()
|
|
2072
|
+
{
|
|
2073
|
+
int i = 0;
|
|
2074
|
+
// Subtract one for epoll or kqueue because of the LoopbreakDescriptor
|
|
2075
|
+
if (Poller == Poller_Epoll || Poller == Poller_Kqueue)
|
|
2076
|
+
i = 1;
|
|
2077
|
+
|
|
2078
|
+
return Descriptors.size() + NewDescriptors.size() - i;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
|
|
2082
|
+
/************************
|
|
2083
|
+
EventMachine_t::WatchPid
|
|
2084
|
+
************************/
|
|
2085
|
+
|
|
2086
|
+
#ifdef HAVE_KQUEUE
|
|
2087
|
+
const uintptr_t EventMachine_t::WatchPid (int pid)
|
|
2088
|
+
{
|
|
2089
|
+
if (Poller != Poller_Kqueue)
|
|
2090
|
+
throw std::runtime_error("must enable kqueue (EM.kqueue=true) for pid watching support");
|
|
2091
|
+
|
|
2092
|
+
struct kevent event;
|
|
2093
|
+
int kqres;
|
|
2094
|
+
|
|
2095
|
+
EV_SET(&event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT | NOTE_FORK, 0, 0);
|
|
2096
|
+
|
|
2097
|
+
// Attempt to register the event
|
|
2098
|
+
kqres = kevent(kqfd, &event, 1, NULL, 0, NULL);
|
|
2099
|
+
if (kqres == -1) {
|
|
2100
|
+
char errbuf[200];
|
|
2101
|
+
sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
|
|
2102
|
+
throw std::runtime_error(errbuf);
|
|
2103
|
+
}
|
|
2104
|
+
Bindable_t* b = new Bindable_t();
|
|
2105
|
+
Pids.insert(std::make_pair (pid, b));
|
|
2106
|
+
|
|
2107
|
+
return b->GetBinding();
|
|
2108
|
+
}
|
|
2109
|
+
#else
|
|
2110
|
+
const uintptr_t EventMachine_t::WatchPid (int pid UNUSED)
|
|
2111
|
+
{
|
|
2112
|
+
throw std::runtime_error("no pid watching support on this system");
|
|
2113
|
+
}
|
|
2114
|
+
#endif
|
|
2115
|
+
|
|
2116
|
+
/**************************
|
|
2117
|
+
EventMachine_t::UnwatchPid
|
|
2118
|
+
**************************/
|
|
2119
|
+
|
|
2120
|
+
void EventMachine_t::UnwatchPid (int pid)
|
|
2121
|
+
{
|
|
2122
|
+
Bindable_t *b = Pids[pid];
|
|
2123
|
+
assert(b);
|
|
2124
|
+
Pids.erase(pid);
|
|
2125
|
+
|
|
2126
|
+
#ifdef HAVE_KQUEUE
|
|
2127
|
+
struct kevent k;
|
|
2128
|
+
|
|
2129
|
+
EV_SET(&k, pid, EVFILT_PROC, EV_DELETE, 0, 0, 0);
|
|
2130
|
+
/*int t =*/ kevent (kqfd, &k, 1, NULL, 0, NULL);
|
|
2131
|
+
// t==-1 if the process already exited; ignore this for now
|
|
2132
|
+
#endif
|
|
2133
|
+
|
|
2134
|
+
if (EventCallback)
|
|
2135
|
+
(*EventCallback)(b->GetBinding(), EM_CONNECTION_UNBOUND, NULL, 0);
|
|
2136
|
+
|
|
2137
|
+
delete b;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
void EventMachine_t::UnwatchPid (const uintptr_t sig)
|
|
2141
|
+
{
|
|
2142
|
+
for(std::map<int, Bindable_t*>::iterator i=Pids.begin(); i != Pids.end(); i++)
|
|
2143
|
+
{
|
|
2144
|
+
if (i->second->GetBinding() == sig) {
|
|
2145
|
+
UnwatchPid (i->first);
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
throw std::runtime_error("attempted to remove invalid pid signature");
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
/*************************
|
|
2155
|
+
EventMachine_t::WatchFile
|
|
2156
|
+
*************************/
|
|
2157
|
+
|
|
2158
|
+
const uintptr_t EventMachine_t::WatchFile (const char *fpath)
|
|
2159
|
+
{
|
|
2160
|
+
struct stat sb;
|
|
2161
|
+
int sres;
|
|
2162
|
+
int wd = -1;
|
|
2163
|
+
|
|
2164
|
+
sres = stat(fpath, &sb);
|
|
2165
|
+
|
|
2166
|
+
if (sres == -1) {
|
|
2167
|
+
char errbuf[300];
|
|
2168
|
+
sprintf(errbuf, "error registering file %s for watching: %s", fpath, strerror(errno));
|
|
2169
|
+
throw std::runtime_error(errbuf);
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
#ifdef HAVE_INOTIFY
|
|
2173
|
+
if (!inotify) {
|
|
2174
|
+
inotify = new InotifyDescriptor(this);
|
|
2175
|
+
assert (inotify);
|
|
2176
|
+
Add(inotify);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
wd = inotify_add_watch(inotify->GetSocket(), fpath,
|
|
2180
|
+
IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_MOVE) ;
|
|
2181
|
+
if (wd == -1) {
|
|
2182
|
+
char errbuf[300];
|
|
2183
|
+
sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
|
|
2184
|
+
throw std::runtime_error(errbuf);
|
|
2185
|
+
}
|
|
2186
|
+
#endif
|
|
2187
|
+
|
|
2188
|
+
#ifdef HAVE_KQUEUE
|
|
2189
|
+
if (Poller != Poller_Kqueue)
|
|
2190
|
+
throw std::runtime_error("must enable kqueue (EM.kqueue=true) for file watching support");
|
|
2191
|
+
|
|
2192
|
+
// With kqueue we have to open the file first and use the resulting fd to register for events
|
|
2193
|
+
wd = open(fpath, O_RDONLY);
|
|
2194
|
+
if (wd == -1) {
|
|
2195
|
+
char errbuf[300];
|
|
2196
|
+
sprintf(errbuf, "failed to open file %s for registering with kqueue: %s", fpath, strerror(errno));
|
|
2197
|
+
throw std::runtime_error(errbuf);
|
|
2198
|
+
}
|
|
2199
|
+
_RegisterKqueueFileEvent(wd);
|
|
2200
|
+
#endif
|
|
2201
|
+
|
|
2202
|
+
if (wd != -1) {
|
|
2203
|
+
Bindable_t* b = new Bindable_t();
|
|
2204
|
+
Files.insert(std::make_pair (wd, b));
|
|
2205
|
+
|
|
2206
|
+
return b->GetBinding();
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
throw std::runtime_error("no file watching support on this system"); // is this the right thing to do?
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
|
|
2213
|
+
/***************************
|
|
2214
|
+
EventMachine_t::UnwatchFile
|
|
2215
|
+
***************************/
|
|
2216
|
+
|
|
2217
|
+
void EventMachine_t::UnwatchFile (int wd)
|
|
2218
|
+
{
|
|
2219
|
+
Bindable_t *b = Files[wd];
|
|
2220
|
+
assert(b);
|
|
2221
|
+
Files.erase(wd);
|
|
2222
|
+
|
|
2223
|
+
#ifdef HAVE_INOTIFY
|
|
2224
|
+
inotify_rm_watch(inotify->GetSocket(), wd);
|
|
2225
|
+
#elif HAVE_KQUEUE
|
|
2226
|
+
// With kqueue, closing the monitored fd automatically clears all registered events for it
|
|
2227
|
+
close(wd);
|
|
2228
|
+
#endif
|
|
2229
|
+
|
|
2230
|
+
if (EventCallback)
|
|
2231
|
+
(*EventCallback)(b->GetBinding(), EM_CONNECTION_UNBOUND, NULL, 0);
|
|
2232
|
+
|
|
2233
|
+
delete b;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
void EventMachine_t::UnwatchFile (const uintptr_t sig)
|
|
2237
|
+
{
|
|
2238
|
+
for(std::map<int, Bindable_t*>::iterator i=Files.begin(); i != Files.end(); i++)
|
|
2239
|
+
{
|
|
2240
|
+
if (i->second->GetBinding() == sig) {
|
|
2241
|
+
UnwatchFile (i->first);
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
throw std::runtime_error("attempted to remove invalid watch signature");
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
|
|
2249
|
+
/***********************************
|
|
2250
|
+
EventMachine_t::_ReadInotify_Events
|
|
2251
|
+
************************************/
|
|
2252
|
+
|
|
2253
|
+
void EventMachine_t::_ReadInotifyEvents()
|
|
2254
|
+
{
|
|
2255
|
+
#ifdef HAVE_INOTIFY
|
|
2256
|
+
char buffer[1024];
|
|
2257
|
+
|
|
2258
|
+
assert(EventCallback);
|
|
2259
|
+
|
|
2260
|
+
for (;;) {
|
|
2261
|
+
int returned = read(inotify->GetSocket(), buffer, sizeof(buffer));
|
|
2262
|
+
assert(!(returned == 0 || (returned == -1 && errno == EINVAL)));
|
|
2263
|
+
if (returned <= 0) {
|
|
2264
|
+
break;
|
|
2265
|
+
}
|
|
2266
|
+
int current = 0;
|
|
2267
|
+
while (current < returned) {
|
|
2268
|
+
struct inotify_event* event = (struct inotify_event*)(buffer+current);
|
|
2269
|
+
std::map<int, Bindable_t*>::const_iterator bindable = Files.find(event->wd);
|
|
2270
|
+
if (bindable != Files.end()) {
|
|
2271
|
+
if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE)){
|
|
2272
|
+
(*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "modified", 8);
|
|
2273
|
+
}
|
|
2274
|
+
if (event->mask & IN_MOVE_SELF){
|
|
2275
|
+
(*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "moved", 5);
|
|
2276
|
+
}
|
|
2277
|
+
if (event->mask & IN_DELETE_SELF) {
|
|
2278
|
+
(*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
|
|
2279
|
+
UnwatchFile ((int)event->wd);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
current += sizeof(struct inotify_event) + event->len;
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
#endif
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
|
|
2289
|
+
/*************************************
|
|
2290
|
+
EventMachine_t::_HandleKqueuePidEvent
|
|
2291
|
+
*************************************/
|
|
2292
|
+
|
|
2293
|
+
#ifdef HAVE_KQUEUE
|
|
2294
|
+
void EventMachine_t::_HandleKqueuePidEvent(struct kevent *event)
|
|
2295
|
+
{
|
|
2296
|
+
assert(EventCallback);
|
|
2297
|
+
|
|
2298
|
+
if (event->fflags & NOTE_FORK)
|
|
2299
|
+
(*EventCallback)(Pids [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "fork", 4);
|
|
2300
|
+
if (event->fflags & NOTE_EXIT) {
|
|
2301
|
+
(*EventCallback)(Pids [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "exit", 4);
|
|
2302
|
+
// stop watching the pid if it died
|
|
2303
|
+
UnwatchPid ((int)event->ident);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
#endif
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
/**************************************
|
|
2310
|
+
EventMachine_t::_HandleKqueueFileEvent
|
|
2311
|
+
***************************************/
|
|
2312
|
+
|
|
2313
|
+
#ifdef HAVE_KQUEUE
|
|
2314
|
+
void EventMachine_t::_HandleKqueueFileEvent(struct kevent *event)
|
|
2315
|
+
{
|
|
2316
|
+
assert(EventCallback);
|
|
2317
|
+
|
|
2318
|
+
if (event->fflags & NOTE_WRITE)
|
|
2319
|
+
(*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
|
|
2320
|
+
if (event->fflags & NOTE_RENAME)
|
|
2321
|
+
(*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
|
|
2322
|
+
if (event->fflags & NOTE_DELETE) {
|
|
2323
|
+
(*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
|
|
2324
|
+
UnwatchFile ((int)event->ident);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
#endif
|
|
2328
|
+
|
|
2329
|
+
|
|
2330
|
+
/****************************************
|
|
2331
|
+
EventMachine_t::_RegisterKqueueFileEvent
|
|
2332
|
+
*****************************************/
|
|
2333
|
+
|
|
2334
|
+
#ifdef HAVE_KQUEUE
|
|
2335
|
+
void EventMachine_t::_RegisterKqueueFileEvent(int fd)
|
|
2336
|
+
{
|
|
2337
|
+
struct kevent newevent;
|
|
2338
|
+
int kqres;
|
|
2339
|
+
|
|
2340
|
+
// Setup the event with our fd and proper flags
|
|
2341
|
+
EV_SET(&newevent, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_WRITE, 0, 0);
|
|
2342
|
+
|
|
2343
|
+
// Attempt to register the event
|
|
2344
|
+
kqres = kevent(kqfd, &newevent, 1, NULL, 0, NULL);
|
|
2345
|
+
if (kqres == -1) {
|
|
2346
|
+
char errbuf[200];
|
|
2347
|
+
sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
|
|
2348
|
+
close(fd);
|
|
2349
|
+
throw std::runtime_error(errbuf);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
#endif
|
|
2353
|
+
|
|
2354
|
+
|
|
2355
|
+
/************************************
|
|
2356
|
+
EventMachine_t::GetHeartbeatInterval
|
|
2357
|
+
*************************************/
|
|
2358
|
+
|
|
2359
|
+
float EventMachine_t::GetHeartbeatInterval()
|
|
2360
|
+
{
|
|
2361
|
+
return ((float)HeartbeatInterval / 1000000);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
|
|
2365
|
+
/************************************
|
|
2366
|
+
EventMachine_t::SetHeartbeatInterval
|
|
2367
|
+
*************************************/
|
|
2368
|
+
|
|
2369
|
+
int EventMachine_t::SetHeartbeatInterval(float interval)
|
|
2370
|
+
{
|
|
2371
|
+
int iv = (int)(interval * 1000000);
|
|
2372
|
+
if (iv > 0) {
|
|
2373
|
+
HeartbeatInterval = iv;
|
|
2374
|
+
return 1;
|
|
2375
|
+
}
|
|
2376
|
+
return 0;
|
|
2377
|
+
}
|
|
2378
|
+
//#endif // OS_UNIX
|