Class: FFI::Libfuse::Ackbar

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi/libfuse/ackbar.rb

Overview

Its a trap!

Implements the self-pipe trick for handling signals in multi-threaded ruby programs

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(traps, force: false, signal: Signal) ⇒ Ackbar

Returns a new instance of Ackbar.

Parameters:

  • traps (Hash<Symbol|String|Integer,String|Proc>)

    Map of signal or signo to signal handler as per Signal.trap

  • force (Boolean) (defaults to: false)

    Unless set, only installs traps currently set to 'DEFAULT'.



35
36
37
38
39
40
41
# File 'lib/ffi/libfuse/ackbar.rb', line 35

def initialize(traps, force: false, signal: Signal)
  @signal = signal
  @traps = traps.transform_keys { |k| signame(k) }
  @pr, @pw = ::IO.pipe
  @monitor = nil
  @restore = @traps.to_h { |sig, handler| [sig, trap(sig, handler, force: force)] }
end

Instance Attribute Details

#prIO (readonly)

Read side of the self-pipe

Returns:

  • (IO)

    for use with IO.select only

See Also:



16
17
18
# File 'lib/ffi/libfuse/ackbar.rb', line 16

def pr
  @pr
end

Class Method Details

.trap(traps, force: false) {|signals| ... } ⇒ Object

Run a block with the given traps, restoring the original traps on completion

Parameters:

  • traps (Hash)

    map of signal to handler as per Signal.trap

Yield Parameters:

Returns:

  • (Object)

    the result of the block



23
24
25
26
27
28
# File 'lib/ffi/libfuse/ackbar.rb', line 23

def trap(traps, force: false)
  signals = new(traps, force: force)
  yield signals
ensure
  signals&.restore
end

Instance Method Details

#monitor(name: 'SignalMonitor') ⇒ Thread

Start a thread to monitor for signals optionally yields between signals

Parameters:

  • name (String) (defaults to: 'SignalMonitor')

    The name of the monitor thread

Yield Returns:

  • (Integer)

    timeout in seconds or nil to wait until signal or #restore

Returns:

  • (Thread)

    the monitor thread



72
73
74
75
76
77
78
79
80
# File 'lib/ffi/libfuse/ackbar.rb', line 72

def monitor(name: 'SignalMonitor')
  @monitor ||= Thread.new do
    Thread.current.name = name
    loop do
      @pr.wait_readable(block_given? ? yield : nil)
      break unless self.next
    end
  end
end

#nextBoolean, Array<String,Object>

Handle the next available signal on the pipe (without blocking)

Returns:

  • (Boolean)

    false if pipe is closed, true if pipe would block

  • (Array<String,Object>)

    signal name, signal result when a signal has been processed



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ffi/libfuse/ackbar.rb', line 46

def next
  signame = signal.signame(@pr.read_nonblock(1).unpack1('c'))
  t = @traps[signame]
  [signame, t.arity.zero? ? t.call : t.call(signame)]
rescue EOFError
  # the signal pipe writer is closed - we are exiting.
  @pr.close
  false
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
  # oh well...
  true
end

#restoreObject Also known as: close

Restore traps as they were at #new and close the write side of the pipe



60
61
62
63
64
65
# File 'lib/ffi/libfuse/ackbar.rb', line 60

def restore
  @restore.each_pair { |signame, handler| signal.trap(signame, handler) }
  @restore = nil
  @pw.close
  @monitor&.join
end