eventmachine 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/em.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: em.h 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: em.h 377 2007-06-13 01:05:56Z blackhedd $
4
4
 
5
5
  File: em.h
6
6
  Date: 06Apr06
@@ -70,12 +70,20 @@ class EventMachine_t
70
70
  const char *OpenDatagramSocket (const char *, int);
71
71
  const char *CreateUnixDomainServer (const char*);
72
72
  const char *_OpenFileForWriting (const char*);
73
+ //const char *Popen (const char*, const char*);
74
+ const char *Socketpair (char* const*);
73
75
 
74
76
  void Add (EventableDescriptor*);
77
+ void Modify (EventableDescriptor*);
75
78
 
76
79
  void SetTimerQuantum (int);
77
80
  static void SetuidString (const char*);
81
+ static int SetRlimitNofile (int);
78
82
 
83
+ // Temporary:
84
+ void _UseEpoll();
85
+
86
+ /*
79
87
  public:
80
88
  enum { // Event names
81
89
  TIMER_FIRED = 100,
@@ -85,19 +93,29 @@ class EventMachine_t
85
93
  CONNECTION_COMPLETED = 104,
86
94
  LOOPBREAK_SIGNAL = 105
87
95
  };
96
+ */
88
97
 
89
98
 
90
99
  private:
91
100
  bool _RunOnce();
92
101
  bool _RunTimers();
93
102
  void _AddNewDescriptors();
103
+ void _ModifyDescriptors();
94
104
  void _InitializeLoopBreaker();
105
+
106
+ bool _RunSelectOnce();
107
+ bool _RunEpollOnce();
108
+
109
+ void _ModifyEpollEvent (EventableDescriptor*);
110
+
111
+ public:
95
112
  void _ReadLoopBreaker();
96
113
 
97
114
  private:
98
115
  enum {
99
116
  MaxOutstandingTimers = 1000,
100
- HeartbeatInterval = 2
117
+ HeartbeatInterval = 2,
118
+ MaxEpollDescriptors = 64*1024
101
119
  };
102
120
  void (*EventCallback)(const char*, int, const char*, int);
103
121
 
@@ -107,6 +125,7 @@ class EventMachine_t
107
125
  multimap<Int64, Timer_t> Timers;
108
126
  vector<EventableDescriptor*> Descriptors;
109
127
  vector<EventableDescriptor*> NewDescriptors;
128
+ set<EventableDescriptor*> ModifiedDescriptors;
110
129
 
111
130
  time_t NextHeartbeatTime;
112
131
 
@@ -117,6 +136,10 @@ class EventMachine_t
117
136
  #endif
118
137
 
119
138
  timeval Quantum;
139
+
140
+ private:
141
+ bool bEpoll;
142
+ int epfd; // Epoll file-descriptor
120
143
  };
121
144
 
122
145
 
data/ext/emwin.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: emwin.cpp 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: emwin.cpp 327 2007-05-26 11:10:45Z blackhedd $
4
4
 
5
5
  File: emwin.cpp
6
6
  Date: 05May06
@@ -138,7 +138,7 @@ bool EventMachine_t::_RunTimers()
138
138
  if (i->first > gCurrentLoopTime)
139
139
  break;
140
140
  if (EventCallback)
141
- (*EventCallback) ("", TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
141
+ (*EventCallback) ("", EM_TIMER_FIRED, i->second.GetBinding().c_str(), i->second.GetBinding().length());
142
142
  Timers.erase (i);
143
143
  }
144
144
  return true;
