eventmachine 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
- $Id: DEFERRABLES 541 2007-09-17 15:08:36Z blackhedd $
1
+ $Id: DEFERRABLES 551 2007-09-22 12:57:36Z blackhedd $
2
2
 
3
3
  [DOCUMENT UNDER CONSTRUCTION]
4
4
 
5
- EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use spawned processes. For more information, see the separate document LIGHTWEIGHT_CONCURRENCY.
5
+ EventMachine (EM) adds two different formalisms for lightweight concurrency to the Ruby programmer's toolbox: spawned processes and deferrables. This note will show you how to use deferrables. For more information, see the separate document LIGHTWEIGHT_CONCURRENCY.
6
6
 
7
7
 
8
8
  === What are Deferrables?
@@ -132,7 +132,7 @@ Now of course it's somewhat trivial to define two callbacks in the same method,
132
132
 
133
133
  Remember that you can add a callback or an errback to a Deferrable at any point in time, regardless of whether the status of the deferred operation is known (more precisely, regardless of when #set_deferred_status is called on the object). Even hours or days later.
134
134
 
135
- When you add a callback or errback to a Deferrable object on which #set_deferred_status has not yet been called, the callback/errback is queued up for future execution, inside the Deferrable object. When you add a callback or errback to a Deferrable on which #set_deferred_status has already been called, the callback/errback will be executed immediately. Your code doesn't have to worry about the ordering, and there are no timing issues, as you would expect with a threaded approach.
135
+ When you add a callback or errback to a Deferrable object on which #set_deferred_status has not yet been called, the callback/errback is queued up for future execution, inside the Deferrable object. When you add a callback or errback to a Deferrable on which #set_deferred_status has already been called, the callback/errback will be executed immediately. Your code doesn't have to worry about the ordering, and there are no timing issues, as there would be with a threaded approach.
136
136
 
137
137
  For more information on Deferrables and their typical usage patterns, look in the EM unit tests. There are also quite a few sugarings (including EM::Deferrable#future) that make typical Deferrable usages syntactically easier to work with.
138
138
 
@@ -0,0 +1,77 @@
1
+ $Id: PURE_RUBY 602 2007-12-05 18:45:29Z blackhedd $
2
+
3
+ EventMachine is supplied in three alternative versions.
4
+
5
+ 1) A version that includes a Ruby extension written in C++. This version requires compilation;
6
+ 2) A version for JRuby that contains a precompiled JAR file written in Java;
7
+ 3) A pure Ruby version that has no external dependencies and can run in any Ruby environment.
8
+
9
+ The Java version of EventMachine is packaged in a distinct manner and must be installed using a
10
+ special procedure. This version is described fully in a different document, and not considered
11
+ further here.
12
+
13
+ The C++ and pure-Ruby versions, however, are shipped in the same distribution. You use the same
14
+ files (either tarball or Ruby gem) to install both of these versions.
15
+
16
+ If you intend to use the C++ version, you must successfully compile EventMachine after you install it.
17
+ (The gem installation attempts to perform this step automatically.)
18
+
19
+ If you choose not to compile the EventMachine C++ extension, or if your compilation fails for any
20
+ reason, you still have a fully-functional installation of the pure-Ruby version of EM.
21
+
22
+ However, for technical reasons, a default EM installation (whether or not the compilation succeeds)
23
+ will always assume that the compiled ("extension") implementation should be used.
24
+
25
+ If you want your EM program to use the pure Ruby version, you must specifically request it. There
26
+ are two ways to do this: by setting either a Ruby global variable, or an environment string.
27
+
28
+ The following code will invoke the pure-Ruby implementation of EM:
29
+
30
+ $eventmachine_library = :pure_ruby
31
+ require 'eventmachine'
32
+
33
+ EM.library_type #=> "pure_ruby"
34
+
35
+ Notice that this requires a code change and is not the preferred way to select pure Ruby, unless
36
+ for some reason you are absolutely sure you will never want the compiled implementation.
37
+
38
+ Setting the following environment string has the same effect:
39
+
40
+ export EVENTMACHINE_LIBRARY="pure_ruby"
41
+
42
+ This technique gives you the flexibility to select either version at runtime with no code changes.
43
+
44
+ Support
45
+
46
+ The EventMachine development team has committed to support precisely the same APIs for all the
47
+ various implementations of EM.
48
+
49
+ This means that you can expect any EM program to behave identically, whether you use pure Ruby,
50
+ the compiled C++ extension, or JRuby. Deviations from this behavior are to be considered bugs
51
+ and should be reported as such.
52
+
53
+ There is a small number of exceptions to this rule, which arise from underlying platform
54
+ distinctions. Notably, EM#epoll is a silent no-op in the pure Ruby implementation.
55
+
56
+
57
+ When Should You Use the Pure-Ruby Implementation of EM?
58
+
59
+
60
+ Use the pure Ruby implementation of EM when you must support a platform for which no C++ compiler
61
+ is available, or on which the standard EM C++ code can't be compiled.
62
+
63
+ Keep in mind that you don't need a C++ compiler in order to deploy EM applications that rely on
64
+ the compiled version, so long as appropriate C++ runtime libraries are available on the target platform.
65
+
66
+ In extreme cases, you may find that you can develop software with the compiled EM version, but are
67
+ not allowed to install required runtime libraries on the deployment system(s). This would be another
68
+ case in which the pure Ruby implementation can be useful.
69
+
70
+ In general you should avoid the pure Ruby version of EM when performance and scalability are important.
71
+ EM in pure Ruby will necessarily run slower than the compiled version. Depending on your application
72
+ this may or may not be a key issue.
73
+
74
+ Also, since EPOLL is not supported in pure Ruby, your applications will be affected by Ruby's built-in
75
+ limit of 1024 file and socket descriptors that may be open in a single process. For maximum scalability
76
+ and performance, always use EPOLL if possible.
77
+
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: cmain.cpp 502 2007-08-24 09:29:53Z blackhedd $
3
+ $Id: cmain.cpp 585 2007-11-27 14:29:34Z blackhedd $
4
4
 
