eventmachine 0.12.6-x86-mswin32-60 → 0.12.8-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/{docs/README → README} +21 -13
  2. data/Rakefile +14 -4
  3. data/docs/DEFERRABLES +0 -5
  4. data/docs/INSTALL +2 -4
  5. data/docs/LEGAL +1 -1
  6. data/docs/LIGHTWEIGHT_CONCURRENCY +0 -2
  7. data/docs/PURE_RUBY +0 -2
  8. data/docs/RELEASE_NOTES +0 -2
  9. data/docs/SMTP +0 -7
  10. data/docs/SPAWNED_PROCESSES +0 -4
  11. data/docs/TODO +0 -2
  12. data/eventmachine.gemspec +41 -32
  13. data/examples/ex_channel.rb +43 -0
  14. data/examples/ex_queue.rb +2 -0
  15. data/examples/helper.rb +2 -0
  16. data/ext/cmain.cpp +685 -586
  17. data/ext/cplusplus.cpp +15 -6
  18. data/ext/ed.cpp +1732 -1522
  19. data/ext/ed.h +407 -380
  20. data/ext/em.cpp +2263 -1937
  21. data/ext/em.h +223 -186
  22. data/ext/eventmachine.h +111 -98
  23. data/ext/eventmachine_cpp.h +1 -0
  24. data/ext/extconf.rb +4 -0
  25. data/ext/kb.cpp +81 -82
  26. data/ext/pipe.cpp +349 -351
  27. data/ext/project.h +21 -0
  28. data/ext/rubymain.cpp +1047 -847
  29. data/ext/ssl.cpp +38 -1
  30. data/ext/ssl.h +5 -1
  31. data/java/src/com/rubyeventmachine/Application.java +7 -3
  32. data/java/src/com/rubyeventmachine/EmReactor.java +16 -1
  33. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +25 -3
  34. data/lib/{protocols → em}/buftok.rb +16 -5
  35. data/lib/em/callback.rb +26 -0
  36. data/lib/em/channel.rb +57 -0
  37. data/lib/em/connection.rb +505 -0
  38. data/lib/em/deferrable.rb +144 -165
  39. data/lib/em/file_watch.rb +54 -0
  40. data/lib/em/future.rb +24 -25
  41. data/lib/em/messages.rb +1 -1
  42. data/lib/em/process_watch.rb +44 -0
  43. data/lib/em/processes.rb +119 -113
  44. data/lib/em/protocols.rb +35 -0
  45. data/lib/em/protocols/header_and_content.rb +138 -0
  46. data/lib/em/protocols/httpclient.rb +263 -0
  47. data/lib/em/protocols/httpclient2.rb +582 -0
  48. data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
  49. data/lib/em/protocols/linetext2.rb +160 -0
  50. data/lib/{protocols → em/protocols}/memcache.rb +37 -7
  51. data/lib/em/protocols/object_protocol.rb +39 -0
  52. data/lib/em/protocols/postgres3.rb +247 -0
  53. data/lib/em/protocols/saslauth.rb +175 -0
  54. data/lib/em/protocols/smtpclient.rb +331 -0
  55. data/lib/em/protocols/smtpserver.rb +547 -0
  56. data/lib/em/protocols/stomp.rb +200 -0
  57. data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
  58. data/lib/em/queue.rb +61 -0
  59. data/lib/em/spawnable.rb +53 -56
  60. data/lib/em/streamer.rb +92 -74
  61. data/lib/em/timers.rb +55 -0
  62. data/lib/em/version.rb +3 -0
  63. data/lib/eventmachine.rb +1636 -1926
  64. data/lib/evma.rb +1 -1
  65. data/lib/jeventmachine.rb +106 -101
  66. data/lib/pr_eventmachine.rb +47 -36
  67. data/tasks/project.rake +2 -1
  68. data/tests/client.crt +31 -0
  69. data/tests/client.key +51 -0
  70. data/tests/test_attach.rb +18 -0
  71. data/tests/test_basic.rb +285 -231
  72. data/tests/test_channel.rb +63 -0
  73. data/tests/test_connection_count.rb +2 -2
  74. data/tests/test_epoll.rb +162 -163
  75. data/tests/test_errors.rb +36 -36
  76. data/tests/test_exc.rb +22 -25
  77. data/tests/test_file_watch.rb +49 -0
  78. data/tests/test_futures.rb +77 -93
  79. data/tests/test_hc.rb +2 -2
  80. data/tests/test_httpclient.rb +55 -52
  81. data/tests/test_httpclient2.rb +153 -155
  82. data/tests/test_inactivity_timeout.rb +30 -0
  83. data/tests/test_kb.rb +8 -9
  84. data/tests/test_ltp2.rb +274 -277
  85. data/tests/test_next_tick.rb +135 -109
  86. data/tests/test_object_protocol.rb +37 -0
  87. data/tests/test_process_watch.rb +48 -0
  88. data/tests/test_processes.rb +128 -95
  89. data/tests/test_proxy_connection.rb +92 -0
  90. data/tests/test_pure.rb +1 -5
  91. data/tests/test_queue.rb +44 -0
  92. data/tests/test_running.rb +9 -14
  93. data/tests/test_sasl.rb +32 -34
  94. data/tests/test_send_file.rb +175 -176
  95. data/tests/test_servers.rb +37 -41
  96. data/tests/test_smtpserver.rb +47 -55
  97. data/tests/test_spawn.rb +284 -291
  98. data/tests/test_ssl_args.rb +1 -1
  99. data/tests/test_ssl_methods.rb +1 -1
  100. data/tests/test_ssl_verify.rb +82 -0
  101. data/tests/test_timers.rb +81 -88
  102. data/tests/test_ud.rb +0 -7
  103. data/tests/testem.rb +1 -1
  104. metadata +52 -36
  105. data/lib/em/eventable.rb +0 -39
  106. data/lib/eventmachine_version.rb +0 -31
  107. data/lib/protocols/header_and_content.rb +0 -129
  108. data/lib/protocols/httpcli2.rb +0 -803
  109. data/lib/protocols/httpclient.rb +0 -270
  110. data/lib/protocols/linetext2.rb +0 -161
  111. data/lib/protocols/postgres.rb +0 -261
  112. data/lib/protocols/saslauth.rb +0 -179
  113. data/lib/protocols/smtpclient.rb +0 -308
  114. data/lib/protocols/smtpserver.rb +0 -556
  115. data/lib/protocols/stomp.rb +0 -153
  116. data/tests/test_eventables.rb +0 -77
