mt-libuv 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +24 -0
  8. data/README.md +195 -0
  9. data/Rakefile +31 -0
  10. data/ext/README.md +6 -0
  11. data/ext/Rakefile +28 -0
  12. data/lib/mt-libuv/async.rb +51 -0
  13. data/lib/mt-libuv/check.rb +59 -0
  14. data/lib/mt-libuv/coroutines.rb +79 -0
  15. data/lib/mt-libuv/dns.rb +98 -0
  16. data/lib/mt-libuv/error.rb +88 -0
  17. data/lib/mt-libuv/ext/ext.rb +322 -0
  18. data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
  19. data/lib/mt-libuv/ext/platform/unix.rb +69 -0
  20. data/lib/mt-libuv/ext/platform/windows.rb +83 -0
  21. data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
  22. data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
  23. data/lib/mt-libuv/ext/tasks/win.rb +29 -0
  24. data/lib/mt-libuv/ext/tasks.rb +27 -0
  25. data/lib/mt-libuv/ext/types.rb +253 -0
  26. data/lib/mt-libuv/fiber_pool.rb +83 -0
  27. data/lib/mt-libuv/file.rb +309 -0
  28. data/lib/mt-libuv/filesystem.rb +263 -0
  29. data/lib/mt-libuv/fs_event.rb +37 -0
  30. data/lib/mt-libuv/handle.rb +108 -0
  31. data/lib/mt-libuv/idle.rb +59 -0
  32. data/lib/mt-libuv/mixins/accessors.rb +41 -0
  33. data/lib/mt-libuv/mixins/assertions.rb +25 -0
  34. data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
  35. data/lib/mt-libuv/mixins/listener.rb +69 -0
  36. data/lib/mt-libuv/mixins/net.rb +42 -0
  37. data/lib/mt-libuv/mixins/resource.rb +30 -0
  38. data/lib/mt-libuv/mixins/stream.rb +276 -0
  39. data/lib/mt-libuv/pipe.rb +217 -0
  40. data/lib/mt-libuv/prepare.rb +59 -0
  41. data/lib/mt-libuv/q.rb +475 -0
  42. data/lib/mt-libuv/reactor.rb +567 -0
  43. data/lib/mt-libuv/signal.rb +62 -0
  44. data/lib/mt-libuv/spawn.rb +113 -0
  45. data/lib/mt-libuv/tcp.rb +465 -0
  46. data/lib/mt-libuv/timer.rb +107 -0
  47. data/lib/mt-libuv/tty.rb +42 -0
  48. data/lib/mt-libuv/udp.rb +302 -0
  49. data/lib/mt-libuv/version.rb +5 -0
  50. data/lib/mt-libuv/work.rb +86 -0
  51. data/lib/mt-libuv.rb +80 -0
  52. data/mt-libuv.gemspec +62 -0
  53. data/spec/async_spec.rb +67 -0
  54. data/spec/coroutines_spec.rb +121 -0
  55. data/spec/cpu_spec.rb +10 -0
  56. data/spec/defer_spec.rb +906 -0
  57. data/spec/dns_spec.rb +110 -0
  58. data/spec/dsl_spec.rb +43 -0
  59. data/spec/filesystem_spec.rb +270 -0
  60. data/spec/idle_spec.rb +44 -0
  61. data/spec/pipe_spec.rb +151 -0
  62. data/spec/spawn_spec.rb +119 -0
  63. data/spec/tcp_spec.rb +272 -0
  64. data/spec/test.sh +4 -0
  65. data/spec/test_fail.sh +3 -0
  66. data/spec/test_read.sh +3 -0
  67. data/spec/timer_spec.rb +14 -0
  68. data/spec/udp_spec.rb +73 -0
  69. data/spec/zen_spec.rb +34 -0
  70. metadata +196 -0