5
5
  File: cmain.cpp
6
6
  Date: 06Apr06
@@ -181,6 +181,17 @@ extern "C" void evma_close_connection (const char *binding, int after_writing)
181
181
  ConnectionDescriptor::CloseConnection (binding, (after_writing ? true : false));
182
182
  }
183
183
 
184
+ /***********************************
185
+ evma_report_connection_error_status
186
+ ***********************************/
187
+
188
+ extern "C" int evma_report_connection_error_status (const char *binding)
189
+ {
190
+ if (!EventMachine)
191
+ throw std::runtime_error ("not initialized");
192
+ return ConnectionDescriptor::ReportErrorStatus (binding);
193
+ }
194
+
184
195
  /********************
185
196
  evma_stop_tcp_server
186
197
  ********************/
@@ -264,6 +275,22 @@ extern "C" int evma_get_subprocess_pid (const char *binding, pid_t *pid)
264
275
  return 0;
265
276
  }
266
277
 
278
+ /**************************
279
+ evma_get_subprocess_status
280
+ **************************/
281
+
282
+ extern "C" int evma_get_subprocess_status (const char *binding, int *status)
283
+ {
284
+ if (!EventMachine)
285
+ throw std::runtime_error ("not initialized");
286
+ if (status) {
287
+ *status = EventMachine->SubprocessExitStatus;
288
+ return 1;
289
+ }
290
+ else
291
+ return 0;
292
+ }
293
+
267
294
 
268
295
  /*********************
269
296
  evma_signal_loopbreak
@@ -331,7 +358,19 @@ extern "C" void evma_set_timer_quantum (int interval)
331
358
  {
332
359
  if (!EventMachine)
333
360
  throw std::runtime_error ("not initialized");
334
- EventMachine->SetTimerQuantum (interval);
361
+ EventMachine->SetTimerQuantum (interval);
362
+ }
363
+
364
+ /************************
365
+ evma_set_max_timer_count
366
+ ************************/
367
+
368
+ extern "C" void evma_set_max_timer_count (int ct)
369
+ {
370
+ // This may only be called if the reactor is not running.
371
+ if (EventMachine)
372
+ throw std::runtime_error ("already initialized");
373
+ EventMachine_t::SetMaxTimerCount (ct);
335
374
  }
