eventmachine 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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