Class: IO::Reactor

Inherits:
Object
  • Object
show all
Defined in:
lib/io/reactor.rb

Overview

An object-oriented multiplexing asynchronous IO mechanism for Ruby.

Synopsis

reactor = IO::Reactor.new
data_to_send = "some stuff to send"

reader, writer = IO.pipe

# Read from the reader end of the pipe until the writer finishes
reactor.register( reader, :read ) do |io,event|
    if io.eof?
        reactor.unregister( io )
        io.close
    else
        puts io.read( 256 )
    end
end

# Write to the writer end of the pipe until there's no data left
reactor.register( writer, :write ) do |io,event|
    bytes = io.write( data_to_send )
    data_to_send.slice!( 0, bytes )

    if data_to_send.empty?
        reactor.unregister( io )
        io.close
    end
end

# Now pump the reactor until both sides are done
reactor.poll until reactor.empty?

Author

Michael Granger <[email protected]>

Copyright © 2002-2008 The FaerieMUD Consortium. All rights reserved.

This module is free software. You may use, modify, and/or redistribute this software under the same terms as Ruby itself.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Version

$Id: reactor.rb 86 2008-08-19 23:41:58Z deveiant $

Constant Summary collapse

SVNRev =

SVN Revision

%q$Rev: 86 $
SVNId =

SVN Id

%q$Id: reactor.rb 86 2008-08-19 23:41:58Z deveiant $
VERSION =

Package version

'1.0.4'
VALID_EVENTS =

List of valid event types, in the order IO#select returns them

[:read, :write, :error]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeReactor

Create and return a new IO reactor object.



78
79
80
81
82
83
84
85
86
87
# File 'lib/io/reactor.rb', line 78

def initialize
	@handles		= Hash.new {|hsh,key|
		hsh[ key ] = {
			:events		=> [],
			:handler	=> nil,
			:args		=> [],
		}
	}
	@pending_events	= Hash.new {|hsh,key| hsh[ key ] = []}
end

Instance Attribute Details

#handlesObject (readonly)

The Hash of handles (instances of IO or its subclasses) associated with the reactor. The keys are the IO objects, and the values are a Hash of event/s => handler.



97
98
99
# File 'lib/io/reactor.rb', line 97

def handles
  @handles
end

#pending_eventsObject (readonly) Also known as: pendingEvents

The Hash of unhandled events which occurred in the last call to #poll, keyed by handle.



101
102
103
# File 'lib/io/reactor.rb', line 101

def pending_events
  @pending_events
end

Instance Method Details

#clearObject

Clear all registered handles from the poll object. Returns the handles that were cleared.



223
224
225
226
227
228
229
230
# File 'lib/io/reactor.rb', line 223

def clear
	rv = @handles.keys

	@pending_events.clear
	@handles.clear

	return rv
end

#disable_events(io, *events) ⇒ Object Also known as: disableEvents

Remove the specified events from the list that will be polled for on the given io handle.

Raises:

  • (RuntimeError)


154
155
156
157
158
# File 'lib/io/reactor.rb', line 154

def disable_events( io, *events )
	raise RuntimeError, "Cannot disable the :error event" if
		events.include?( :error )
	@handles[ io ][:events] -= events
end

#empty?Boolean

Returns true if no handles are associated with the receiver.

Returns:

  • (Boolean)


295
296
297
# File 'lib/io/reactor.rb', line 295

def empty?
	@handles.empty?
end

#enable_events(io, *events) ⇒ Object Also known as: enableEvents

Add the specified events to the list that will be polled for on the given io handle.



146
147
148
# File 'lib/io/reactor.rb', line 146

def enable_events( io, *events )
	@handles[ io ][:events] |= events
end

#event_enabled?(io, event) ⇒ Boolean Also known as: has_event_enabled?

Returns true if the specified event is enabled for the given io.

Returns:

  • (Boolean)


163
164
165
166
167
# File 'lib/io/reactor.rb', line 163

def event_enabled?( io, event )
	return false unless @handles.key?( io )
	return true if event == :error # Error is always enabled for all handles
	return @handles[ io ][ :events ].include?( event )
end

#poll(timeout = -1 )) ⇒ Object

Poll the handles registered to the reactor for pending events. The following event types are defined:

:read

Data may be read from the handle without blocking.

:write

Data may be written to the handle without blocking.

:error