@@ -0,0 +1,567 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+
5
+ module MTLibuv
6
+ class Reactor
7
+ include Resource, Assertions
8
+ extend Accessors
9
+
10
+
11
+ LIBUV_MIN_POOL = ENV['LIBUV_MIN_POOL'] || 8
12
+ LIBUV_MAX_POOL = ENV['LIBUV_MAX_POOL'] || 40
13
+ LIBUV_MAX_QUEUE = ENV['LIBUV_MAX_QUEUE'] || 50000
14
+ THREAD_POOL = ::Concurrent::ThreadPoolExecutor.new(
15
+ min_threads: LIBUV_MIN_POOL,
16
+ max_threads: LIBUV_MAX_POOL,
17
+ max_queue: LIBUV_MAX_QUEUE
18
+ )
19
+ CRITICAL = ::Mutex.new
20
+
21
+
22
+ module ClassMethods
23
+ # Get default reactor
24
+ #
25
+ # @return [::MTLibuv::Reactor]
26
+ def default
27
+ return @default unless @default.nil?
28
+ CRITICAL.synchronize {
29
+ return @default ||= create(::MTLibuv::Ext.default_loop)
30
+ }
31
+ end
32
+
33
+ # Create new MTLibuv reactor
34
+ #
35
+ # @return [::MTLibuv::Reactor]
36
+ def new(&blk)
37
+ memory = ::MTLibuv::Ext::LIBC.malloc(::MTLibuv::Ext.loop_size)
38
+ ::MTLibuv::Ext.loop_init(memory)
39
+
40
+ thread = create(memory)
41
+ if block_given?
42
+ ::Thread.new do
43
+ thread.run &blk
44
+ end
45
+ end
46
+ thread
47
+ end
48
+
49
+ # Build a Ruby MTLibuv reactor from an existing reactor pointer
50
+ #
51
+ # @return [::MTLibuv::Reactor]
52
+ def create(pointer)
53
+ allocate.tap { |i| i.send(:initialize, pointer) }
54
+ end
55
+
56
+ # Checks for the existence of a reactor on the current thread
57
+ #
58
+ # @return [::MTLibuv::Reactor | nil]
59
+ def current
60
+ Thread.current.thread_variable_get(:reactor)
61
+ end
62
+ end
63
+ extend ClassMethods
64
+
65
+
66
+ # Initialize a reactor using an FFI::Pointer to a libuv reactor
67
+ def initialize(pointer) # :notnew:
68
+ @pointer = pointer
69
+ @reactor = self
70
+ @run_count = 0
71
+ @ref_count = 0
72
+ @fiber_pool = FiberPool.new(self)
73
+
74
+ # Create an async call for scheduling work from other threads
75
+ @run_queue = Queue.new
76
+ @process_queue = @reactor.async { process_queue_cb }
77
+ @process_queue.unref
78
+
79
+ # Create a next tick timer
80
+ @next_tick = @reactor.timer { next_tick_cb }
81
+ @next_tick.unref
82
+
83
+ # Create an async call for ending the reactor
84
+ @stop_reactor = @reactor.async { stop_cb }
85
+ @stop_reactor.unref
86
+
87
+ # MTLibuv can prevent the application shutting down once the main thread has ended
88
+ # The addition of a prepare function prevents this from happening.
89
+ @reactor_prep = prepare {}
90
+ @reactor_prep.unref
91
+ @reactor_prep.start
92
+
93
+ # LibUV ingnores program interrupt by default.
94
+ # We provide normal behaviour and allow this to be overriden
95
+ @on_signal = []
96
+ sig_callback = proc { signal_cb }
97
+ self.signal(:INT, &sig_callback).unref
98
+ self.signal(:HUP, &sig_callback).unref
99
+ self.signal(:TERM, &sig_callback).unref
100
+
101
+ # Notify of errors
102
+ @throw_on_exit = nil
103
+ @reactor_notify_default = @reactor_notify = proc { |error|
104
+ @throw_on_exit = error
105
+ }
106
+ @fiber_pool.on_error &@reactor_notify
107
+ end
108
+
109
+ attr_reader :run_count, :fiber_pool
110
+
111
+
112
+ protected
113
+
114
+
115
+ def stop_cb
116
+ return unless @reactor_running
117
+ Thread.current.thread_variable_set(:reactor, nil)
118
+ @reactor_running = false
119
+
120
+ ::MTLibuv::Ext.stop(@pointer)
121
+ end
122
+
123
+ def signal_cb
124
+ if @on_signal.empty?
125
+ stop_cb
126
+ else
127
+ @on_signal.each(&:call)
128
+ end
129
+ end
130
+
131
+ def next_tick_cb
132
+ @next_tick_scheduled = false
133
+ @next_tick.unref
134
+ process_queue_cb
135
+ end
136
+
137
+ def process_queue_cb
138
+ # ensure we only execute what was required for this tick
139
+ length = @run_queue.length
140
+ update_time
141
+ length.times do
142
+ # This allows any item to pause its execution without effecting this loop
143
+ @fiber_pool.exec { process_item }
144
+ end
145
+ end
146
+
147
+ def process_item
148
+ begin
149
+ run = @run_queue.pop true # pop non-block
150
+ run.call
151
+ rescue Exception => e
152
+ @reactor.log e, 'performing next tick callback'
153
+ end
154
+ end
155
+
156
+
157
+ public
158
+
159
+
160
+ # Overwrite as errors in jRuby can literally hang VM when inspecting
161
+ # as many many classes will reference this class
162
+ def inspect
163
+ "#<#{self.class}:0x#{self.__id__.to_s(16)} NT=#{@run_queue.length}>"
164
+ end
165
+
166
+
167
+ def handle; @pointer; end
168
+
169
+ # Run the actual event reactor. This method will block until the reactor is stopped.
170
+ #
171
+ # @param run_type [:UV_RUN_DEFAULT, :UV_RUN_ONCE, :UV_RUN_NOWAIT]
172
+ # @yieldparam promise [::MTLibuv::Q::Promise] Yields a promise that can be used for logging unhandled
173
+ # exceptions on the reactor.
174
+ def run(run_type = :UV_RUN_DEFAULT)
175
+ if not @reactor_running
176
+ begin
177
+ @reactor_running = true
178
+ raise 'only one reactor allowed per-thread' if Thread.current.thread_variable_get(:reactor)
179
+
180
+ Thread.current.thread_variable_set(:reactor, @reactor)
181
+ @throw_on_exit = nil
182
+ update_time
183
+ @fiber_pool.reset
184
+ @fiber_pool.exec { yield @reactor } if block_given?
185
+ @run_count += 1
186
+ ::MTLibuv::Ext.run(@pointer, run_type) # This is blocking
187
+ ensure
188
+ Thread.current.thread_variable_set(:reactor, nil)
189
+ @reactor_running = false
190
+ @run_queue.clear
191
+ end
192
+
193
+ # Raise the last unhandled error to occur on the reactor thread
194
+ raise @throw_on_exit if @throw_on_exit
195
+
196
+ elsif block_given?
197
+ if reactor_thread?
198
+ update_time
199
+ yield @reactor
200
+ else
201
+ raise 'reactor already running on another thread'
202
+ end
203
+ end
204
+
205
+ @reactor
206
+ end
207
+
208
+ # Execute the provided block of code in a fiber from the pool
209
+ def exec
210
+ @fiber_pool.exec { yield }
211
+ end
212
+
213
+ # Prevents the reactor loop from stopping
214
+ def ref
215
+ if reactor_thread? && reactor_running?
216
+ @process_queue.ref if @ref_count == 0
217
+ @ref_count += 1
218
+ end
219
+ end
220
+
221
+ # Allows the reactor loop to stop
222
+ def unref
223
+ if reactor_thread? && reactor_running? && @ref_count > 0
224
+ @ref_count -= 1
225
+ @process_queue.unref if @ref_count == 0
226
+ end
227
+ end
228
+
229
+ # Return the number of active handles in the event loop
230
+ def active_handles
231
+ uvloop = Ext::UvLoop.new @pointer
232
+ uvloop[:active_handles]
233
+ end
234
+
235
+
236
+ # Provides a promise notifier for receiving un-handled exceptions
237
+ #
238
+ # @return [::MTLibuv::Q::Promise]
239
+ def notifier
240
+ @reactor_notify = if block_given?
241
+ Proc.new
242
+ else
243
+ @reactor_notify_default
244
+ end
245
+ self
246
+ end
247
+
248
+ # Creates a deferred result object for where the result of an operation may only be returned
249
+ # at some point in the future or is being processed on a different thread (thread safe)
250
+ #
251
+ # @return [::MTLibuv::Q::Deferred]
252
+ def defer
253
+ Q.defer(@reactor)
254
+ end
255
+
256
+ # Combines multiple promises into a single promise that is resolved when all of the input
257
+ # promises are resolved. (thread safe)
258
+ #
259
+ # @param *promises [::MTLibuv::Q::Promise] a number of promises that will be combined into a single promise
260
+ # @return [::MTLibuv::Q::Promise] Returns a single promise that will be resolved with an array of values,
261
+ # each value corresponding to the promise at the same index in the `promises` array. If any of
262
+ # the promises is resolved with a rejection, this resulting promise will be resolved with the
263
+ # same rejection.
264
+ def all(*promises)
265
+ Q.all(@reactor, *promises)
266
+ end
267
+
268
+ #
269
+ # Combines multiple promises into a single promise that is resolved when any of the input
270
+ # promises are resolved.
271
+ #
272
+ # @param *promises [::MTLibuv::Q::Promise] a number of promises that will be combined into a single promise
273
+ # @return [::MTLibuv::Q::Promise] Returns a single promise
274
+ def any(*promises)
275
+ Q.any(@reactor, *promises)
276
+ end
277
+
278
+ #
279
+ # Combines multiple promises into a single promise that is resolved when all of the input
280
+ # promises are resolved or rejected.
281
+ #
282
+ # @param *promises [::MTLibuv::Q::Promise] a number of promises that will be combined into a single promise
283
+ # @return [::MTLibuv::Q::Promise] Returns a single promise that will be resolved with an array of values,
284
+ # each [result, wasResolved] value pair corresponding to a at the same index in the `promises` array.
285
+ def finally(*promises)
286
+ Q.finally(@reactor, *promises)
287
+ end
288
+
289
+ # Creates a promise that is resolved as rejected with the specified reason. This api should be
290
+ # used to forward rejection in a chain of promises. If you are dealing with the last promise in
291
+ # a promise chain, you don't need to worry about it.
292
+ def reject(reason)
293
+ Q.reject(@reactor, reason)
294
+ end
295
+
296
+ # forces reactor time update, useful for getting more granular times
297
+ #
298
+ # @return nil
299
+ def update_time
300
+ ::MTLibuv::Ext.update_time(@pointer)
301
+ self
302
+ end
303
+
304
+ # Get current time in milliseconds
305
+ #
306
+ # @return [Integer]
307
+ def now
308
+ ::MTLibuv::Ext.now(@pointer)
309
+ end
310
+
311
+ # Lookup an error code and return is as an error object
312
+ #
313
+ # @param err [Integer] The error code to look up.
314
+ # @return [::MTLibuv::Error]
315
+ def lookup_error(err)
316
+ name = ::MTLibuv::Ext.err_name(err)
317
+
318
+ if name
319
+ msg = ::MTLibuv::Ext.strerror(err)
320
+ ::MTLibuv::Error.const_get(name.to_sym).new("#{msg}, #{name}:#{err}")
321
+ else
322
+ # We want a back-trace in this case
323
+ raise "error lookup failed for code #{err}"
324
+ end
325
+ rescue Exception => e
326
+ @reactor.log e, 'performing error lookup'
327
+ e
328
+ end
329
+
330
+ def sleep(msecs)
331
+ fiber = Fiber.current
332
+ time = timer {
333
+ time.close
334
+ fiber.resume
335
+ }.start(msecs)
336
+ Fiber.yield
337
+ end
338
+
339
+ # Get a new TCP instance
340
+ #
341
+ # @return [::MTLibuv::TCP]
342
+ def tcp(**opts, &callback)
343
+ TCP.new(@reactor, progress: callback, **opts)
344
+ end
345
+
346
+ # Get a new UDP instance
347
+ #
348
+ # @return [::MTLibuv::UDP]
349
+ def udp(**opts, &callback)
350
+ UDP.new(@reactor, progress: callback, **opts)
351
+ end
352
+
353
+ # Get a new TTY instance
354
+ #
355
+ # @param fileno [Integer] Integer file descriptor of a tty device
356
+ # @param readable [true, false] Boolean indicating if TTY is readable
357
+ # @return [::MTLibuv::TTY]
358
+ def tty(fileno, readable = false)
359
+ assert_type(Integer, fileno, "io#fileno must return an integer file descriptor, #{fileno.inspect} given")
360
+
361
+ TTY.new(@reactor, fileno, readable)
362
+ end
363
+
364
+ # Get a new Pipe instance
365
+ #
366
+ # @param ipc [true, false] indicate if a handle will be used for ipc, useful for sharing tcp socket between processes
367
+ # @return [::MTLibuv::Pipe]
368
+ def pipe(ipc = false)
369
+ Pipe.new(@reactor, ipc)
370
+ end
371
+
372
+ # Get a new timer instance
373
+ #
374
+ # @param callback [Proc] the callback to be called on timer trigger
375
+ # @return [::MTLibuv::Timer]
376
+ def timer
377
+ handle = Timer.new(@reactor)
378
+ handle.progress &Proc.new if block_given?
379
+ handle
380
+ end
381
+
382
+ # Get a new Prepare handle
383
+ #
384
+ # @return [::MTLibuv::Prepare]
385
+ def prepare
386
+ handle = Prepare.new(@reactor)
387
+ handle.progress &Proc.new if block_given?
388
+ handle
389
+ end
390
+
391
+ # Get a new Check handle
392
+ #
393
+ # @return [::MTLibuv::Check]
394
+ def check
395
+ handle = Check.new(@reactor)
396
+ handle.progress &Proc.new if block_given?
397
+ handle
398
+ end
399
+
400
+ # Get a new Idle handle
401
+ #
402
+ # @param callback [Proc] the callback to be called on idle trigger
403
+ # @return [::MTLibuv::Idle]
404
+ def idle
405
+ handle = Idle.new(@reactor)
406
+ handle.progress &Proc.new if block_given?
407
+ handle
408
+ end
409
+
410
+ # Get a new Async handle
411
+ #
412
+ # @return [::MTLibuv::Async]
413
+ def async
414
+ handle = Async.new(@reactor)
415
+ handle.progress &Proc.new if block_given?
416
+ handle
417
+ end
418
+
419
+ # Get a new signal handler
420
+ #
421
+ # @return [::MTLibuv::Signal]
422
+ def signal(signum = nil)
423
+ handle = Signal.new(@reactor)
424
+ handle.progress &Proc.new if block_given?
425
+ handle.start(signum) if signum
426
+ handle
427
+ end
428
+
429
+ # Allows user defined behaviour when sig int is received
430
+ def on_program_interrupt(&callback)
431
+ @on_signal << callback
432
+ self
433
+ end
434
+
435
+ # Queue some work for processing in the libuv thread pool
436
+ #
437
+ # @param callback [Proc] the callback to be called in the thread pool
438
+ # @return [::MTLibuv::Work]
439
+ # @raise [ArgumentError] if block is not given
440
+ def work
441
+ ref
442
+ d = defer
443
+ THREAD_POOL.post do
444
+ begin
445
+ d.resolve(yield)
446
+ rescue Exception => e
447
+ d.reject(e)
448
+ end
449
+ end
450
+ promise = d.promise
451
+ promise.finally { unref }
452
+ promise
453
+ end
454
+
455
+ # Lookup a hostname
456
+ #
457
+ # @param hostname [String] the domain name to lookup
458
+ # @param port [Integer, String] the service being connected too
459
+ # @param callback [Proc] the callback to be called on success
460
+ # @return [::MTLibuv::Dns]
461
+ def lookup(hostname, hint = :IPv4, port = 9, wait: true)
462
+ dns = Dns.new(@reactor, hostname, port, hint, wait: wait) # Work is a promise object
463
+ if wait
464
+ dns.results
465
+ else
466
+ dns.then &Proc.new if block_given?
467
+ dns
468
+ end
469
+ end
470
+
471
+ # Get a new FSEvent instance
472
+ #
473
+ # @param path [String] the path to the file or folder for watching
474
+ # @return [::MTLibuv::FSEvent]
475
+ # @raise [ArgumentError] if path is not a string
476
+ def fs_event(path)
477
+ assert_type(String, path)
478
+ FSEvent.new(@reactor, path)
479
+ end
480
+
481
+ # Opens a file and returns an object that can be used to manipulate it
482
+ #
483
+ # @param path [String] the path to the file or folder for watching
484
+ # @param flags [Integer] see ruby File::Constants
485
+ # @param mode [Integer]
486
+ # @return [::MTLibuv::File]
487
+ def file(path, flags = 0, mode: 0, **opts, &blk)
488
+ assert_type(String, path, "path must be a String")
489
+ assert_type(Integer, flags, "flags must be an Integer")
490
+ assert_type(Integer, mode, "mode must be an Integer")
491
+ File.new(@reactor, path, flags, mode: mode, **opts, &blk)
492
+ end
493
+
494
+ # Returns an object for manipulating the filesystem
495
+ #
496
+ # @return [::MTLibuv::Filesystem]
497
+ def filesystem
498
+ Filesystem.new(@reactor)
499
+ end
500
+
501
+ def spawn(cmd, **args)
502
+ Spawn.new(@reactor, cmd, **args)
503
+ end
504
+
505
+ # Schedule some work to be processed on the event reactor as soon as possible (thread safe)
506
+ #
507
+ # @yield the callback to be called on the reactor thread
508
+ def schedule
509
+ if reactor_thread?
510
+ yield
511
+ else
512
+ @run_queue << Proc.new
513
+ @process_queue.call
514
+ end
515
+ self
516
+ end
517
+
518
+ # Queue some work to be processed in the next iteration of the event reactor (thread safe)
519
+ #
520
+ # @param callback [Proc] the callback to be called on the reactor thread
521
+ def next_tick(&block)
522
+ @run_queue << block
523
+ if reactor_thread?
524
+ # Create a next tick timer
525
+ if not @next_tick_scheduled
526
+ @next_tick.start(0)
527
+ @next_tick_scheduled = true
528
+ @next_tick.ref
529
+ end
530
+ else
531
+ @process_queue.call
532
+ end
533
+
534
+ self
535
+ end
536
+
537
+ # Notifies the reactor there was an event that should be logged
538
+ #
539
+ # @param error [Exception] the error
540
+ # @param msg [String|nil] optional context on the error
541
+ # @param trace [Array<String>] optional additional trace of caller if async
542
+ def log(error, msg = nil, trace = nil)
543
+ @reactor_notify.call(error, msg, trace)
544
+ end
545
+
546
+ # Closes handles opened by the reactor class and completes the current reactor iteration (thread safe)
547
+ def stop
548
+ return unless @reactor_running
549
+ @stop_reactor.call
550
+ end
551
+
552
+ # True if the calling thread is the same thread as the reactor.
553
+ #
554
+ # @return [Boolean]
555
+ def reactor_thread?
556
+ self == Thread.current.thread_variable_get(:reactor)
557
+ end
558
+
559
+ # Tells you whether the MTLibuv reactor reactor is currently running.
560
+ #
561
+ # @return [Boolean]
562
+ def reactor_running?
563
+ @reactor_running
564
+ end
565
+ alias_method :running?, :reactor_running?
566
+ end
567
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTLibuv
4
+ class Signal < Handle
5
+
6
+
7
+ define_callback function: :on_sig, params: [:pointer, :int]
8
+
9
+
10
+ SIGNALS = {
11
+ :HUP => 1,
12
+ :SIGHUP => 1,
13
+ :INT => 2,
14
+ :SIGINT => 2,
15
+ :TERM => 15,
16
+ :SIGTERM => 15,
17
+ :BREAK => 21,
18
+ :SIGBREAK => 21,
19
+ :WINCH => 28,
20
+ :SIGWINCH => 28
21
+ }
22
+
23
+
24
+ # @param reactor [::MTLibuv::Reactor] reactor this signal handler will be associated
25
+ # @param callback [Proc] callback to be called when the signal is triggered
26
+ def initialize(reactor)
27
+ @reactor = reactor
28
+
29
+ signal_ptr = ::MTLibuv::Ext.allocate_handle_signal
30
+ error = check_result(::MTLibuv::Ext.signal_init(reactor.handle, signal_ptr))
31
+
32
+ super(signal_ptr, error)
33
+ end
34
+
35
+ # Enables the signal handler.
36
+ def start(signal)
37
+ return if @closed
38
+ signal = SIGNALS[signal] if signal.is_a? Symbol
39
+ error = check_result ::MTLibuv::Ext.signal_start(handle, callback(:on_sig), signal)
40
+ reject(error) if error
41
+ self
42
+ end
43
+
44
+ # Disables the signal handler.
45
+ def stop
46
+ return if @closed
47
+ error = check_result ::MTLibuv::Ext.signal_stop(handle)
48
+ reject(error) if error
49
+ self
50
+ end
51
+
52
+
53
+ private
54
+
55
+
56
+ def on_sig(handle, signal)
57
+ @reactor.exec do
58
+ defer.notify(signal) # notify of a call
59
+ end
60
+ end
61
+ end
62
+ end