data/ext/epoll.cpp ADDED
@@ -0,0 +1,26 @@
1
+ /*****************************************************************************
2
+
3
+ $Id: epoll.cpp 352 2007-06-06 20:24:11Z blackhedd $
4
+
5
+ File: epoll.cpp
6
+ Date: 06Jun07
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
+
21
+ #ifdef HAVE_EPOLL
22
+
23
+ #include "project.h"
24
+
25
+ #endif // HAVE_EPOLL
26
+
data/ext/epoll.h ADDED
@@ -0,0 +1,25 @@
1
+ /*****************************************************************************
2
+
3
+ $Id: epoll.h 352 2007-06-06 20:24:11Z blackhedd $
4
+
5
+ File: epoll.h
6
+ Date: 06Jun07
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
+
21
+ #ifdef HAVE_EPOLL
22
+
23
+
24
+ #endif // HAVE_EPOLL
25
+
data/ext/eventmachine.h CHANGED
@@ -1,6 +1,6 @@
1
1
  /*****************************************************************************
2
2
 
3
- $Id: eventmachine.h 318 2007-05-22 22:06:24Z blackhedd $
3
+ $Id: eventmachine.h 377 2007-06-13 01:05:56Z blackhedd $
4
4
 
5
5
  File: eventmachine.h
6
6
  Date: 15Apr06
@@ -24,6 +24,15 @@ See the file COPYING for complete licensing information.
24
24
  extern "C" {
25
25
  #endif
26
26
 
27
+ enum { // Event names
28
+ EM_TIMER_FIRED = 100,
29
+ EM_CONNECTION_READ = 101,
30
+ EM_CONNECTION_UNBOUND = 102,
31
+ EM_CONNECTION_ACCEPTED = 103,
32
+ EM_CONNECTION_COMPLETED = 104,
33
+ EM_LOOPBREAK_SIGNAL = 105
34
+ };
35
+
27
36
  void evma_initialize_library (void(*)(const char*, int, const char*, int));
28
37
  void evma_run_machine();
29
38
  void evma_release_library();
@@ -35,10 +44,13 @@ extern "C" {
35
44
  const char *evma_open_datagram_socket (const char *server, int port);
36
45
  void evma_start_tls (const char *binding);
37
46
  int evma_get_peername (const char *binding, struct sockaddr*);
47
+ int evma_get_subprocess_pid (const char *binding, pid_t*);
38
48
  int evma_send_data_to_connection (const char *binding, const char *data, int data_length);
39
49
  int evma_send_datagram (const char *binding, const char *data, int data_length, const char *address, int port);
40
50
  int evma_get_comm_inactivity_timeout (const char *binding, /*out*/int *value);
41
51
  int evma_set_comm_inactivity_timeout (const char *binding, /*in,out*/int *value);
52
+ int evma_get_outbound_data_size (const char *binding);
53
+
42
54
  void evma_close_connection (const char *binding, int after_writing);
43
55
  void evma_signal_loopbreak();
44
56
  void evma_set_timer_quantum (int);
@@ -46,6 +58,12 @@ extern "C" {
46
58
  void evma_stop_machine();
47
59
 
48
60
  const char *evma__write_file (const char *filename);
61
+ const char *evma_popen (char * const*cmd_strings);
62
+
63
+ int evma_set_rlimit_nofile (int n_files);
64
+
65
+ // Temporary:
66
+ void evma__epoll();
49
67
 
50
68
  #if __cplusplus
51
69
  }
data/ext/extconf.rb CHANGED
@@ -1,4 +1,4 @@
1
- # $Id: extconf.rb 318 2007-05-22 22:06:24Z blackhedd $
1
+ # $Id: extconf.rb 385 2007-06-16 11:38:59Z blackhedd $
2
2
  #
3
3
  #----------------------------------------------------------------------------
4
4
  #
@@ -89,6 +89,46 @@ when /darwin/
89
89
  # Ff line contributed by Daniel Harple.
90
90
  CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
91
91
 