An error has occurred on the handle. This event type is always enabled, regardless of whether or not it is passed as one of the events.

Any handlers specified when the handles were registered are run for those handles with events. If a block is given, it will be invoked once for each handle which doesn’t have an explicit handler. If no block is given, events without explicit handlers are inserted into the reactor’s pending_events attribute.

The timeout argument is the number of floating-point seconds to wait for an event before returning (ie., fourth argument to the underlying select() call); negative timeout values will cause #poll to block until there is at least one event to report.

This method returns the number of handles on which one or more events occurred.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/io/reactor.rb', line 259

def poll( timeout=-1 ) # :yields: io, eventMask
	timeout = timeout.to_f
	@pending_events.clear
	count = 0

	unless @handles.empty?
		timeout = nil if timeout < 0
		evented_handles = self.get_pending_events( timeout )

		# For each event of each io that had an event happen, call any
		# associated callback, or any provided block, or failing both of
		# those, add the event to the hash of unhandled pending events.
		evented_handles.each do |io,events|
			count += 1
			events.each do |ev|
				# Don't continue if the io was unregistered by an earlier handler
				break unless @handles.key?( io )
				
				args = @handles[ io ][:args]

				if @handles[ io ][:handler]
					@handles[ io ][:handler].call( io, ev, *args )
				elsif block_given?
					yield( io, ev, *args )
				else
					@pending_events[io].push( ev )
				end
			end
		end
	end

	return count
end

#register(io, *args, &handler) ⇒ Object Also known as: add

Register the specified IO object with the reactor for events given as args. The reactor will test the given io for the events specified whenever #poll is called. See the #poll method for a list of valid events. If no events are specified, only :error events will be polled for.

If a handler is specified, it will be called whenever the io has any of the specified events occur to it. It should take at least two parameters: the io and the event.

If args contains any objects except the Symbols ‘:read’, ‘:write’, or ‘:error’, and a handler is specified, they will be saved and passed to handler for each event.

Registering a handle will unregister any previously registered event/handler+arguments pairs associated with the handle.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/io/reactor.rb', line 121

def register( io, *args, &handler )
	events = VALID_EVENTS & args
	args -= events

	self.unregister( io )
	self.enable_events( io, *events )
	if handler
		self.set_handler( io, *args, &handler )
	else
		self.set_args( io, *args )
	end

	return self
end

#registered?(io) ⇒ Boolean

Returns true if the given io handle is registered with the reactor.

Returns:

  • (Boolean)


139
140
141
# File 'lib/io/reactor.rb', line 139

def registered?( io )
	return @handles.key?( io )
end

#remove_args(io) ⇒ Object Also known as: removeArgs

Remove the arguments for the given handle to the given args.



205
206
207
# File 'lib/io/reactor.rb', line 205

def remove_args( io )
	return @handles[ io ][:args].clear
end

#remove_handler(io) ⇒ Object Also known as: removeHandler

Remove and return the handler for events on the given io handle.



185
186
187
188
189
190
# File 'lib/io/reactor.rb', line 185

def remove_handler( io )
	rval = @handles[ io ][:handler]
	@handles[ io ][:handler] = nil
	self.remove_args( io )
	return rval
end

#set_args(io, *args) ⇒ Object Also known as: setArgs

Set the additional arguments to pass to the handler for the given io handle on each event to the given args.



196
197
198
199
200
# File 'lib/io/reactor.rb', line 196

def set_args( io, *args )
	rval = @handles[ io ][:args]
	@handles[ io ][:args] = args
	return rval
end

#set_handler(io, *args, &handler) ⇒ Object Also known as: setHandler

Set the handler for events on the given io handle to the specified handler. If any args are present, they will be passed as an exploded array to the handler for each event. Returns the previously-registered handler, if any.



175
176
177
178
179
180
# File 'lib/io/reactor.rb', line 175

def set_handler( io, *args, &handler )
	rval = @handles[ io ][:handler]
	@handles[ io ][:handler] = handler
	self.set_args( io, *args )
	return rval
end

#unregister(io) ⇒ Object Also known as: remove

Remove the specified io from the receiver’s list of registered handles, if present. Returns the handle if it was registered, or nil if it was not.



214
215
216
217
# File 'lib/io/reactor.rb', line 214

def unregister( io )
	@pending_events.delete( io )
	@handles.delete( io )
end