Module: Winipc

Defined in:
lib/winipc.rb,
lib/winipc/version.rb,
ext/winipc/winipc.c

Overview

winipc — Windows local IPC for Ruby: named pipes, shared memory, and named synchronization objects, with a safe-by-default API.

# named pipe, server side
Winipc::Pipe.listen("myapp/control") do |server|
  conn = server.accept
  puts conn.read(1024)
  conn.write("ack")
  conn.close
end

# client side (another process)
Winipc::Pipe.connect("myapp/control") do |c|
  c.write("hello")
  c.read(1024)
end

Defined Under Namespace

Classes: Abandoned, AccessDenied, BrokenPipe, Canceled, Closed, Error, Event, Exists, ModeError, Mutex, NotFound, NotOwner, OSError, Pipe, PipeBusy, RangeError, Semaphore, SharedMemory, TimeoutError, WouldExceedMax

Constant Summary collapse

PIPE_ACCESS_INBOUND =

—- Win32 flag values (verified) ————————————–

0x00000001
PIPE_ACCESS_OUTBOUND =
0x00000002
PIPE_ACCESS_DUPLEX =
0x00000003
FILE_FLAG_OVERLAPPED =
0x40000000
PIPE_TYPE_MESSAGE =
0x00000004
PIPE_READMODE_MESSAGE =
0x00000002
PIPE_WAIT =
0x00000000
PIPE_REJECT_REMOTE_CLIENTS =
0x00000008
PIPE_UNLIMITED_INSTANCES =
255
GENERIC_READ =
0x80000000
GENERIC_WRITE =
0x40000000
VERSION =
"0.1.0"
K_MUTEX =

Sync objects share the C constructors; subclasses pass their kind.

INT2FIX(K_MUTEX)
K_EVENT =
INT2FIX(K_EVENT)
K_SEM =
INT2FIX(K_SEM)

Class Method Summary collapse

Class Method Details

.ms_for(timeout) ⇒ Object

Raises:

  • (ArgumentError)


112
113
114
115
116
117
118
119
120
121
# File 'lib/winipc.rb', line 112

def ms_for(timeout)
  return -1 if timeout.nil? # INFINITE (block until signaled / connected)

  t = Float(timeout)
  raise ArgumentError, "timeout must be non-negative, got #{timeout.inspect}" if t.negative?

  ms = (t * 1000).round
  # Never collapse a tiny-but-positive wait into a non-blocking poll.
  ms.zero? && t.positive? ? 1 : ms
end

.obj_path(name, scope) ⇒ Object

Map a bare object name into the Local\ (default) or Global\ namespace.



83
84
85
86
87
88
89
90
# File 'lib/winipc.rb', line 83

def obj_path(name, scope)
  prefix = case scope
           when :local  then "Local\\"
           when :global then "Global\\"
           else raise ArgumentError, "scope must be :local or :global, got #{scope.inspect}"
           end
  "#{prefix}#{name}"
end

.pipe_path(name) ⇒ Object

Normalize a bare pipe name to the \.pipe\ namespace (pass a full \serverpipename through unchanged).



77
78
79
80
# File 'lib/winipc.rb', line 77

def pipe_path(name)
  s = name.to_s
  s.start_with?("\\\\") ? s : "\\\\.\\pipe\\#{s}"
end

.run_blockingObject

Run a blocking native call cooperatively. Under a Fiber scheduler (e.g. winloop) the call is offloaded to a worker Thread so the calling fiber parks (Thread#value routes through the scheduler) and the event loop keeps serving other fibers; with no scheduler it runs inline (the C call already releases the GVL). On fiber unwind the worker is killed+joined so it can’t leak or consume data destined for a later op.

Caveat: if the fiber is unwound (e.g. Timeout) in the instant after the worker acquired a resource but before the value was delivered, that acquisition is lost — there is no generic way to hand it back (the same inherent limitation as a cancelled read that already pulled bytes).



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/winipc.rb', line 57

def run_blocking
  sched = Fiber.scheduler
  return yield unless sched

  worker = Thread.new do
    Thread.current.report_on_exception = false
    yield
  end
  begin
    worker.value
  ensure
    if worker.alive?
      worker.kill
      worker.join
    end
  end
end