92
+ when /linux/
93
+ unless have_library('pthread')
94
+ exit
95
+ end
96
+
97
+ flags << '-DOS_UNIX'
98
+ flags << '-DBUILD_FOR_RUBY'
99
+
100
+ # Original epoll test is inadequate because 2.4 kernels have the header
101
+ # but not the code.
102
+ #flags << '-DHAVE_EPOLL' if have_header('sys/epoll.h')
103
+ if have_header('sys/epoll.h')
104
+ File.open("hasEpollTest.c", "w") {|f|
105
+ f.puts "#include <sys/epoll.h>"
106
+ f.puts "int main() { epoll_create(1024); return 0;}"
107
+ }
108
+ (e = system( "gcc hasEpollTest.c -o hasEpollTest " )) and (e = $?.to_i)
109
+ `rm -f hasEpollTest.c hasEpollTest`
110
+ flags << '-DHAVE_EPOLL' if e == 0
111
+ end
112
+
113
+ dir_config('ssl')
114
+ if have_library('ssl') and
115
+ have_library('crypto') and
116
+ have_header('openssl/ssl.h') and
117
+ have_header('openssl/err.h')
118
+ flags << '-DWITH_SSL'
119
+ else
120
+ flags << '-DWITHOUT_SSL'
121
+ end
122
+ # on Unix we need a g++ link, not gcc.
123
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
124
+
125
+ # Modify the mkmf constant LINK_SO so the generated shared object is stripped.
126
+ # You might think modifying CONFIG['LINK_SO'] would be a better way to do this,
127
+ # but it doesn't work because mkmf doesn't look at CONFIG['LINK_SO'] again after
128
+ # it initializes.
129
+ linkso = Object.send :remove_const, "LINK_SO"
130
+ LINK_SO = linkso + "; strip $@"
131
+
92
132
  else
93
133
  unless have_library('pthread')
94
134
  exit
@@ -108,6 +148,7 @@ else
108
148
  end
109
149
  # on Unix we need a g++ link, not gcc.
110
150
  CONFIG['LDSHARED'] = "$(CXX) -shared"
151
+
111
152
  end
112
153
 
113
154
  if $CPPFLAGS
data/ext/files.cpp CHANGED
@@ -24,8 +24,8 @@ See the file COPYING for complete licensing information.
24
24
  FileStreamDescriptor::FileStreamDescriptor
25
25
  ******************************************/
26
26
 
27
- FileStreamDescriptor::FileStreamDescriptor (int fd):
28
- EventableDescriptor (fd),
27
+ FileStreamDescriptor::FileStreamDescriptor (int fd, EventMachine_t *em):
28
+ EventableDescriptor (fd, em),
29
29
  OutboundDataSize (0)
