module XS # Wraps the libxs library and attaches to the API functions # module LibXS extend FFI::Library begin # bias the library discovery to a path inside the gem first, then # to the usual system paths inside_gem = File.join(File.dirname(__FILE__), '..', '..', 'ext') XS_LIB_PATHS = [ inside_gem, '/usr/local/lib', '/opt/local/lib', '/usr/local/homebrew/lib', '/usr/lib64' ].map{|path| "#{path}/libxs.#{FFI::Platform::LIBSUFFIX}"} ffi_lib(XS_LIB_PATHS + %w{libxs}) rescue LoadError STDERR.puts "Unable to load this gem. The libxs library (or DLL) could not be found." STDERR.puts "If this is a Windows platform, make sure libxs.dll is on the PATH." STDERR.puts "For non-Windows platforms, make sure libxs is located in this search path:" STDERR.puts XS_LIB_PATHS.inspect exit 255 end # Size_t not working properly on Windows find_type(:size_t) rescue typedef(:ulong, :size_t) # Context and misc api # # @blocking = true is a hint to FFI that the following (and only the following) # function may block, therefore it should release the GIL before calling it. # This can aid in situations where the function call will/may block and another # thread within the lib may try to call back into the ruby runtime. Failure to # release the GIL will result in a hang; the hint *may* allow things to run # smoothly for Ruby runtimes hampered by a GIL. # # This is really only honored by the MRI implementation but it *is* necessary # otherwise the runtime hangs (and requires a kill -9 to terminate) # @blocking = true attach_function :xs_errno, [], :int @blocking = true attach_function :xs_init, [], :pointer @blocking = true attach_function :xs_setctxopt, [:pointer, :int, :pointer, :int], :int @blocking = true attach_function :xs_socket, [:pointer, :int], :pointer @blocking = true attach_function :xs_strerror, [:int], :pointer @blocking = true attach_function :xs_term, [:pointer], :int @blocking = true attach_function :xs_version, [:pointer, :pointer, :pointer], :void def self.version if @version.nil? major = FFI::MemoryPointer.new :int minor = FFI::MemoryPointer.new :int patch = FFI::MemoryPointer.new :int LibXS.xs_version major, minor, patch @version = {:major => major.read_int, :minor => minor.read_int, :patch => patch.read_int} end @version end # Message api @blocking = true attach_function :xs_msg_close, [:pointer], :int @blocking = true attach_function :xs_msg_copy, [:pointer, :pointer], :int @blocking = true attach_function :xs_msg_data, [:pointer], :pointer @blocking = true attach_function :xs_msg_init, [:pointer], :int @blocking = true attach_function :xs_msg_init_size, [:pointer, :size_t], :int @blocking = true attach_function :xs_msg_init_data, [:pointer, :pointer, :size_t, :pointer, :pointer], :int @blocking = true attach_function :xs_msg_move, [:pointer, :pointer], :int @blocking = true attach_function :xs_msg_size, [:pointer], :size_t # Used for casting pointers back to the struct # class Msg < FFI::Struct layout :content, :pointer, :flags, :uint8, :vsm_size, :uint8, :vsm_data, [:uint8, 30] end # class Msg # Socket api @blocking = true attach_function :xs_bind, [:pointer, :string], :int @blocking = true attach_function :xs_connect, [:pointer, :string], :int @blocking = true attach_function :xs_close, [:pointer], :int @blocking = true attach_function :xs_getsockopt, [:pointer, :int, :pointer, :pointer], :int @blocking = true attach_function :xs_recvmsg, [:pointer, :pointer, :int], :int @blocking = true attach_function :xs_recv, [:pointer, :pointer, :size_t, :int], :int @blocking = true attach_function :xs_sendmsg, [:pointer, :pointer, :int], :int @blocking = true attach_function :xs_send, [:pointer, :pointer, :size_t, :int], :int @blocking = true attach_function :xs_setsockopt, [:pointer, :int, :pointer, :int], :int # Poll api @blocking = true attach_function :xs_poll, [:pointer, :int, :long], :int module PollItemLayout def self.included(base) base.class_eval do layout :socket, :pointer, :fd, :int, :events, :short, :revents, :short end end end # module PollItemLayout class PollItem < FFI::Struct include PollItemLayout def socket() self[:socket]; end def fd() self[:fd]; end def readable? (self[:revents] & XS::POLLIN) > 0 end def writable? (self[:revents] & XS::POLLOUT) > 0 end def both_accessible? readable? && writable? end def inspect "socket [#{socket}], fd [#{fd}], events [#{self[:events]}], revents [#{self[:revents]}]" end def to_s; inspect; end end # class PollItem end end # module XS