Class: FFI::Libfuse::FuseCommon Abstract
- Inherits:
-
Object
- Object
- FFI::Libfuse::FuseCommon
- Defined in:
- lib/ffi/libfuse/fuse_common.rb
Overview
Base class for Fuse, which itself is just a constant pointing to Fuse2 or Fuse3
This class documents the Ruby re-implementation of native fuse functions. It will not generally be used directly.
Instance Method Summary collapse
-
#daemonize ⇒ Object
private
Ruby implementation of fuse_daemonize which does not work under MRI probably due to the way ruby needs to understand native threads.
-
#default_traps ⇒ Object
Ruby implementation of fuse default traps.
-
#exit(_signame = nil) ⇒ Thread
Starts a thread to unmount the filesystem and stop the processing loop.
-
#fuse_clean_cache ⇒ Object
private
Keep track of time until next cache clean for the caching performed by libfuse itself.
-
#fuse_loop(**_options) ⇒ Object
private
Ruby implementation of single threaded fuse loop.
-
#fuse_loop_mt(max_idle_threads: 10, max_threads: nil, **_options) ⇒ Object
private
Ruby implementation of multi threaded fuse loop.
-
#run(native: false, **options) ⇒ Integer
Run the mounted filesystem until exit.
-
#run_native(foreground: true, single_thread: true, **options) ⇒ Object
private
Running fuse loop natively.
-
#run_ruby(foreground: true, single_thread: true, traps: {}, remember: false, **options) ⇒ Object
private
Implement fuse loop in ruby.
Instance Method Details
#daemonize ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Ruby implementation of fuse_daemonize which does not work under MRI probably due to the way ruby needs to understand native threads.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 129 def daemonize raise 'Cannot daemonize without support for fork' unless Process.respond_to?(:fork) # pipe to wait for fork pr, pw = ::IO.pipe if Process.fork # rubocop:disable Style/RescueModifier status = pr.read(1).unpack1('c') rescue 1 Kernel.exit!(status) # rubocop:enable Style/RescueModifier end begin Process.setsid Dir.chdir '/' # close/redirect file descriptors $stdin.close [$stdout, $stderr].each { |io| io.reopen('/dev/null', 'w') } pw.write([0].pack('c')) ensure pw.close end end |
#default_traps ⇒ Object
Ruby implementation of fuse default traps
- INT, HUP, TERM, TSTP to unmount and exit filesystem
- PIPE is ignored
121 122 123 124 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 121 def default_traps exproc = ->(signame) { exit(signame) } @default_traps ||= { INT: exproc, HUP: exproc, TERM: exproc, TSTP: exproc, PIPE: 'IGNORE' } end |
#exit(_signame = nil) ⇒ Thread
Starts a thread to unmount the filesystem and stop the processing loop. generally expected to be called from a signal handler
213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 213 def exit(_signame = nil) return unless @fuse # Unmount/exit in a separate thread so the main fuse thread can keep running. @exit ||= Thread.new do unmount # without this sleep before exit, MacOS does not complete unmounting sleep 0.2 if mac_fuse? Libfuse.fuse_exit(@fuse) true end end |
#fuse_clean_cache ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Keep track of time until next cache clean for the caching performed by libfuse itself
160 161 162 163 164 165 166 167 168 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 160 def fuse_clean_cache now = Time.now @next_cache_clean ||= now return @next_cache_clean - now unless now >= @next_cache_clean delay = Libfuse.fuse_clean_cache(@fuse) @next_cache_clean = now + delay delay end |
#fuse_loop(**_options) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Ruby implementation of single threaded fuse loop
172 173 174 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 172 def fuse_loop(**) safe_fuse_process until fuse_exited? end |
#fuse_loop_mt(max_idle_threads: 10, max_threads: nil, **_options) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Ruby implementation of multi threaded fuse loop
We cannot simulate the clone_fd behaviour of fuse_loop_mt as the required fd is not exposed in the low level fuse api.
183 184 185 186 187 188 189 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 183 def fuse_loop_mt(max_idle_threads: 10, max_threads: nil, **) ThreadPool.new(name: 'FuseThread', max_idle: max_idle_threads.to_i, max_active: max_threads&.to_i) do raise StopIteration if fuse_exited? safe_fuse_process end.join end |
#run(native: false, **options) ⇒ Integer
Run the mounted filesystem until exit
42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 42 def run(native: false, **) return false unless mounted? native ? run_native(**) : run_ruby(**) rescue Errno => e -e.errno rescue StandardError, ScriptError => e warn "#{e}\n#{e.backtrace.join("\n")}" -1 ensure teardown end |
#run_native(foreground: true, single_thread: true, **options) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Running fuse loop natively
Pros
- clone_fd will work
- filesystem interrupts may work
Cons
- multi-threading will create a new ruby thread for every callback
- cannot daemonize multi-threaded (hangs) TODO: Why - pthread_lock?, GVL?
- cannot pass signals to the filesystem
- connot use fuse_context (because the ruby thread is not the native thread)
105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 105 def run_native(foreground: true, single_thread: true, **) raise 'Cannot run daemonized native multi-thread fuse_loop' if !single_thread && !foreground clear_default_traps (se = session) && Libfuse.fuse_set_signal_handlers(se) Libfuse.fuse_daemonize(foreground ? 1 : 0) single_thread ? Libfuse.fuse_loop(@fuse) : native_fuse_loop_mt(**) ensure (se = session) && Libfuse.fuse_remove_signal_handlers(se) end |
#run_ruby(foreground: true, single_thread: true, traps: {}, remember: false, **options) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Implement fuse loop in ruby
Pros:
- multi-threading works
- can set max_threads @see https://github.com/libfuse/libfuse/issues/203
- can send signals to the FS (eg to reload)
- daemonize works
Cons:
- clone_fd is ignored
- filesystem interrupts probably can't work
75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/ffi/libfuse/fuse_common.rb', line 75 def run_ruby(foreground: true, single_thread: true, traps: {}, remember: false, **) traps = default_traps.merge(traps).keep_if { |_, v| v } Ackbar.trap(default_traps.merge(traps)) do |signals| daemonize unless foreground # Monitor for signals (and cache cleaning if required) signals.monitor { fuse_cache_timeout(remember) } single_thread ? fuse_loop(**) : fuse_loop_mt(**) 0 end end |