Module: Winloop

Defined in:
lib/winloop.rb,
lib/winloop/version.rb,
lib/winloop/scheduler.rb,
ext/winloop/winloop.c

Overview

winloop — a native Windows (IOCP) Fiber Scheduler for MRI Ruby.

require "winloop"

Winloop.run do
  server = TCPServer.new("127.0.0.1", 9292)
  loop do
    client = server.accept          # io_wait via AFD poll
    Fiber.schedule do               # one fiber per connection
      while (line = client.gets)    # io_read via recv_nonblock + io_wait
        client.write(line)
      end
      client.close
    end
  end
end

All blocking socket I/O, sleeps, timeouts and Mutex/Queue/Thread#join inside the block run cooperatively on a single thread, driven by one I/O Completion Port. See Winloop::Scheduler for the hook implementations.

Defined Under Namespace

Classes: Backend, Error, MinHeap, Scheduler

Constant Summary collapse

VERSION =
"0.1.0"
READABLE =

Export the Ruby IO event bits the scheduler/backend agree on.

INT2NUM(RB_READABLE)
WRITABLE =
INT2NUM(RB_WRITABLE)
PRIORITY =
INT2NUM(RB_PRIORITY)

Class Method Summary collapse

Class Method Details

.runObject

Install a fresh Winloop::Scheduler on the current thread, run ‘block` inside a non-blocking fiber, drive the event loop until every scheduled fiber has finished, and return the block’s value.

Nesting is not supported; if a scheduler is already installed, the block is simply scheduled on it.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/winloop.rb', line 52

def run
  raise ArgumentError, "Winloop.run requires a block" unless block_given?

  # Already inside a scheduler (e.g. a nested Winloop.run within a fiber): just
  # run the block here — blocking operations already cooperate with that loop.
  return yield if Fiber.scheduler

  scheduler = Scheduler.new
  Fiber.set_scheduler(scheduler)
  result = nil
  error = nil
  Fiber.schedule do
    begin
      result = yield
    rescue Exception => e # rubocop:disable Lint/RescueException
      error = e
    end
  end
  scheduler.close # drives the loop to completion
  raise error if error
  result
ensure
  # Detach our (now-closed) scheduler so subsequent ordinary I/O on this
  # thread doesn't hit a closed backend. Safe here: we are on the main fiber
  # and close has already returned (the reentrant-close trap only bites when
  # set_scheduler(nil) is called from *inside* #close during finalization).
  Fiber.set_scheduler(nil) if Fiber.scheduler.equal?(scheduler)
end

.supported?Boolean

True if the IOCP/AFD backend is available on this platform.

Returns:

  • (Boolean)


42
43
44
# File 'lib/winloop.rb', line 42

def supported?
  const_defined?(:Backend)
end