@@ -63,6 +63,7 @@ namespace EM {
63
63
  virtual ~Connection() {}
64
64
 
65
65
  virtual void Connect (const char*, int);
66
+ virtual void BindConnect (const char *, int, const char*, int);
66
67
 
67
68
  void SendData (const char *data);
68
69
  void SendData (const char *data, int length);
data/ext/extconf.rb CHANGED
@@ -15,6 +15,9 @@ end
15
15
  add_define 'BUILD_FOR_RUBY'
16
16
  add_define 'HAVE_RBTRAP' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
17
17
  add_define "HAVE_TBR" if have_func('rb_thread_blocking_region')# and have_macro('RUBY_UBF_IO', 'ruby.h')
18
+ add_define "HAVE_INOTIFY" if inotify = have_func('inotify_init', 'sys/inotify.h')
19
+ add_define "HAVE_OLD_INOTIFY" if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
20
+ add_define 'HAVE_WRITEV' if have_func('writev', 'sys/uio.h')
18
21
 
19
22
  # Minor platform details between *nix and Windows:
20
23
 
@@ -59,6 +62,7 @@ when /solaris/
59
62
  # on Unix we need a g++ link, not gcc.
60
63
  CONFIG['LDSHARED'] = "$(CXX) -shared"
61
64
  end
65
+
62
66
  when /openbsd/
63
67
  # OpenBSD branch contributed by Guillaume Sellier.
64
68
 
data/ext/kb.cpp CHANGED
@@ -1,82 +1,81 @@
1
- /*****************************************************************************
2
-
3
- $Id$
4
-
5
- File: kb.cpp
6
- Date: 24Aug07
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
- #include "project.h"
21
-
22
-
23
- /**************************************
24
- KeyboardDescriptor::KeyboardDescriptor
25
- **************************************/
26
-
27
- KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em):
28
- EventableDescriptor (0, parent_em),
29
- bReadAttemptedAfterClose (false),
30
- LastIo (gCurrentLoopTime),
31
- InactivityTimeout (0)
32
- {
33
- #ifdef HAVE_EPOLL
34
- EpollEvent.events = EPOLLIN;
35
- #endif
36
- #ifdef HAVE_KQUEUE
37
- MyEventMachine->ArmKqueueReader (this);
38
- #endif
39
- }
40
-
41
-
42
- /***************************************
43
- KeyboardDescriptor::~KeyboardDescriptor
44
- ***************************************/
45
-
46
- KeyboardDescriptor::~KeyboardDescriptor()
47
- {
48
- }
49
-
50
-
51
- /*************************
52
- KeyboardDescriptor::Write
53
- *************************/
54
-
55
- void KeyboardDescriptor::Write()
56
- {
57
- // Why are we here?
58
- throw std::runtime_error ("bad code path in keyboard handler");
59
- }
60
-
61
-
62
- /*****************************
63
- KeyboardDescriptor::Heartbeat
64
- *****************************/
65
-
66
- void KeyboardDescriptor::Heartbeat()
67
- {
68
- // no-op
69
- }
70
-
71
-
72
- /************************
73
- KeyboardDescriptor::Read
74
- ************************/
75
-
76
- void KeyboardDescriptor::Read()
77
- {
78
- char c;
79
- read (GetSocket(), &c, 1);
80
- if (EventCallback)
81
- (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, &c, 1);
82
- }
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: kb.cpp
6
+ Date: 24Aug07
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
+ #include "project.h"
21
+
22
+
23
+ /**************************************
24
+ KeyboardDescriptor::KeyboardDescriptor
25
+ **************************************/
26
+
27
+ KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em):
28
+ EventableDescriptor (0, parent_em),
29
+ bReadAttemptedAfterClose (false),
30
+ LastIo (gCurrentLoopTime),
31
+ InactivityTimeout (0)
32
+ {
33
+ #ifdef HAVE_EPOLL
34
+ EpollEvent.events = EPOLLIN;
35
+ #endif
36
+ #ifdef HAVE_KQUEUE
37
+ MyEventMachine->ArmKqueueReader (this);
38
+ #endif
39
+ }
40
+
41
+
42
+ /***************************************
43
+ KeyboardDescriptor::~KeyboardDescriptor
44
+ ***************************************/
45
+
46
+ KeyboardDescriptor::~KeyboardDescriptor()
47
+ {
48
+ }
49
+
50
+
51
+ /*************************
52
+ KeyboardDescriptor::Write
53
+ *************************/
54
+
55
+ void KeyboardDescriptor::Write()
56
+ {
57
+ // Why are we here?
58
+ throw std::runtime_error ("bad code path in keyboard handler");
59
+ }
60
+
61
+
62
+ /*****************************
63
+ KeyboardDescriptor::Heartbeat
64
+ *****************************/
65
+
66
+ void KeyboardDescriptor::Heartbeat()
67
+ {
68
+ // no-op
69
+ }
70
+
71
+
72
+ /************************
73
+ KeyboardDescriptor::Read
74
+ ************************/
75
+
76
+ void KeyboardDescriptor::Read()
77
+ {
78
+ char c;
79
+ read (GetSocket(), &c, 1);
80
+ _GenericInboundDispatch(&c, 1);
81
+ }
data/ext/pipe.cpp CHANGED
@@ -1,351 +1,349 @@
1
- /*****************************************************************************
2
-
3
- $Id$
4
-
5
- File: pipe.cpp
6
- Date: 30May07
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
- #include "project.h"
21
-
22
-
23
- #ifdef OS_UNIX
24
- // THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
25
-
26
- /******************************
27
- PipeDescriptor::PipeDescriptor
28
- ******************************/
29
-
30
- PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
31
- EventableDescriptor (fd, parent_em),
32
- bReadAttemptedAfterClose (false),
33
- LastIo (gCurrentLoopTime),
34
- InactivityTimeout (0),
35
- OutboundDataSize (0),
36
- SubprocessPid (subpid)
37
- {
38
- #ifdef HAVE_EPOLL
39
- EpollEvent.events = EPOLLIN;
40
- #endif
41
- #ifdef HAVE_KQUEUE
42
- MyEventMachine->ArmKqueueReader (this);
43
- #endif
44
- }
45
-
46
-
47
- /*******************************
48
- PipeDescriptor::~PipeDescriptor
49
- *******************************/
50
-
51
- PipeDescriptor::~PipeDescriptor()
52
- {
53
- // Run down any stranded outbound data.
54
- for (size_t i=0; i < OutboundPages.size(); i++)
55
- OutboundPages[i].Free();
56
-
57
- /* As a virtual destructor, we come here before the base-class
58
- * destructor that closes our file-descriptor.
59
- * We have to make sure the subprocess goes down (if it's not
60
- * already down) and we have to reap the zombie.
61
- *
62
- * This implementation is PROVISIONAL and will surely be improved.
63
- * The intention here is that we never block, hence the highly
64
- * undesirable sleeps. But if we can't reap the subprocess even
65
- * after sending it SIGKILL, then something is wrong and we
66
- * throw a fatal exception, which is also not something we should
67
- * be doing.
68
- *
69
- * Eventually the right thing to do will be to have the reactor
70
- * core respond to SIGCHLD by chaining a handler on top of the
71
- * one Ruby may have installed, and dealing with a list of dead
72
- * children that are pending cleanup.
73
- *
74
- * Since we want to have a signal processor integrated into the
75
- * client-visible API, let's wait until that is done before cleaning
76
- * this up.
77
- *
78
- * Added a very ugly hack to support passing the subprocess's exit
79
- * status to the user. It only makes logical sense for user code to access
80
- * the subprocess exit status in the unbind callback. But unbind is called
81
- * back during the EventableDescriptor destructor. So by that time there's
82
- * no way to call back this object through an object binding, because it's
83
- * already been cleaned up. We might have added a parameter to the unbind
84
- * callback, but that would probably break a huge amount of existing code.
85
- * So the hack-solution is to define an instance variable in the EventMachine
86
- * object and stick the exit status in there, where it can easily be accessed
87
- * with an accessor visible to user code.
88
- * User code should ONLY access the exit status from within the unbind callback.
89
- * Otherwise there's no guarantee it'll be valid.
90
- * This hack won't make it impossible to run multiple EventMachines in a single
91
- * process, but it will make it impossible to reliably nest unbind calls
92
- * within other unbind calls. (Not sure if that's even possible.)
93
- */
94
-
95
- assert (MyEventMachine);
96
-
97
- /* Another hack to make the SubprocessPid available to get_subprocess_status */
98
- MyEventMachine->SubprocessPid = SubprocessPid;
99
-
100
- /* 01Mar09: Updated to use a small nanosleep in a loop. When nanosleep is interrupted by SIGCHLD,
101
- * it resumes the system call after processing the signal (resulting in unnecessary latency).
102
- * Calling nanosleep in a loop avoids this problem.
103
- */
104
- struct timespec req = {0, 50000000}; // 0.05s
105
- int n;
106
-
107
- // wait 0.25s for the process to die
108
- for (n=0; n<5; n++) {
109
- if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
110
- nanosleep (&req, NULL);
111
- }
112
-
113
- // send SIGTERM and wait another 0.5s
114
- kill (SubprocessPid, SIGTERM);
115
- for (n=0; n<10; n++) {
116
- nanosleep (&req, NULL);
117
- if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
118
- }
119
-
120
- // send SIGKILL and wait another 1s
121
- kill (SubprocessPid, SIGKILL);
122
- for (n=0; n<20; n++) {
123
- nanosleep (&req, NULL);
124
- if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
125
- }
126
-
127
- // still not dead, give up!
128
- throw std::runtime_error ("unable to reap subprocess");
129
- }
130
-
131
-
132
-
133
- /********************
134
- PipeDescriptor::Read
135
- ********************/
136
-
137
- void PipeDescriptor::Read()
138
- {
139
- int sd = GetSocket();
140
- if (sd == INVALID_SOCKET) {
141
- assert (!bReadAttemptedAfterClose);
142
- bReadAttemptedAfterClose = true;
143
- return;
144
- }
145
-
146
- LastIo = gCurrentLoopTime;
147
-
148
- int total_bytes_read = 0;
149
- char readbuffer [16 * 1024];
150
-
151
- for (int i=0; i < 10; i++) {
152
- // Don't read just one buffer and then move on. This is faster
153
- // if there is a lot of incoming.
154
- // But don't read indefinitely. Give other sockets a chance to run.
155
- // NOTICE, we're reading one less than the buffer size.
156
- // That's so we can put a guard byte at the end of what we send
157
- // to user code.
158
- // Use read instead of recv, which on Linux gives a "socket operation
159
- // on nonsocket" error.
160
-
161
-
162
- int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
163
- //cerr << "<R:" << r << ">";
164
-
165
- if (r > 0) {
166
- total_bytes_read += r;
167
- LastRead = gCurrentLoopTime;
168
-
169
- // Add a null-terminator at the the end of the buffer
170
- // that we will send to the callback.
171
- // DO NOT EVER CHANGE THIS. We want to explicitly allow users
172
- // to be able to depend on this behavior, so they will have
173
- // the option to do some things faster. Additionally it's
174
- // a security guard against buffer overflows.
175
- readbuffer [r] = 0;
176
- if (EventCallback)
177
- (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r);
178
- }
179
- else if (r == 0) {
180
- break;
181
- }
182
- else {
183
- // Basically a would-block, meaning we've read everything there is to read.
184
- break;
185
- }
186
-
187
- }
188
-
189
-
190
- if (total_bytes_read == 0) {
191
- // If we read no data on a socket that selected readable,
192
- // it generally means the other end closed the connection gracefully.
193
- ScheduleClose (false);
194
- //bCloseNow = true;
195
- }
196
-
197
- }
198
-
199
- /*********************
200
- PipeDescriptor::Write
201
- *********************/
202
-
203
- void PipeDescriptor::Write()
204
- {
205
- int sd = GetSocket();
206
- assert (sd != INVALID_SOCKET);
207
-
208
- LastIo = gCurrentLoopTime;
209
- char output_buffer [16 * 1024];
210
- size_t nbytes = 0;
211
-
212
- while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
213
- OutboundPage *op = &(OutboundPages[0]);
214
- if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
215
- memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
216
- nbytes += (op->Length - op->Offset);
217
- op->Free();
218
- OutboundPages.pop_front();
219
- }
220
- else {
221
- int len = sizeof(output_buffer) - nbytes;
222
- memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
223
- op->Offset += len;
224
- nbytes += len;
225
- }
226
- }
227
-
228
- // We should never have gotten here if there were no data to write,
229
- // so assert that as a sanity check.
230
- // Don't bother to make sure nbytes is less than output_buffer because
231
- // if it were we probably would have crashed already.
232
- assert (nbytes > 0);
233
-
234
- assert (GetSocket() != INVALID_SOCKET);
235
- int bytes_written = write (GetSocket(), output_buffer, nbytes);
236
-
237
- if (bytes_written > 0) {
238
- OutboundDataSize -= bytes_written;
239
- if ((size_t)bytes_written < nbytes) {
240
- int len = nbytes - bytes_written;
241
- char *buffer = (char*) malloc (len + 1);
242
- if (!buffer)
243
- throw std::runtime_error ("bad alloc throwing back data");
244
- memcpy (buffer, output_buffer + bytes_written, len);
245
- buffer [len] = 0;
246
- OutboundPages.push_front (OutboundPage (buffer, len));
247
- }
248
- #ifdef HAVE_EPOLL
249
- EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
250
- assert (MyEventMachine);
251
- MyEventMachine->Modify (this);
252
- #endif
253
- }
254
- else {
255
- #ifdef OS_UNIX
256
- if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
257
- #endif
258
- #ifdef OS_WIN32
259
- if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
260
- #endif
261
- Close();
262
- }
263
- }
264
-
265
-
266
- /*************************
267
- PipeDescriptor::Heartbeat
268
- *************************/
269
-
270
- void PipeDescriptor::Heartbeat()
271
- {
272
- // If an inactivity timeout is defined, then check for it.
273
- if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
274
- ScheduleClose (false);
275
- //bCloseNow = true;
276
- }
277
-
278
-
279
- /*****************************
280
- PipeDescriptor::SelectForRead
281
- *****************************/
282
-
283
- bool PipeDescriptor::SelectForRead()
284
- {
285
- /* Pipe descriptors, being local by definition, don't have
286
- * a pending state, so this is simpler than for the
287
- * ConnectionDescriptor object.
288
- */
289
- return true;
290
- }
291
-
292
- /******************************
293
- PipeDescriptor::SelectForWrite
294
- ******************************/
295
-
296
- bool PipeDescriptor::SelectForWrite()
297
- {
298
- /* Pipe descriptors, being local by definition, don't have
299
- * a pending state, so this is simpler than for the
300
- * ConnectionDescriptor object.
301
- */
302
- return (GetOutboundDataSize() > 0);
303
- }
304
-
305
-
306
-
307
-
308
- /********************************
309
- PipeDescriptor::SendOutboundData
310
- ********************************/
311
-
312
- int PipeDescriptor::SendOutboundData (const char *data, int length)
313
- {
314
- //if (bCloseNow || bCloseAfterWriting)
315
- if (IsCloseScheduled())
316
- return 0;
317
-
318
- if (!data && (length > 0))
319
- throw std::runtime_error ("bad outbound data");
320
- char *buffer = (char *) malloc (length + 1);
321
- if (!buffer)
322
- throw std::runtime_error ("no allocation for outbound data");
323
- memcpy (buffer, data, length);
324
- buffer [length] = 0;
325
- OutboundPages.push_back (OutboundPage (buffer, length));
326
- OutboundDataSize += length;
327
- #ifdef HAVE_EPOLL
328
- EpollEvent.events = (EPOLLIN | EPOLLOUT);
329
- assert (MyEventMachine);
330
- MyEventMachine->Modify (this);
331
- #endif
332
- return length;
333
- }
334
-
335
- /********************************
336
- PipeDescriptor::GetSubprocessPid
337
- ********************************/
338
-
339
- bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
340
- {
341
- bool ok = false;
342
- if (pid && (SubprocessPid > 0)) {
343
- *pid = SubprocessPid;
344
- ok = true;
345
- }
346
- return ok;
347
- }
348
-
349
-
350
- #endif // OS_UNIX
351
-
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: pipe.cpp
6
+ Date: 30May07
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
+ #include "project.h"
21
+
22
+
23
+ #ifdef OS_UNIX
24
+ // THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
25
+
26
+ /******************************
27
+ PipeDescriptor::PipeDescriptor
28
+ ******************************/
29
+
30
+ PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
31
+ EventableDescriptor (fd, parent_em),
32
+ bReadAttemptedAfterClose (false),
33
+ LastIo (gCurrentLoopTime),
34
+ InactivityTimeout (0),
35
+ OutboundDataSize (0),
36
+ SubprocessPid (subpid)
37
+ {
38
+ #ifdef HAVE_EPOLL
39
+ EpollEvent.events = EPOLLIN;
40
+ #endif
41
+ #ifdef HAVE_KQUEUE
42
+ MyEventMachine->ArmKqueueReader (this);
43
+ #endif
44
+ }
45
+
46
+
47
+ /*******************************
48
+ PipeDescriptor::~PipeDescriptor
49
+ *******************************/
50
+
51
+ PipeDescriptor::~PipeDescriptor()
52
+ {
53
+ // Run down any stranded outbound data.
54
+ for (size_t i=0; i < OutboundPages.size(); i++)
55
+ OutboundPages[i].Free();
56
+
57
+ /* As a virtual destructor, we come here before the base-class
58
+ * destructor that closes our file-descriptor.
59
+ * We have to make sure the subprocess goes down (if it's not
60
+ * already down) and we have to reap the zombie.
61
+ *
62
+ * This implementation is PROVISIONAL and will surely be improved.
63
+ * The intention here is that we never block, hence the highly
64
+ * undesirable sleeps. But if we can't reap the subprocess even
65
+ * after sending it SIGKILL, then something is wrong and we
66
+ * throw a fatal exception, which is also not something we should
67
+ * be doing.
68
+ *
69
+ * Eventually the right thing to do will be to have the reactor
70
+ * core respond to SIGCHLD by chaining a handler on top of the
71
+ * one Ruby may have installed, and dealing with a list of dead
72
+ * children that are pending cleanup.
73
+ *
74
+ * Since we want to have a signal processor integrated into the
75
+ * client-visible API, let's wait until that is done before cleaning
76
+ * this up.
77
+ *
78
+ * Added a very ugly hack to support passing the subprocess's exit
79
+ * status to the user. It only makes logical sense for user code to access
80
+ * the subprocess exit status in the unbind callback. But unbind is called
81
+ * back during the EventableDescriptor destructor. So by that time there's
82
+ * no way to call back this object through an object binding, because it's
83
+ * already been cleaned up. We might have added a parameter to the unbind
84
+ * callback, but that would probably break a huge amount of existing code.
85
+ * So the hack-solution is to define an instance variable in the EventMachine
86
+ * object and stick the exit status in there, where it can easily be accessed
87
+ * with an accessor visible to user code.
88
+ * User code should ONLY access the exit status from within the unbind callback.
89
+ * Otherwise there's no guarantee it'll be valid.
90
+ * This hack won't make it impossible to run multiple EventMachines in a single
91
+ * process, but it will make it impossible to reliably nest unbind calls
92
+ * within other unbind calls. (Not sure if that's even possible.)
93
+ */
94
+
95
+ assert (MyEventMachine);
96
+
97
+ /* Another hack to make the SubprocessPid available to get_subprocess_status */
98
+ MyEventMachine->SubprocessPid = SubprocessPid;
99
+
100
+ /* 01Mar09: Updated to use a small nanosleep in a loop. When nanosleep is interrupted by SIGCHLD,
101
+ * it resumes the system call after processing the signal (resulting in unnecessary latency).
102
+ * Calling nanosleep in a loop avoids this problem.
103
+ */
104
+ struct timespec req = {0, 50000000}; // 0.05s
105
+ int n;
106
+
107
+ // wait 0.25s for the process to die
108
+ for (n=0; n<5; n++) {
109
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
110
+ nanosleep (&req, NULL);
111
+ }
112
+
113
+ // send SIGTERM and wait another 0.5s
114
+ kill (SubprocessPid, SIGTERM);
115
+ for (n=0; n<10; n++) {
116
+ nanosleep (&req, NULL);
117
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
118
+ }
119
+
120
+ // send SIGKILL and wait another 1s
121
+ kill (SubprocessPid, SIGKILL);
122
+ for (n=0; n<20; n++) {
123
+ nanosleep (&req, NULL);
124
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
125
+ }
126
+
127
+ // still not dead, give up!
128
+ throw std::runtime_error ("unable to reap subprocess");
129
+ }
130
+
131
+
132
+
133
+ /********************
134
+ PipeDescriptor::Read
135
+ ********************/
136
+
137
+ void PipeDescriptor::Read()
138
+ {
139
+ int sd = GetSocket();
140
+ if (sd == INVALID_SOCKET) {
141
+ assert (!bReadAttemptedAfterClose);
142
+ bReadAttemptedAfterClose = true;
143
+ return;
144
+ }
145
+
146
+ LastIo = gCurrentLoopTime;
147
+
148
+ int total_bytes_read = 0;
149
+ char readbuffer [16 * 1024];
150
+
151
+ for (int i=0; i < 10; i++) {
152
+ // Don't read just one buffer and then move on. This is faster
153
+ // if there is a lot of incoming.
154
+ // But don't read indefinitely. Give other sockets a chance to run.
155
+ // NOTICE, we're reading one less than the buffer size.
156
+ // That's so we can put a guard byte at the end of what we send
157
+ // to user code.
158
+ // Use read instead of recv, which on Linux gives a "socket operation
159
+ // on nonsocket" error.
160
+
161
+
162
+ int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
163
+ //cerr << "<R:" << r << ">";
164
+
165
+ if (r > 0) {
166
+ total_bytes_read += r;
167
+
168
+ // Add a null-terminator at the the end of the buffer
169
+ // that we will send to the callback.
170
+ // DO NOT EVER CHANGE THIS. We want to explicitly allow users
171
+ // to be able to depend on this behavior, so they will have
172
+ // the option to do some things faster. Additionally it's
173
+ // a security guard against buffer overflows.
174
+ readbuffer [r] = 0;
175
+ _GenericInboundDispatch(readbuffer, r);
176
+ }
177
+ else if (r == 0) {
178
+ break;
179
+ }
180
+ else {
181
+ // Basically a would-block, meaning we've read everything there is to read.
182
+ break;
183
+ }
184
+
185
+ }
186
+
187
+
188
+ if (total_bytes_read == 0) {
189
+ // If we read no data on a socket that selected readable,
190
+ // it generally means the other end closed the connection gracefully.
191
+ ScheduleClose (false);
192
+ //bCloseNow = true;
193
+ }
194
+
195
+ }
196
+
197
+ /*********************
198
+ PipeDescriptor::Write
199
+ *********************/
200
+
201
+ void PipeDescriptor::Write()
202
+ {
203
+ int sd = GetSocket();
204
+ assert (sd != INVALID_SOCKET);
205
+
206
+ LastIo = gCurrentLoopTime;
207
+ char output_buffer [16 * 1024];
208
+ size_t nbytes = 0;
209
+
210
+ while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
211
+ OutboundPage *op = &(OutboundPages[0]);
212
+ if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
213
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
214
+ nbytes += (op->Length - op->Offset);
215
+ op->Free();
216
+ OutboundPages.pop_front();
217
+ }
218
+ else {
219
+ int len = sizeof(output_buffer) - nbytes;
220
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
221
+ op->Offset += len;
222
+ nbytes += len;
223
+ }
224
+ }
225
+
226
+ // We should never have gotten here if there were no data to write,
227
+ // so assert that as a sanity check.
228
+ // Don't bother to make sure nbytes is less than output_buffer because
229
+ // if it were we probably would have crashed already.
230
+ assert (nbytes > 0);
231
+
232
+ assert (GetSocket() != INVALID_SOCKET);
233
+ int bytes_written = write (GetSocket(), output_buffer, nbytes);
234
+
235
+ if (bytes_written > 0) {
236
+ OutboundDataSize -= bytes_written;
237
+ if ((size_t)bytes_written < nbytes) {
238
+ int len = nbytes - bytes_written;
239
+ char *buffer = (char*) malloc (len + 1);
240
+ if (!buffer)
241
+ throw std::runtime_error ("bad alloc throwing back data");
242
+ memcpy (buffer, output_buffer + bytes_written, len);
243
+ buffer [len] = 0;
244
+ OutboundPages.push_front (OutboundPage (buffer, len));
245
+ }
246
+ #ifdef HAVE_EPOLL
247
+ EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
248
+ assert (MyEventMachine);
249
+ MyEventMachine->Modify (this);
250
+ #endif
251
+ }
252
+ else {
253
+ #ifdef OS_UNIX
254
+ if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
255
+ #endif
256
+ #ifdef OS_WIN32
257
+ if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
258
+ #endif
259
+ Close();
260
+ }
261
+ }
262
+
263
+
264
+ /*************************
265
+ PipeDescriptor::Heartbeat
266
+ *************************/
267
+
268
+ void PipeDescriptor::Heartbeat()
269
+ {
270
+ // If an inactivity timeout is defined, then check for it.
271
+ if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
272
+ ScheduleClose (false);
273
+ //bCloseNow = true;
274
+ }
275
+
276
+
277
+ /*****************************
278
+ PipeDescriptor::SelectForRead
279
+ *****************************/
280
+
281
+ bool PipeDescriptor::SelectForRead()
282
+ {
283
+ /* Pipe descriptors, being local by definition, don't have
284
+ * a pending state, so this is simpler than for the
285
+ * ConnectionDescriptor object.
286
+ */
287
+ return true;
288
+ }
289
+
290
+ /******************************
291
+ PipeDescriptor::SelectForWrite
292
+ ******************************/
293
+
294
+ bool PipeDescriptor::SelectForWrite()
295
+ {
296
+ /* Pipe descriptors, being local by definition, don't have
297
+ * a pending state, so this is simpler than for the
298
+ * ConnectionDescriptor object.
299
+ */
300
+ return (GetOutboundDataSize() > 0);
301
+ }
302
+
303
+
304
+
305
+
306
+ /********************************
307
+ PipeDescriptor::SendOutboundData
308
+ ********************************/
309
+
310
+ int PipeDescriptor::SendOutboundData (const char *data, int length)
311
+ {
312
+ //if (bCloseNow || bCloseAfterWriting)
313
+ if (IsCloseScheduled())
314
+ return 0;
315
+
316
+ if (!data && (length > 0))
317
+ throw std::runtime_error ("bad outbound data");
318
+ char *buffer = (char *) malloc (length + 1);
319
+ if (!buffer)
320
+ throw std::runtime_error ("no allocation for outbound data");
321
+ memcpy (buffer, data, length);
322
+ buffer [length] = 0;
323
+ OutboundPages.push_back (OutboundPage (buffer, length));
324
+ OutboundDataSize += length;
325
+ #ifdef HAVE_EPOLL
326
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
327
+ assert (MyEventMachine);
328
+ MyEventMachine->Modify (this);
329
+ #endif
330
+ return length;
331
+ }
332
+
333
+ /********************************
334
+ PipeDescriptor::GetSubprocessPid
335
+ ********************************/
336
+
337
+ bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
338
+ {
339
+ bool ok = false;
340
+ if (pid && (SubprocessPid > 0)) {
341
+ *pid = SubprocessPid;
342
+ ok = true;
343
+ }
344
+ return ok;
345
+ }
346
+
347
+
348
+ #endif // OS_UNIX
349
+