ebb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,311 @@
1
+ # Ruby Binding to the Ebb Web Server
2
+ # Copyright (c) 2008 Ry Dahl. This software is released under the MIT License.
3
+ # See README file for details.
4
+
5
+ require File.dirname(__FILE__) + '/ebb/version'
6
+
7
+ module Ebb
8
+ autoload :FFI, File.dirname(__FILE__) + '/../ext/ebb_ffi'
9
+
10
+ def self.running?
11
+ FFI::server_open?
12
+ end
13
+
14
+ def self.stop_server()
15
+ @running = false
16
+ end
17
+
18
+ def self.start_server(app, options={})
19
+ if options.has_key?(:fileno)
20
+ fd = options[:fileno].to_i
21
+ FFI::server_listen_on_fd(fd)
22
+ log.puts "Ebb is listening on file descriptor #{fd}"
23
+ # missing until libebb adds UNIX socket support
24
+ # elsif options.has_key?(:unix_socket)
25
+ # socketfile = options[:unix_socket]
26
+ # FFI::server_listen_on_unix_socket(socketfile)
27
+ # log.puts "Ebb is listening on unix socket #{socketfile}"
28
+ else
29
+ port = (options[:Port] || options[:port] || 4001).to_i
30
+ FFI::server_listen_on_port(port)
31
+ log.puts "Ebb is listening at http://0.0.0.0:#{port}/"
32
+ end
33
+
34
+ if options.has_key?(:ssl_cert) and options.has_key?(:ssl_key)
35
+ unless FFI.respond_to?(:server_set_secure)
36
+ log.puts "ebb compiled without ssl support. get gnutls"
37
+ else
38
+ cert_file = options[:ssl_cert]
39
+ key_file = options[:ssl_key]
40
+ if FileTest.readable?(cert_file) and FileTest.readable?(cert_file)
41
+ FFI::server_set_secure(cert_file, key_file);
42
+ else
43
+ log.puts "error opening certificate or key file"
44
+ end
45
+ end
46
+ end
47
+
48
+ log.puts "Ebb PID #{Process.pid}"
49
+
50
+ @running = true
51
+ Connection.reset_responses
52
+ trap('INT') { stop_server }
53
+
54
+ while @running
55
+ FFI::server_process_connections()
56
+ while request = FFI::server_waiting_requests.shift
57
+ if app.respond_to?(:deferred?) and !app.deferred?(request.env)
58
+ process(app, request)
59
+ else
60
+ Thread.new(request) { |req| process(app, req) }
61
+ end
62
+ end
63
+ end
64
+ FFI::server_unlisten()
65
+ end
66
+
67
+ def self.process(app, req)
68
+ res = req.response
69
+ req.connection.responses << res
70
+
71
+ # p req.env
72
+ status = headers = body = nil
73
+ catch(:async) do
74
+ status, headers, body = app.call(req.env)
75
+ end
76
+
77
+ # James Tucker's async response scheme
78
+ # check out
79
+ # http://github.com/raggi/thin/tree/async_for_rack/example/async_app.ru
80
+ res.call(status, headers, body) if status != 0
81
+ # if status == 0 then the application promises to call
82
+ # env['async.callback'].call(status, headers, body)
83
+ # later on...
84
+
85
+ end
86
+
87
+ class Connection
88
+ def self.reset_responses
89
+ @@responses = {} # used for memory management :|
90
+ end
91
+
92
+ def self.responses
93
+ @@responses
94
+ end
95
+
96
+ def responses
97
+ @@responses[self]
98
+ end
99
+
100
+ # called from c
101
+ def append_request(req)
102
+ @requests.push req
103
+ end
104
+
105
+ def on_open
106
+ @requests = []
107
+ @@responses[self] = []
108
+ end
109
+
110
+ def on_close
111
+ # garbage collection !
112
+ @requests.each { |req| req.connection = nil }
113
+ responses.each { |res| res.connection = nil }
114
+ @@responses.delete(self)
115
+ end
116
+
117
+ def writing?
118
+ ! @being_written.nil?
119
+ end
120
+
121
+ def write
122
+ return if writing?
123
+ return unless res = responses.first
124
+ return if res.output.empty?
125
+ # NOTE: connection_write does not buffer!
126
+ chunk = res.output.shift
127
+ @being_written = chunk # need to store this so ruby doesn't gc it
128
+ FFI::connection_write(self, chunk)
129
+ end
130
+
131
+ # called after FFI::connection_write if complete
132
+ def on_writable
133
+ @being_written = nil
134
+ return unless res = responses.first
135
+ if res.finished?
136
+ responses.shift
137
+ if res.last
138
+ FFI::connection_schedule_close(self)
139
+ return
140
+ end
141
+ end
142
+ write
143
+ end
144
+ end
145
+
146
+ class Response
147
+ attr_reader :output
148
+ attr_accessor :last, :connection
149
+ def initialize(connection, last)
150
+ @connection = connection
151
+ @last = last
152
+ @output = []
153
+ @finished = false
154
+ @chunked = false
155
+ end
156
+
157
+ def call(status, headers, body)
158
+ @head = "HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status.to_i]}\r\n"
159
+ headers.each { |field, value| @head << "#{field}: #{value}\r\n" }
160
+ @head << "\r\n"
161
+
162
+ # XXX i would prefer to do
163
+ # @chunked = true unless body.respond_to?(:length)
164
+ @chunked = true if headers["Transfer-Encoding"] == "chunked"
165
+ # I also don't like this
166
+ @last = true if headers["Connection"] == "close"
167
+
168
+ # Note: not setting Content-Length. do it yourself.
169
+
170
+ body.each do |chunk|
171
+ if @head.nil?
172
+ write(chunk)
173
+ else
174
+ write(@head + chunk)
175
+ @head = nil
176
+ end
177
+ @connection.write
178
+ end
179
+
180
+ body.on_error { close } if body.respond_to?(:on_error)
181
+
182
+ if body.respond_to?(:on_eof)
183
+ body.on_eof { finish }
184
+ else
185
+ finish
186
+ end
187
+
188
+ # deferred requests SHOULD NOT respond to close
189
+ body.close if body.respond_to?(:close)
190
+ end
191
+
192
+ def finished?
193
+ @output.empty? and @finished
194
+ end
195
+
196
+ def finish
197
+ @finished = true
198
+ if @chunked
199
+ write("")
200
+ @connection.write
201
+ end
202
+ end
203
+
204
+ def write(chunk)
205
+ encoded = @chunked ? "#{chunk.length.to_s(16)}\r\n#{chunk}\r\n" : chunk
206
+ @output << encoded
207
+ end
208
+
209
+ HTTP_STATUS_CODES = {
210
+ 100 => 'Continue',
211
+ 101 => 'Switching Protocols',
212
+ 200 => 'OK',
213
+ 201 => 'Created',
214
+ 202 => 'Accepted',
215
+ 203 => 'Non-Authoritative Information',
216
+ 204 => 'No Content',
217
+ 205 => 'Reset Content',
218
+ 206 => 'Partial Content',
219
+ 300 => 'Multiple Choices',
220
+ 301 => 'Moved Permanently',
221
+ 302 => 'Moved Temporarily',
222
+ 303 => 'See Other',
223
+ 304 => 'Not Modified',
224
+ 305 => 'Use Proxy',
225
+ 400 => 'Bad Request',
226
+ 401 => 'Unauthorized',
227
+ 402 => 'Payment Required',
228
+ 403 => 'Forbidden',
229
+ 404 => 'Not Found',
230
+ 405 => 'Method Not Allowed',
231
+ 406 => 'Not Acceptable',
232
+ 407 => 'Proxy Authentication Required',
233
+ 408 => 'Request Time-out',
234
+ 409 => 'Conflict',
235
+ 410 => 'Gone',
236
+ 411 => 'Length Required',
237
+ 412 => 'Precondition Failed',
238
+ 413 => 'Request Entity Too Large',
239
+ 414 => 'Request-URI Too Large',
240
+ 415 => 'Unsupported Media Type',
241
+ 500 => 'Internal Server Error',
242
+ 501 => 'Not Implemented',
243
+ 502 => 'Bad Gateway',
244
+ 503 => 'Service Unavailable',
245
+ 504 => 'Gateway Time-out',
246
+ 505 => 'HTTP Version not supported'
247
+ }.freeze
248
+ end
249
+
250
+ @@log = STDOUT
251
+
252
+ def self.log=(output)
253
+ @@log = output
254
+ end
255
+
256
+ def self.log
257
+ @@log
258
+ end
259
+
260
+ class Request
261
+ attr_accessor :connection
262
+
263
+ def env
264
+ @env ||= begin
265
+ @env_ffi.update(
266
+ 'SERVER_NAME' => '0.0.0.0',
267
+ 'SCRIPT_NAME' => '',
268
+ 'SERVER_SOFTWARE' => Ebb::VERSION_STRING,
269
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
270
+ 'rack.version' => [0, 1],
271
+ 'rack.errors' => STDERR,
272
+ 'rack.url_scheme' => 'http',
273
+ 'rack.multiprocess' => false,
274
+ 'rack.run_once' => false,
275
+
276
+ 'rack.input' => self,
277
+ 'async.callback' => response,
278
+ 'CONTENT_LENGTH' => @env_ffi.delete('HTTP_CONTENT_LENGTH'),
279
+ 'CONTENT_TYPE' => @env_ffi.delete('HTTP_CONTENT_TYPE')
280
+ )
281
+ end
282
+ end
283
+
284
+ def keep_alive?
285
+ FFI::request_should_keep_alive?(self)
286
+ end
287
+
288
+ def response
289
+ @response ||= begin
290
+ last = !keep_alive? # this is the last response if the request isnt keep-alive
291
+ Response.new(@connection, last)
292
+ end
293
+ end
294
+
295
+ def read(want = 1024)
296
+ FFI::request_read(self, want)
297
+ end
298
+ end
299
+ end
300
+
301
+
302
+ module Rack
303
+ module Handler
304
+ module Ebb
305
+ def self.run(app, options={})
306
+ ::Ebb.start_server(app, options)
307
+ end
308
+ end
309
+ end
310
+ end
311
+
@@ -0,0 +1,4 @@
1
+ module Ebb
2
+ VERSION = "0.3.0"
3
+ VERSION_STRING = "Ebb #{VERSION}"
4
+ end
@@ -0,0 +1,803 @@
1
+ /*
2
+ * libev simple C++ wrapper classes
3
+ *
4
+ * Copyright (c) 2007,2008 Marc Alexander Lehmann <libev@schmorp.de>
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without modifica-
8
+ * tion, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ *
13
+ * 2. Redistributions in binary form must reproduce the above copyright
14
+ * notice, this list of conditions and the following disclaimer in the
15
+ * documentation and/or other materials provided with the distribution.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
19
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
21
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
25
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
26
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ *
28
+ * Alternatively, the contents of this file may be used under the terms of
29
+ * the GNU General Public License ("GPL") version 2 or any later version,
30
+ * in which case the provisions of the GPL are applicable instead of
31
+ * the above. If you wish to allow the use of your version of this file
32
+ * only under the terms of the GPL and not to allow others to use your
33
+ * version of this file under the BSD license, indicate your decision
34
+ * by deleting the provisions above and replace them with the notice
35
+ * and other provisions required by the GPL. If you do not delete the
36
+ * provisions above, a recipient may use your version of this file under
37
+ * either the BSD or the GPL.
38
+ */
39
+
40
+ #ifndef EVPP_H__
41
+ #define EVPP_H__
42
+
43
+ #ifdef EV_H
44
+ # include EV_H
45
+ #else
46
+ # include "ev.h"
47
+ #endif
48
+
49
+ #ifndef EV_USE_STDEXCEPT
50
+ # define EV_USE_STDEXCEPT 1
51
+ #endif
52
+
53
+ #if EV_USE_STDEXCEPT
54
+ # include <stdexcept>
55
+ #endif
56
+
57
+ namespace ev {
58
+
59
+ typedef ev_tstamp tstamp;
60
+
61
+ enum {
62
+ UNDEF = EV_UNDEF,
63
+ NONE = EV_NONE,
64
+ READ = EV_READ,
65
+ WRITE = EV_WRITE,
66
+ TIMEOUT = EV_TIMEOUT,
67
+ PERIODIC = EV_PERIODIC,
68
+ SIGNAL = EV_SIGNAL,
69
+ CHILD = EV_CHILD,
70
+ STAT = EV_STAT,
71
+ IDLE = EV_IDLE,
72
+ CHECK = EV_CHECK,
73
+ PREPARE = EV_PREPARE,
74
+ FORK = EV_FORK,
75
+ ASYNC = EV_ASYNC,
76
+ EMBED = EV_EMBED,
77
+ ERROR = EV_ERROR,
78
+ };
79
+
80
+ enum
81
+ {
82
+ AUTO = EVFLAG_AUTO,
83
+ NOENV = EVFLAG_NOENV,
84
+ FORKCHECK = EVFLAG_FORKCHECK,
85
+ SELECT = EVBACKEND_SELECT,
86
+ POLL = EVBACKEND_POLL,
87
+ EPOLL = EVBACKEND_EPOLL,
88
+ KQUEUE = EVBACKEND_KQUEUE,
89
+ DEVPOLL = EVBACKEND_DEVPOLL,
90
+ PORT = EVBACKEND_PORT
91
+ };
92
+
93
+ enum
94
+ {
95
+ NONBLOCK = EVLOOP_NONBLOCK,
96
+ ONESHOT = EVLOOP_ONESHOT
97
+ };
98
+
99
+ enum how_t
100
+ {
101
+ ONE = EVUNLOOP_ONE,
102
+ ALL = EVUNLOOP_ALL
103
+ };
104
+
105
+ struct bad_loop
106
+ #if EV_USE_STDEXCEPT
107
+ : std::runtime_error
108
+ #endif
109
+ {
110
+ #if EV_USE_STDEXCEPT
111
+ bad_loop ()
112
+ : std::runtime_error ("libev event loop cannot be initialized, bad value of LIBEV_FLAGS?")
113
+ {
114
+ }
115
+ #endif
116
+ };
117
+
118
+ #ifdef EV_AX
119
+ # undef EV_AX
120
+ #endif
121
+
122
+ #ifdef EV_AX_
123
+ # undef EV_AX_
124
+ #endif
125
+
126
+ #if EV_MULTIPLICITY
127
+ # define EV_AX raw_loop
128
+ # define EV_AX_ raw_loop,
129
+ #else
130
+ # define EV_AX
131
+ # define EV_AX_
132
+ #endif
133
+
134
+ struct loop_ref
135
+ {
136
+ loop_ref (EV_P) throw ()
137
+ #if EV_MULTIPLICITY
138
+ : EV_AX (EV_A)
139
+ #endif
140
+ {
141
+ }
142
+
143
+ bool operator == (const loop_ref &other) const throw ()
144
+ {
145
+ #if EV_MULTIPLICITY
146
+ return EV_AX == other.EV_AX;
147
+ #else
148
+ return true;
149
+ #endif
150
+ }
151
+
152
+ bool operator != (const loop_ref &other) const throw ()
153
+ {
154
+ #if EV_MULTIPLICITY
155
+ return ! (*this == other);
156
+ #else
157
+ return false;
158
+ #endif
159
+ }
160
+
161
+ #if EV_MULTIPLICITY
162
+ bool operator == (struct ev_loop *other) const throw ()
163
+ {
164
+ return this->EV_AX == other;
165
+ }
166
+
167
+ bool operator != (struct ev_loop *other) const throw ()
168
+ {
169
+ return ! (*this == other);
170
+ }
171
+
172
+ bool operator == (const struct ev_loop *other) const throw ()
173
+ {
174
+ return this->EV_AX == other;
175
+ }
176
+
177
+ bool operator != (const struct ev_loop *other) const throw ()
178
+ {
179
+ return (*this == other);
180
+ }
181
+
182
+ operator struct ev_loop * () const throw ()
183
+ {
184
+ return EV_AX;
185
+ }
186
+
187
+ operator const struct ev_loop * () const throw ()
188
+ {
189
+ return EV_AX;
190
+ }
191
+
192
+ bool is_default () const throw ()
193
+ {
194
+ return EV_AX == ev_default_loop (0);
195
+ }
196
+ #endif
197
+
198
+ void loop (int flags = 0)
199
+ {
200
+ ev_loop (EV_AX_ flags);
201
+ }
202
+
203
+ void unloop (how_t how = ONE) throw ()
204
+ {
205
+ ev_unloop (EV_AX_ how);
206
+ }
207
+
208
+ void post_fork () throw ()
209
+ {
210
+ #if EV_MULTIPLICITY
211
+ ev_loop_fork (EV_AX);
212
+ #else
213
+ ev_default_fork ();
214
+ #endif
215
+ }
216
+
217
+ unsigned int count () const throw ()
218
+ {
219
+ return ev_loop_count (EV_AX);
220
+ }
221
+
222
+ unsigned int backend () const throw ()
223
+ {
224
+ return ev_backend (EV_AX);
225
+ }
226
+
227
+ tstamp now () const throw ()
228
+ {
229
+ return ev_now (EV_AX);
230
+ }
231
+
232
+ void ref () throw ()
233
+ {
234
+ ev_ref (EV_AX);
235
+ }
236
+
237
+ void unref () throw ()
238
+ {
239
+ ev_unref (EV_AX);
240
+ }
241
+
242
+ void set_io_collect_interval (tstamp interval) throw ()
243
+ {
244
+ ev_set_io_collect_interval (EV_AX_ interval);
245
+ }
246
+
247
+ void set_timeout_collect_interval (tstamp interval) throw ()
248
+ {
249
+ ev_set_timeout_collect_interval (EV_AX_ interval);
250
+ }
251
+
252
+ // function callback
253
+ void once (int fd, int events, tstamp timeout, void (*cb)(int, void *), void* arg = 0) throw ()
254
+ {
255
+ ev_once (EV_AX_ fd, events, timeout, cb, arg);
256
+ }
257
+
258
+ // method callback
259
+ template<class K, void (K::*method)(int)>
260
+ void once (int fd, int events, tstamp timeout, K *object) throw ()
261
+ {
262
+ once (fd, events, timeout, method_thunk<K, method>, object);
263
+ }
264
+
265
+ template<class K, void (K::*method)(int)>
266
+ static void method_thunk (int revents, void* arg)
267
+ {
268
+ K *obj = static_cast<K *>(arg);
269
+ (obj->*method) (revents);
270
+ }
271
+
272
+ // const method callback
273
+ template<class K, void (K::*method)(int) const>
274
+ void once (int fd, int events, tstamp timeout, const K *object) throw ()
275
+ {
276
+ once (fd, events, timeout, const_method_thunk<K, method>, object);
277
+ }
278
+
279
+ template<class K, void (K::*method)(int) const>
280
+ static void const_method_thunk (int revents, void* arg)
281
+ {
282
+ K *obj = static_cast<K *>(arg);
283
+ (obj->*method) (revents);
284
+ }
285
+
286
+ // simple method callback
287
+ template<class K, void (K::*method)()>
288
+ void once (int fd, int events, tstamp timeout, K *object) throw ()
289
+ {
290
+ once (fd, events, timeout, method_noargs_thunk<K, method>, object);
291
+ }
292
+
293
+ template<class K, void (K::*method)()>
294
+ static void method_noargs_thunk (int revents, void* arg)
295
+ {
296
+ K *obj = static_cast<K *>(arg);
297
+ (obj->*method) ();
298
+ }
299
+
300
+ // simpler function callback
301
+ template<void (*cb)(int)>
302
+ void once (int fd, int events, tstamp timeout) throw ()
303
+ {
304
+ once (fd, events, timeout, simpler_func_thunk<cb>);
305
+ }
306
+
307
+ template<void (*cb)(int)>
308
+ static void simpler_func_thunk (int revents, void* arg)
309
+ {
310
+ (*cb) (revents);
311
+ }
312
+
313
+ // simplest function callback
314
+ template<void (*cb)()>
315
+ void once (int fd, int events, tstamp timeout) throw ()
316
+ {
317
+ once (fd, events, timeout, simplest_func_thunk<cb>);
318
+ }
319
+
320
+ template<void (*cb)()>
321
+ static void simplest_func_thunk (int revents, void* arg)
322
+ {
323
+ (*cb) ();
324
+ }
325
+
326
+ void feed_fd_event (int fd, int revents) throw ()
327
+ {
328
+ ev_feed_fd_event (EV_AX_ fd, revents);
329
+ }
330
+
331
+ void feed_signal_event (int signum) throw ()
332
+ {
333
+ ev_feed_signal_event (EV_AX_ signum);
334
+ }
335
+
336
+ #if EV_MULTIPLICITY
337
+ struct ev_loop* EV_AX;
338
+ #endif
339
+
340
+ };
341
+
342
+ #if EV_MULTIPLICITY
343
+ struct dynamic_loop : loop_ref
344
+ {
345
+
346
+ dynamic_loop (unsigned int flags = AUTO) throw (bad_loop)
347
+ : loop_ref (ev_loop_new (flags))
348
+ {
349
+ if (!EV_AX)
350
+ throw bad_loop ();
351
+ }
352
+
353
+ ~dynamic_loop () throw ()
354
+ {
355
+ ev_loop_destroy (EV_AX);
356
+ EV_AX = 0;
357
+ }
358
+
359
+ private:
360
+
361
+ dynamic_loop (const dynamic_loop &);
362
+
363
+ dynamic_loop & operator= (const dynamic_loop &);
364
+
365
+ };
366
+ #endif
367
+
368
+ struct default_loop : loop_ref
369
+ {
370
+ default_loop (unsigned int flags = AUTO) throw (bad_loop)
371
+ #if EV_MULTIPLICITY
372
+ : loop_ref (ev_default_loop (flags))
373
+ #endif
374
+ {
375
+ if (
376
+ #if EV_MULTIPLICITY
377
+ !EV_AX
378
+ #else
379
+ !ev_default_loop (flags)
380
+ #endif
381
+ )
382
+ throw bad_loop ();
383
+ }
384
+
385
+ ~default_loop () throw ()
386
+ {
387
+ ev_default_destroy ();
388
+ }
389
+
390
+ private:
391
+ default_loop (const default_loop &);
392
+ default_loop &operator = (const default_loop &);
393
+ };
394
+
395
+ inline loop_ref get_default_loop () throw ()
396
+ {
397
+ #if EV_MULTIPLICITY
398
+ return ev_default_loop (0);
399
+ #else
400
+ return loop_ref ();
401
+ #endif
402
+ }
403
+
404
+ #undef EV_AX
405
+ #undef EV_AX_
406
+
407
+ #undef EV_PX
408
+ #undef EV_PX_
409
+ #if EV_MULTIPLICITY
410
+ # define EV_PX loop_ref EV_A
411
+ # define EV_PX_ loop_ref EV_A_
412
+ #else
413
+ # define EV_PX
414
+ # define EV_PX_
415
+ #endif
416
+
417
+ template<class ev_watcher, class watcher>
418
+ struct base : ev_watcher
419
+ {
420
+ #if EV_MULTIPLICITY
421
+ EV_PX;
422
+
423
+ void set (EV_PX) throw ()
424
+ {
425
+ this->EV_A = EV_A;
426
+ }
427
+ #endif
428
+
429
+ base (EV_PX) throw ()
430
+ #if EV_MULTIPLICITY
431
+ : EV_A (EV_A)
432
+ #endif
433
+ {
434
+ ev_init (this, 0);
435
+ }
436
+
437
+ void set_ (void *data, void (*cb)(EV_P_ ev_watcher *w, int revents)) throw ()
438
+ {
439
+ this->data = data;
440
+ ev_set_cb (static_cast<ev_watcher *>(this), cb);
441
+ }
442
+
443
+ // method callback
444
+ template<class K, void (K::*method)(watcher &w, int)>
445
+ void set (K *object) throw ()
446
+ {
447
+ set_ (object, method_thunk<K, method>);
448
+ }
449
+
450
+ template<class K, void (K::*method)(watcher &w, int)>
451
+ static void method_thunk (EV_P_ ev_watcher *w, int revents)
452
+ {
453
+ K *obj = static_cast<K *>(w->data);
454
+ (obj->*method) (*static_cast<watcher *>(w), revents);
455
+ }
456
+
457
+ // const method callback
458
+ template<class K, void (K::*method)(watcher &w, int) const>
459
+ void set (const K *object) throw ()
460
+ {
461
+ set_ (object, const_method_thunk<K, method>);
462
+ }
463
+
464
+ template<class K, void (K::*method)(watcher &w, int) const>
465
+ static void const_method_thunk (EV_P_ ev_watcher *w, int revents)
466
+ {
467
+ K *obj = static_cast<K *>(w->data);
468
+ (static_cast<K *>(w->data)->*method) (*static_cast<watcher *>(w), revents);
469
+ }
470
+
471
+ // function callback
472
+ template<void (*function)(watcher &w, int)>
473
+ void set (void *data = 0) throw ()
474
+ {
475
+ set_ (data, function_thunk<function>);
476
+ }
477
+
478
+ template<void (*function)(watcher &w, int)>
479
+ static void function_thunk (EV_P_ ev_watcher *w, int revents)
480
+ {
481
+ function (*static_cast<watcher *>(w), revents);
482
+ }
483
+
484
+ // simple callback
485
+ template<class K, void (K::*method)()>
486
+ void set (K *object) throw ()
487
+ {
488
+ set_ (object, method_noargs_thunk<K, method>);
489
+ }
490
+
491
+ template<class K, void (K::*method)()>
492
+ static void method_noargs_thunk (EV_P_ ev_watcher *w, int revents)
493
+ {
494
+ K *obj = static_cast<K *>(w->data);
495
+ (obj->*method) ();
496
+ }
497
+
498
+ void operator ()(int events = EV_UNDEF)
499
+ {
500
+ return ev_cb (static_cast<ev_watcher *>(this))
501
+ (static_cast<ev_watcher *>(this), events);
502
+ }
503
+
504
+ bool is_active () const throw ()
505
+ {
506
+ return ev_is_active (static_cast<const ev_watcher *>(this));
507
+ }
508
+
509
+ bool is_pending () const throw ()
510
+ {
511
+ return ev_is_pending (static_cast<const ev_watcher *>(this));
512
+ }
513
+
514
+ void feed_event (int revents) throw ()
515
+ {
516
+ ev_feed_event (EV_A_ static_cast<const ev_watcher *>(this), revents);
517
+ }
518
+ };
519
+
520
+ inline tstamp now () throw ()
521
+ {
522
+ return ev_time ();
523
+ }
524
+
525
+ inline void delay (tstamp interval) throw ()
526
+ {
527
+ ev_sleep (interval);
528
+ }
529
+
530
+ inline int version_major () throw ()
531
+ {
532
+ return ev_version_major ();
533
+ }
534
+
535
+ inline int version_minor () throw ()
536
+ {
537
+ return ev_version_minor ();
538
+ }
539
+
540
+ inline unsigned int supported_backends () throw ()
541
+ {
542
+ return ev_supported_backends ();
543
+ }
544
+
545
+ inline unsigned int recommended_backends () throw ()
546
+ {
547
+ return ev_recommended_backends ();
548
+ }
549
+
550
+ inline unsigned int embeddable_backends () throw ()
551
+ {
552
+ return ev_embeddable_backends ();
553
+ }
554
+
555
+ inline void set_allocator (void *(*cb)(void *ptr, long size)) throw ()
556
+ {
557
+ ev_set_allocator (cb);
558
+ }
559
+
560
+ inline void set_syserr_cb (void (*cb)(const char *msg)) throw ()
561
+ {
562
+ ev_set_syserr_cb (cb);
563
+ }
564
+
565
+ #if EV_MULTIPLICITY
566
+ #define EV_CONSTRUCT(cppstem,cstem) \
567
+ (EV_PX = get_default_loop ()) throw () \
568
+ : base<ev_ ## cstem, cppstem> (EV_A) \
569
+ { \
570
+ }
571
+ #else
572
+ #define EV_CONSTRUCT(cppstem,cstem) \
573
+ () throw () \
574
+ { \
575
+ }
576
+ #endif
577
+
578
+ /* using a template here would require quite a bit more lines,
579
+ * so a macro solution was chosen */
580
+ #define EV_BEGIN_WATCHER(cppstem,cstem) \
581
+ \
582
+ struct cppstem : base<ev_ ## cstem, cppstem> \
583
+ { \
584
+ void start () throw () \
585
+ { \
586
+ ev_ ## cstem ## _start (EV_A_ static_cast<ev_ ## cstem *>(this)); \
587
+ } \
588
+ \
589
+ void stop () throw () \
590
+ { \
591
+ ev_ ## cstem ## _stop (EV_A_ static_cast<ev_ ## cstem *>(this)); \
592
+ } \
593
+ \
594
+ cppstem EV_CONSTRUCT(cppstem,cstem) \
595
+ \
596
+ ~cppstem () throw () \
597
+ { \
598
+ stop (); \
599
+ } \
600
+ \
601
+ using base<ev_ ## cstem, cppstem>::set; \
602
+ \
603
+ private: \
604
+ \
605
+ cppstem (const cppstem &o); \
606
+ \
607
+ cppstem &operator =(const cppstem &o); \
608
+ \
609
+ public:
610
+
611
+ #define EV_END_WATCHER(cppstem,cstem) \
612
+ };
613
+
614
+ EV_BEGIN_WATCHER (io, io)
615
+ void set (int fd, int events) throw ()
616
+ {
617
+ int active = is_active ();
618
+ if (active) stop ();
619
+ ev_io_set (static_cast<ev_io *>(this), fd, events);
620
+ if (active) start ();
621
+ }
622
+
623
+ void set (int events) throw ()
624
+ {
625
+ int active = is_active ();
626
+ if (active) stop ();
627
+ ev_io_set (static_cast<ev_io *>(this), fd, events);
628
+ if (active) start ();
629
+ }
630
+
631
+ void start (int fd, int events) throw ()
632
+ {
633
+ set (fd, events);
634
+ start ();
635
+ }
636
+ EV_END_WATCHER (io, io)
637
+
638
+ EV_BEGIN_WATCHER (timer, timer)
639
+ void set (ev_tstamp after, ev_tstamp repeat = 0.) throw ()
640
+ {
641
+ int active = is_active ();
642
+ if (active) stop ();
643
+ ev_timer_set (static_cast<ev_timer *>(this), after, repeat);
644
+ if (active) start ();
645
+ }
646
+
647
+ void start (ev_tstamp after, ev_tstamp repeat = 0.) throw ()
648
+ {
649
+ set (after, repeat);
650
+ start ();
651
+ }
652
+
653
+ void again () throw ()
654
+ {
655
+ ev_timer_again (EV_A_ static_cast<ev_timer *>(this));
656
+ }
657
+ EV_END_WATCHER (timer, timer)
658
+
659
+ #if EV_PERIODIC_ENABLE
660
+ EV_BEGIN_WATCHER (periodic, periodic)
661
+ void set (ev_tstamp at, ev_tstamp interval = 0.) throw ()
662
+ {
663
+ int active = is_active ();
664
+ if (active) stop ();
665
+ ev_periodic_set (static_cast<ev_periodic *>(this), at, interval, 0);
666
+ if (active) start ();
667
+ }
668
+
669
+ void start (ev_tstamp at, ev_tstamp interval = 0.) throw ()
670
+ {
671
+ set (at, interval);
672
+ start ();
673
+ }
674
+
675
+ void again () throw ()
676
+ {
677
+ ev_periodic_again (EV_A_ static_cast<ev_periodic *>(this));
678
+ }
679
+ EV_END_WATCHER (periodic, periodic)
680
+ #endif
681
+
682
+ EV_BEGIN_WATCHER (sig, signal)
683
+ void set (int signum) throw ()
684
+ {
685
+ int active = is_active ();
686
+ if (active) stop ();
687
+ ev_signal_set (static_cast<ev_signal *>(this), signum);
688
+ if (active) start ();
689
+ }
690
+
691
+ void start (int signum) throw ()
692
+ {
693
+ set (signum);
694
+ start ();
695
+ }
696
+ EV_END_WATCHER (sig, signal)
697
+
698
+ EV_BEGIN_WATCHER (child, child)
699
+ void set (int pid, int trace = 0) throw ()
700
+ {
701
+ int active = is_active ();
702
+ if (active) stop ();
703
+ ev_child_set (static_cast<ev_child *>(this), pid, trace);
704
+ if (active) start ();
705
+ }
706
+
707
+ void start (int pid, int trace = 0) throw ()
708
+ {
709
+ set (pid, trace);
710
+ start ();
711
+ }
712
+ EV_END_WATCHER (child, child)
713
+
714
+ #if EV_STAT_ENABLE
715
+ EV_BEGIN_WATCHER (stat, stat)
716
+ void set (const char *path, ev_tstamp interval = 0.) throw ()
717
+ {
718
+ int active = is_active ();
719
+ if (active) stop ();
720
+ ev_stat_set (static_cast<ev_stat *>(this), path, interval);
721
+ if (active) start ();
722
+ }
723
+
724
+ void start (const char *path, ev_tstamp interval = 0.) throw ()
725
+ {
726
+ stop ();
727
+ set (path, interval);
728
+ start ();
729
+ }
730
+
731
+ void update () throw ()
732
+ {
733
+ ev_stat_stat (EV_A_ static_cast<ev_stat *>(this));
734
+ }
735
+ EV_END_WATCHER (stat, stat)
736
+ #endif
737
+
738
+ EV_BEGIN_WATCHER (idle, idle)
739
+ void set () throw () { }
740
+ EV_END_WATCHER (idle, idle)
741
+
742
+ EV_BEGIN_WATCHER (prepare, prepare)
743
+ void set () throw () { }
744
+ EV_END_WATCHER (prepare, prepare)
745
+
746
+ EV_BEGIN_WATCHER (check, check)
747
+ void set () throw () { }
748
+ EV_END_WATCHER (check, check)
749
+
750
+ #if EV_EMBED_ENABLE
751
+ EV_BEGIN_WATCHER (embed, embed)
752
+ void set (struct ev_loop *embedded_loop) throw ()
753
+ {
754
+ int active = is_active ();
755
+ if (active) stop ();
756
+ ev_embed_set (static_cast<ev_embed *>(this), embedded_loop);
757
+ if (active) start ();
758
+ }
759
+
760
+ void start (struct ev_loop *embedded_loop) throw ()
761
+ {
762
+ set (embedded_loop);
763
+ start ();
764
+ }
765
+
766
+ void sweep ()
767
+ {
768
+ ev_embed_sweep (EV_A_ static_cast<ev_embed *>(this));
769
+ }
770
+ EV_END_WATCHER (embed, embed)
771
+ #endif
772
+
773
+ #if EV_FORK_ENABLE
774
+ EV_BEGIN_WATCHER (fork, fork)
775
+ void set () throw () { }
776
+ EV_END_WATCHER (fork, fork)
777
+ #endif
778
+
779
+ #if EV_ASYNC_ENABLE
780
+ EV_BEGIN_WATCHER (async, async)
781
+ void set () throw () { }
782
+
783
+ void send () throw ()
784
+ {
785
+ ev_async_send (EV_A_ static_cast<ev_async *>(this));
786
+ }
787
+
788
+ bool async_pending () throw ()
789
+ {
790
+ return ev_async_pending (static_cast<ev_async *>(this));
791
+ }
792
+ EV_END_WATCHER (async, async)
793
+ #endif
794
+
795
+ #undef EV_PX
796
+ #undef EV_PX_
797
+ #undef EV_CONSTRUCT
798
+ #undef EV_BEGIN_WATCHER
799
+ #undef EV_END_WATCHER
800
+ }
801
+
802
+ #endif
803
+