336
375
 
337
376
  /******************
data/ext/ed.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.cpp 497 2007-08-15 13:57:49Z blackhedd $
3
+ $Id: ed.cpp 572 2007-11-15 23:13:45Z blackhedd $
4
4
 
5
5
  File: ed.cpp
6
6
  Date: 06Apr06
@@ -243,6 +243,21 @@ void ConnectionDescriptor::CloseConnection (const char *binding, bool after_writ
243
243
  ed->ScheduleClose (after_writing);
244
244
  }
245
245
 
246
+ /***********************************************
247
+ STATIC: ConnectionDescriptor::ReportErrorStatus
248
+ ***********************************************/
249
+
250
+ int ConnectionDescriptor::ReportErrorStatus (const char *binding)
251
+ {
252
+ // TODO: This is something of a hack, or at least it's a static method of the wrong class.
253
+ // TODO: Poor polymorphism here. We should be calling one virtual method
254
+ // instead of hacking out the runtime information of the target object.
255
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
256
+ if (cd)
257
+ return cd->_ReportErrorStatus();
258
+ return -1;
259
+ }
260
+
246
261
 
247
262
 
248
263
  /**************************************
@@ -395,7 +410,7 @@ void ConnectionDescriptor::Read()
395
410
  LastIo = gCurrentLoopTime;
396
411
 
397
412
  int total_bytes_read = 0;
398
- char readbuffer [16 * 1024];
413
+ char readbuffer [16 * 1024 + 1];
399
414
 
400
415
  for (int i=0; i < 10; i++) {
401
416
  // Don't read just one buffer and then move on. This is faster
@@ -612,6 +627,27 @@ void ConnectionDescriptor::_WriteOutboundData()
612
627
  }
613
628
 
614
629
 
630
+ /****************************************
631
+ ConnectionDescriptor::_ReportErrorStatus
632
+ ****************************************/
633
+
634
+ int ConnectionDescriptor::_ReportErrorStatus()
635
+ {
636
+ int error;
637
+ socklen_t len;
638
+ len = sizeof(error);
639
+ #ifdef OS_UNIX
640
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
641
+ #endif
642
+ #ifdef OS_WIN32
643
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
644
+ #endif
645
+ if ((o == 0) && (error == 0))
646
+ return 0;
647
+ else
648
+ return 1;
649
+ }
650
+
615
651
 
616
652
  /******************************
617
653
  ConnectionDescriptor::StartTls
@@ -926,6 +962,27 @@ DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em):
926
962
  {
927
963
  memset (&ReturnAddress, 0, sizeof(ReturnAddress));
928
964
 
965
+ /* Provisionally added 19Oct07. All datagram sockets support broadcasting.
966
+ * Until now, sending to a broadcast address would give EACCES (permission denied)
967
+ * on systems like Linux and BSD that require the SO_BROADCAST socket-option in order
968
+ * to accept a packet to a broadcast address. Solaris doesn't require it. I think
969
+ * Windows DOES require it but I'm not sure.
970
+ *
971
+ * Ruby does NOT do what we're doing here. In Ruby, you have to explicitly set SO_BROADCAST
972
+ * on a UDP socket in order to enable broadcasting. The reason for requiring the option
973
+ * in the first place is so that applications don't send broadcast datagrams by mistake.
974
+ * I imagine that could happen if a user of an application typed in an address that happened
975
+ * to be a broadcast address on that particular subnet.
976
+ *
977
+ * This is provisional because someone may eventually come up with a good reason not to
978
+ * do it for all UDP sockets. If that happens, then we'll need to add a usercode-level API
979
+ * to set the socket option, just like Ruby does. AND WE'LL ALSO BREAK CODE THAT DOESN'T
980
+ * EXPLICITLY SET THE OPTION.
981
+ */
982
+
983
+ int oval = 1;
984
+ int sob = setsockopt (GetSocket(), SOL_SOCKET, SO_BROADCAST, (char*)&oval, sizeof(oval));
985
+
929
986
  #ifdef HAVE_EPOLL
930
987
  EpollEvent.events = EPOLLIN;
931
988
  #endif
