Re-enable spec/library for full CI runs.
[rbx.git] / kernel / bootstrap / scheduler.rb
blobe345a129c6e3cf4239d85a1e0d9c6aed1ce0bac1
1 ##
2 # The Scheduler provides an interface to the VM, allowing the VM to tell ruby
3 # when various operations have completed.  The operation is performed in the
4 # background with respect to running ruby code.
6 # All methods of Scheduler return right away.  When the operation has
7 # completed, the Channel is sent a value.  Using this setup we can easily
8 # write thread aware code that doesn't block the entire process, only
9 # a single ruby Thread.
11 # == Where should I use calls to Scheduler?
13 # Rubinius uses green threads, so it must never perform a blocking C call as
14 # that will block all ruby threads.
16 # In the case of IO#read, the C read(2) will block until enough bytes are
17 # ready to be read.  Instead of directly calling read(2), send_on_readable is
18 # called and waits in the VM until the file descriptor is ready to be read.
20 class Scheduler
22   ##
23   # In +microseconds+ send the +tag+ to +channel+.  Returns an event id for
24   # use with #cancel.  This is used to implement sleep.  For example:
25   #
26   #   chan = Channel.new
27   #   Scheduler.send_in_microseconds chan, 1000, :hello
28   #   p chan.receive
29   #   # 1 second later :hello is printed
30   #
31   # The chan.receive appears to block the current thread, waking up when there
32   # is a value on the channel.  To the caller of this code it appears that
33   # they have slept for 1 seconds.
35   def self.send_in_microseconds(channel, microseconds, tag)
36     Ruby.primitive :channel_send_in_microseconds
37     raise PrimitiveFailure, "primitive failed"
38   end
40   ##
41   # Same as send_in_microseconds, but the 2nd argument is seconds, not
42   # microseconds.
44   def self.send_in_seconds(chan, seconds, tag)
45     Ruby.primitive :channel_send_in_seconds
46     raise PrimitiveFailure, "primitive failed"
47   end
49   ##
50   # Instructs the VM to send a value to +channel+ when the IO object +io+ is
51   # readable.  +buffer+ and +nbytes+ are optional, and should be set to nil if
52   # you don't wish to use them.  Returns an event id, for use with #cancel
53   #
54   # There are 2 modes of operation, depending on if +buffer+ is nil or not.
55   #
56   # If +buffer+ is nil the +channel+ is sent the file descriptor for +io+.
57   # This can be used to wait on multiple IO objects with one channel and
58   # figure out which IO object was ready given the return value of #receive3
59   #
60   # The second mode is when +buffer+ is an IO::Buffer object.  In this mode
61   # when +io+ becomes readable +nbytes+ bytes are read from IO and stored into
62   # +buffer+.  Note that this is done using the read(2) C function and thus
63   # +nbytes+ is the maximum number of bytes that will be read, not the actual
64   # number read.  The value sent to the channel is the actual number of bytes
65   # read as a Fixnum object.
66   #
67   # If an error occurs while reading in the 2nd mode, the value sent to the
68   # channel is a SystemCallError representing the failure in read(2).
69   #
70   # See IO#sysread in kernel/core/io.rb for a simple example.
72   def self.send_on_readable(chan, io, buffer, nbytes)
73     Ruby.primitive :channel_send_on_readable
74     raise PrimitiveFailure, "primitive failed"
75   end
77   ##
78   # Instructs the VM to send +nil+ to +channel+ when +io+ is writable.
79   # Returns an event id, for use with #cancel
81   def self.send_on_writable(chan, io)
82     Ruby.primitive :channel_send_on_readable
83     raise PrimitiveFailure, "primitive failed"
84   end
86   ##
87   # Instructs the VM to send a Thread object to +chan+ when UNIX signal
88   # +signum+, a Fixnum, is received by the process.  Returns an event id, for
89   # use with #cancel
90   #
91   # The Thread object that will be sent is the Thread object that was running
92   # when the signal was received by the process.
93   #
94   # Example:
95   #
96   #   chan = Channel.new
97   #   Scheduler.send_on_signal chan, 2 # 2 is SIGINT
98   #   thr = Thread.new { some_long_operation }
99   #   val = chan.receive # blocks Thread.current until SIGINT is received
100   #                      # => #<Thread:0x....>
101   #   p val == thr       # => true
102   #
103   # Unlike all other +send_on+ methods, this one is persistant.  Calling
104   # +send_on_signal+ once for SIGINT will cause all future SIGINTs to send a
105   # value to +chan+.
107   def self.send_on_signal(channel, signum)
108     Ruby.primitive :channel_send_on_signal
109     raise PrimitiveFailure, "primitive failed"
110   end
112   ##
113   # Wrapper for the primitive, use +send_on_stopped+
115   def self.send_on_stopped_prim(channel, pid, flags)
116     Ruby.primitive :channel_send_on_stopped
117     raise PrimitiveFailure, "primitive failed"
118   end
120   ##
121   # Instructs the VM to send a value to +channel+ when process +pid+ is no
122   # longer running.  This is used to implement Process.wait.  Returns an event
123   # id for use with #cancel.
124   #
125   # Flags is a Fixnum and currently accepts only Process::WNOHANG to indicate
126   # send_on_stopped should send nil if the process is still running instead of
127   # blocking.
128   #
129   # The value sent to the channel is either:
130   # * A tuple containing +pid+ and +exit_status+
131   # * +false+ if there is no process +pid+
132   # * nil if flags indicated NOHANG
134   def self.send_on_stopped(channel, pid=-1, flags=0)
135     send_on_stopped_prim(channel, pid, flags)
136   end
138   ##
139   # All +send_on+ methods return an event id for the registered event.  That
140   # id can be passed to #cancel to inform the VM that we're not longer
141   # interested in the event.  No value is sent to the channel that was
142   # registered.
144   def self.cancel(id)
145     Ruby.primitive :scheduler_cancel
146     raise PrimitiveFailure, "primitive failed"
147   end