30
30
  {
31
31
  cerr << "#####";
data/ext/files.h CHANGED
@@ -30,7 +30,7 @@ class FileStreamDescriptor
30
30
  class FileStreamDescriptor: public EventableDescriptor
31
31
  {
32
32
  public:
33
- FileStreamDescriptor (int);
33
+ FileStreamDescriptor (int, EventMachine_t*);
34
34
  virtual ~FileStreamDescriptor();
35
35
 
36
36
  virtual void Read();
data/ext/pipe.cpp ADDED
@@ -0,0 +1,307 @@
1
+ /*****************************************************************************
2
+
3
+ $Id: pipe.cpp 401 2007-07-11 12:54:06Z blackhedd $
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 (FILE *fp, pid_t subpid, EventMachine_t *parent_em):
31
+ EventableDescriptor (fileno (fp), parent_em),
32
+ bReadAttemptedAfterClose (false),
33
+ LastIo (gCurrentLoopTime),
34
+ InactivityTimeout (0),
35
+ //MyStream (fp),
36
+ OutboundDataSize (0),
37
+ SubprocessPid (subpid)
38
+ {
39
+ #ifdef HAVE_EPOLL
40
+ EpollEvent.events = EPOLLIN;
41
+ #endif
42
+ }
43
+
44
+
45
+ /*******************************
46
+ PipeDescriptor::~PipeDescriptor
47
+ *******************************/
48
+
49
+ PipeDescriptor::~PipeDescriptor()
50
+ {
51
+ // Run down any stranded outbound data.
52
+ for (size_t i=0; i < OutboundPages.size(); i++)
53
+ OutboundPages[i].Free();
54
+
55
+ /* As a virtual destructor, we come here before the base-class
56
+ * destructor that closes our file-descriptor.
57
+ * We have to make sure the subprocess goes down (if it's not
58
+ * already down) and we have to reap the zombie.
59
+ *
60
+ * This implementation is PROVISIONAL and will surely be improved.
61
+ * The intention here is that we never block, hence the highly
62
+ * undesirable sleeps. But if we can't reap the subprocess even
63
+ * after sending it SIGKILL, then something is wrong and we
64
+ * throw a fatal exception, which is also not something we should
65
+ * be doing.
66
+ *
67
+ * Eventually the right thing to do will be to have the reactor
68
+ * core respond to SIGCHLD by chaining a handler on top of the
69
+ * one Ruby may have installed, and dealing with a list of dead
70
+ * children that are pending cleanup.
71
+ *
72
+ * Since we want to have a signal processor integrated into the
73
+ * client-visible API, let's wait until that is done before cleaning
74
+ * this up.
75
+ */
76
+
77
+ struct timespec req = {0, 10000000};
78
+ kill (SubprocessPid, SIGTERM);
79
+ nanosleep (&req, NULL);
80
+ if (waitpid (SubprocessPid, NULL, WNOHANG) == 0) {
81
+ kill (SubprocessPid, SIGKILL);
82
+ nanosleep (&req, NULL);
83
+ if (waitpid (SubprocessPid, NULL, WNOHANG) == 0)
84
+ throw std::runtime_error ("unable to reap subprocess");
85
+ }
86
+ }
87
+
88
+
89
+
90
+ /********************
91
+ PipeDescriptor::Read
92
+ ********************/
93
+
94
+ void PipeDescriptor::Read()
95
+ {
96
+ int sd = GetSocket();
97
+ if (sd == INVALID_SOCKET) {
98
+ assert (!bReadAttemptedAfterClose);
99
+ bReadAttemptedAfterClose = true;
100
+ return;
101
+ }
102
+
103
+ LastIo = gCurrentLoopTime;
104
+
105
+ int total_bytes_read = 0;
106
+ char readbuffer [16 * 1024];
107
+
108
+ for (int i=0; i < 10; i++) {
109
+ // Don't read just one buffer and then move on. This is faster
110
+ // if there is a lot of incoming.
111
+ // But don't read indefinitely. Give other sockets a chance to run.
112
+ // NOTICE, we're reading one less than the buffer size.
113
+ // That's so we can put a guard byte at the end of what we send
114
+ // to user code.
115
+ // Use read instead of recv, which on Linux gives a "socket operation
116
+ // on nonsocket" error.
117
+
118
+
119
+ int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
120
+ //cerr << "<R:" << r << ">";
121
+
122
+ if (r > 0) {
123
+ total_bytes_read += r;
124
+ LastRead = gCurrentLoopTime;
125
+
126
+ // Add a null-terminator at the the end of the buffer
127
+ // that we will send to the callback.
128
+ // DO NOT EVER CHANGE THIS. We want to explicitly allow users
129
+ // to be able to depend on this behavior, so they will have
130
+ // the option to do some things faster. Additionally it's
131
+ // a security guard against buffer overflows.
132
+ readbuffer [r] = 0;
133
+ if (EventCallback)
134
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r);
135
+ }
136
+ else if (r == 0) {
137
+ break;
138
+ }
139
+ else {
140
+ // Basically a would-block, meaning we've read everything there is to read.
141
+ break;
142
+ }
143
+
144
+ }
145
+
146
+
147
+ if (total_bytes_read == 0) {
148
+ // If we read no data on a socket that selected readable,
149
+ // it generally means the other end closed the connection gracefully.
150
+ ScheduleClose (false);
151
+ //bCloseNow = true;
152
+ }
153
+
154
+ }
155
+
156
+ /*********************
157
+ PipeDescriptor::Write
158
+ *********************/
159
+
160
+ void PipeDescriptor::Write()
161
+ {
162
+ int sd = GetSocket();
163
+ assert (sd != INVALID_SOCKET);
164
+
165
+ LastIo = gCurrentLoopTime;
166
+ char output_buffer [16 * 1024];
167
+ size_t nbytes = 0;
168
+
169
+ while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
170
+ OutboundPage *op = &(OutboundPages[0]);
171
+ if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
172
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
173
+ nbytes += (op->Length - op->Offset);
174
+ op->Free();
175
+ OutboundPages.pop_front();
176
+ }
177
+ else {
178
+ int len = sizeof(output_buffer) - nbytes;
179
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
180
+ op->Offset += len;
181
+ nbytes += len;
182
+ }
183
+ }
184
+
185
+ // We should never have gotten here if there were no data to write,
186
+ // so assert that as a sanity check.
187
+ // Don't bother to make sure nbytes is less than output_buffer because
188
+ // if it were we probably would have crashed already.
189
+ assert (nbytes > 0);
190
+
191
+ assert (GetSocket() != INVALID_SOCKET);
192
+ int bytes_written = write (GetSocket(), output_buffer, nbytes);
193
+
194
+ if (bytes_written > 0) {
195
+ OutboundDataSize -= bytes_written;
196
+ if ((size_t)bytes_written < nbytes) {
197
+ int len = nbytes - bytes_written;
198
+ char *buffer = (char*) malloc (len + 1);
199
+ if (!buffer)
200
+ throw std::runtime_error ("bad alloc throwing back data");
201
+ memcpy (buffer, output_buffer + bytes_written, len);
202
+ buffer [len] = 0;
203
+ OutboundPages.push_front (OutboundPage (buffer, len));
204
+ }
205
+ #ifdef HAVE_EPOLL
206
+ EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
207
+ assert (MyEventMachine);
208
+ MyEventMachine->Modify (this);
209
+ #endif
210
+ }
211
+ else {
212
+ #ifdef OS_UNIX
213
+ if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
214
+ #endif
215
+ #ifdef OS_WIN32
216
+ if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
217
+ #endif
218
+ Close();
219
+ }
220
+ }
221
+
222
+
223
+ /*************************
224
+ PipeDescriptor::Heartbeat
225
+ *************************/
226
+
227
+ void PipeDescriptor::Heartbeat()
228
+ {
229
+ // If an inactivity timeout is defined, then check for it.
230
+ if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
231
+ ScheduleClose (false);
232
+ //bCloseNow = true;
233
+ }
234
+
235
+
236
+ /*****************************
237
+ PipeDescriptor::SelectForRead
238
+ *****************************/
239
+
240
+ bool PipeDescriptor::SelectForRead()
241
+ {
242
+ /* Pipe descriptors, being local by definition, don't have
243
+ * a pending state, so this is simpler than for the
244
+ * ConnectionDescriptor object.
245
+ */
246
+ return true;
247
+ }
248
+
249
+ /******************************
250
+ PipeDescriptor::SelectForWrite
251
+ ******************************/
252
+
253
+ bool PipeDescriptor::SelectForWrite()
254
+ {
255
+ /* Pipe descriptors, being local by definition, don't have
256
+ * a pending state, so this is simpler than for the
257
+ * ConnectionDescriptor object.
258
+ */
259
+ return (GetOutboundDataSize() > 0);
260
+ }
261
+
262
+
263
+
264
+
265
+ /********************************
266
+ PipeDescriptor::SendOutboundData
267
+ ********************************/
268
+
269
+ int PipeDescriptor::SendOutboundData (const char *data, int length)
270
+ {
271
+ //if (bCloseNow || bCloseAfterWriting)
272
+ if (IsCloseScheduled())
273
+ return 0;
274
+
275
+ if (!data && (length > 0))
276
+ throw std::runtime_error ("bad outbound data");
277
+ char *buffer = (char *) malloc (length + 1);
278
+ if (!buffer)
279
+ throw std::runtime_error ("no allocation for outbound data");
280
+ memcpy (buffer, data, length);
281
+ buffer [length] = 0;
282
+ OutboundPages.push_back (OutboundPage (buffer, length));
283
+ OutboundDataSize += length;
284
+ #ifdef HAVE_EPOLL
285
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
286
+ assert (MyEventMachine);
287
+ MyEventMachine->Modify (this);
288
+ #endif
289
+ return length;
290
+ }
291
+
292
+ /********************************
293
+ PipeDescriptor::GetSubprocessPid
294
+ ********************************/
295
+
296
+ bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
297
+ {
298
+ bool ok = false;
299
+ if (pid && (SubprocessPid > 0)) {
300
+ *pid = SubprocessPid;
301
+ ok = true;
302
+ }
303
+ return ok;
304
+ }
305
+
306
+ #endif // OS_UNIX
307
+