@@ -1090,7 +1147,14 @@ DatagramDescriptor::SelectForWrite
1090
1147
 
1091
1148
  bool DatagramDescriptor::SelectForWrite()
1092
1149
  {
1093
- return (GetOutboundDataSize() > 0);
1150
+ /* Changed 15Nov07, per bug report by Mark Zvillius.
1151
+ * The outbound data size will be zero if there are zero-length outbound packets,
1152
+ * so we now select writable in case the outbound page buffer is not empty.
1153
+ * Note that the superclass ShouldDelete method still checks for outbound data size,
1154
+ * which may be wrong.
1155
+ */
1156
+ //return (GetOutboundDataSize() > 0); (Original)
1157
+ return (OutboundPages.size() > 0);
1094
1158
  }
1095
1159
 
1096
1160
 
data/ext/ed.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: ed.h 502 2007-08-24 09:29:53Z blackhedd $
3
+ $Id: ed.h 585 2007-11-27 14:29:34Z blackhedd $
4
4
 
5
5
  File: ed.h
6
6
  Date: 06Apr06
@@ -133,6 +133,7 @@ class ConnectionDescriptor: public EventableDescriptor
133
133
 
134
134
  static int SendDataToConnection (const char*, const char*, int);
135
135
  static void CloseConnection (const char*, bool);
136
+ static int ReportErrorStatus (const char*);
136
137
 
137
138
  int SendOutboundData (const char*, int);
138
139
 
@@ -157,6 +158,7 @@ class ConnectionDescriptor: public EventableDescriptor
157
158
  virtual int GetCommInactivityTimeout (int *value);
158
159
  virtual int SetCommInactivityTimeout (int *value);
159
160
 
161
+
160
162
  protected:
161
163
  struct OutboundPage {
162
164
  OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
@@ -189,6 +191,7 @@ class ConnectionDescriptor: public EventableDescriptor
189
191
  void _DispatchInboundData (const char *buffer, int size);
190
192
  void _DispatchCiphertext();
191
193
  int _SendRawOutboundData (const char*, int);
194
+ int _ReportErrorStatus();
192
195
 
193
196
  };
194
197
 
data/ext/em.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: em.cpp 533 2007-09-14 17:36:04Z blackhedd $
3
+ $Id: em.cpp 587 2007-12-01 13:50:32Z blackhedd $
4
4
 
5
5
  File: em.cpp
6
6
  Date: 06Apr06
@@ -34,6 +34,33 @@ unsigned gLastTickCount;
34
34
  #endif
35
35
 
36
36
 
37
+ /* The numer of max outstanding timers was once a const enum defined in em.h.
38
+ * Now we define it here so that users can change its value if necessary.
39
+ */
40
+ static int MaxOutstandingTimers = 1000;
41
+
42
+
43
+
44
+ /***************************************
45
+ STATIC EventMachine_t::SetMaxTimerCount
46
+ ***************************************/
47
+
48
+ void EventMachine_t::SetMaxTimerCount (int count)
49
+ {
50
+ /* Allow a user to increase the maximum number of outstanding timers.
51
+ * If this gets "too high" (a metric that is of course platform dependent),
52
+ * bad things will happen like performance problems and possible overuse
53
+ * of memory.
54
+ * The actual timer mechanism is very efficient so it's hard to know what
55
+ * the practical max, but 100,000 shouldn't be too problematical.
56
+ */
57
+ if (count < 100)
58
+ count = 100;
59
+ MaxOutstandingTimers = count;
60
+ }
61
+
62
+
63
+
37
64
  /******************************
38
65
  EventMachine_t::EventMachine_t
39
66
  ******************************/
@@ -1382,8 +1409,11 @@ const char *EventMachine_t::Socketpair (char * const*cmd_strings)
1382
1409
  return NULL;
1383
1410
  // from here, all early returns must close the pair of sockets.
1384
1411
 
1385
- // Set the socketpair nonblocking. Obviously DON'T set CLOEXEC.
1386
- if (!SetSocketNonblocking (sv[0]) || !SetSocketNonblocking (sv[1])) {
1412
+ // Set the parent side of the socketpair nonblocking.
1413
+ // We don't care about the child side, and most child processes will expect their
1414
+ // stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out.
1415
+ // Obviously DON'T set CLOEXEC.
1416
+ if (!SetSocketNonblocking (sv[0])) {
1387
1417
  close (sv[0]);
1388
1418
  close (sv[1]);
1389
1419
  return NULL;
@@ -1420,18 +1450,11 @@ EventMachine_t::OpenKeyboard
1420
1450
 
1421
1451
  const char *EventMachine_t::OpenKeyboard()
1422
1452
  {
1423
- #ifdef OS_UNIX
1424
1453
  KeyboardDescriptor *kd = new KeyboardDescriptor (this);
1425
1454
  if (!kd)
1426
1455
  throw std::runtime_error ("no keyboard-object allocated");
1427
1456
  Add (kd);
1428
1457
  return kd->GetBinding().c_str();
1429
- #endif
1430
-
1431
- #ifdef OS_WIN32
1432
- throw std::runtime_error ("open-keyboard is currently unavailable on this platform");
1433
- return NULL;
1434
- #endif
1435
1458
  }
1436
1459
 
1437
1460
 
data/ext/em.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: em.h 502 2007-08-24 09:29:53Z blackhedd $
3
+ $Id: em.h 585 2007-11-27 14:29:34Z blackhedd $
4
4
 
5
5
  File: em.h
6
6
  Date: 06Apr06
@@ -56,6 +56,9 @@ class EventMachine_t
56
56
 
57
57
  class EventMachine_t
58
58
  {
59
+ public:
60
+ static void SetMaxTimerCount (int);
61
+
59
62
  public:
60
63
  EventMachine_t (void(*event_callback)(const char*, int, const char*, int));
61
64
  virtual ~EventMachine_t();
@@ -81,21 +84,11 @@ class EventMachine_t
81
84
  static void SetuidString (const char*);
82
85
  static int SetRlimitNofile (int);
83
86
 
87
+ int SubprocessExitStatus;
88
+
84
89
  // Temporary:
85
90
  void _UseEpoll();
86
91
 
87
- /*
88
- public:
89
- enum { // Event names
90
- TIMER_FIRED = 100,
91
- CONNECTION_READ = 101,
92
- CONNECTION_UNBOUND = 102,
93
- CONNECTION_ACCEPTED = 103,
94
- CONNECTION_COMPLETED = 104,
95
- LOOPBREAK_SIGNAL = 105
96
- };
97
- */
98
-
99
92
 
100
93
  private:
101
94
  bool _RunOnce();
@@ -114,7 +107,6 @@ class EventMachine_t
114
107
 
115
108
  private:
116
109
  enum {
117
- MaxOutstandingTimers = 1000,
118
110
  HeartbeatInterval = 2,
119
111
  MaxEpollDescriptors = 64*1024
120
112
  };
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: eventmachine.h 502 2007-08-24 09:29:53Z blackhedd $
3
+ $Id: eventmachine.h 584 2007-11-27 07:27:32Z blackhedd $
4
4
 
5
5
  File: eventmachine.h
6
6
  Date: 15Apr06
@@ -48,6 +48,7 @@ extern "C" {
48
48
  void evma_start_tls (const char *binding);
49
49
  int evma_get_peername (const char *binding, struct sockaddr*);
50
50
  int evma_get_subprocess_pid (const char *binding, pid_t*);
51
+ int evma_get_subprocess_status (const char *binding, int*);
51
52
  int evma_send_data_to_connection (const char *binding, const char *data, int data_length);
52
53
  int evma_send_datagram (const char *binding, const char *data, int data_length, const char *address, int port);
53
54
  int evma_get_comm_inactivity_timeout (const char *binding, /*out*/int *value);
@@ -56,8 +57,10 @@ extern "C" {
56
57
  int evma_send_file_data_to_connection (const char *binding, const char *filename);
57
58
 
58
59
  void evma_close_connection (const char *binding, int after_writing);
60
+ int evma_report_connection_error_status (const char *binding);
59
61
  void evma_signal_loopbreak();
60
62
  void evma_set_timer_quantum (int);
63
+ void evma_set_max_timer_count (int);
61
64
  void evma_setuid_string (const char *username);
62
65
  void evma_stop_machine();
63
66