Module: GReactor

Defined in:
lib/greactor.rb,
lib/greactor/io.rb,
lib/greactor/engine.rb,
lib/greactor/timers.rb,
lib/greactor/events.rb,
lib/greactor/version.rb,
lib/greactor/logging.rb,
lib/greactor/settings.rb,
lib/greactor/protocol.rb,
lib/greactor/io_wrappers.rb,
lib/greactor/io_ssl_wrappers.rb

Overview

please read the README file for an introduction to the GReactor Library.

Defined Under Namespace

Modules: Settings Classes: BasicIO, ListenerIO, Protocol, SSLBasicIO, SSLListenerIO, TimedEvent

Constant Summary collapse

VERSION =
"0.0.14"

Class Method Summary collapse

Class Method Details

.add_io(io, params) ⇒ GReactor::BasicIO

Adds a manually created IO object to the GReactor engine. The io object will be wrapped inside a BasicIO wrapper.

This method is also used by listeners to add any accepted connections to the GReactor engine's IO stack.

Returns:


64
65
66
67
# File 'lib/greactor/io.rb', line 64

def add_io io, params
	new_io = BasicIO.new io, params
	add_raw_io io, new_io
end

.add_raw_io(io, handler) ⇒ Object Also known as: add_raw_io_to_stack

Adds a raw IO object to the stack and a handler for that IO.

The handler MUST respond to the following methods:

clear?

returns true or false - if true, the IO object will be removed from the stack.

on_disconnect

called once the IO is closed (following a call to `clear?`).

call

called whenever the IO contains data to be read.

The handler MUST retain a copy of the IO object to perform it's actions.

The handler MUST clear the IO's incoming buffer each time `call` is called.


116
117
118
# File 'lib/greactor/io.rb', line 116

def add_raw_io io, handler
	@io_locker.synchronize { @ios[io] = handler }
end

.add_ssl_io(io, params) ⇒ GReactor::BasicIO

Adds a manually created IO object to the GReactor engine. The io object will be wrapped inside a SSLBasicIO wrapper.

This method is also used by listeners to add any accepted connections to the GReactor engine's IO stack.

Returns:


87
88
89
90
# File 'lib/greactor/io.rb', line 87

def add_ssl_io io, params
	new_io = SSLBasicIO.new io, params
	add_raw_io io, new_io
end

.callback(object, method_name, *args, &block) ⇒ true

This method runs an object's method asynchronously and returns immediately. This method will also run an optional callback if a block is supplied.

This method accepts:

object

an object who's method will be called.

method

the method's name to be called. type: Symbol.

*args

any arguments to be passed to the method.

block (optional)

If a block is supplied, it will be used as a callback and the method's return value will be passed on to the block.

Returns:

  • (true)

    always returns true.


29
30
31
# File 'lib/greactor/events.rb', line 29

def callback object, method_name, *args, &block
	block ? queue([object.method(method_name), args, block], @callback_proc) : queue(args, object.method(method_name))
end

.clear_listenersGReactor

resets the listeners stack so that on the next restart they wouldn't be activated.

Returns:

  • (GReactor)

    always returns the GReactor module.


54
55
56
57
# File 'lib/greactor/io.rb', line 54

def clear_listeners
	@listeners_locker.synchronize { @listeners.clear }
	self
end

.close_io(io) ⇒ nil

Closes the IO object and removes it from the GReactor's engine, also calling the #on_disconnect callback if the IO's handler asnwers to the event (see Protocol for a handler example).

Returns:

  • (nil)

93
94
95
96
97
# File 'lib/greactor/io.rb', line 93

def close_io io
	queue [( @io_locker.synchronize { @ios.delete(io) } )], @disconnect_proc
	( io.close unless io.closed? ) rescue nil
	nil
end

.count_iosObject

Returns the number of registered IO objects.


69
70
71
# File 'lib/greactor/io.rb', line 69

def count_ios
	@ios.count
end

.create_logger(log_file = STDOUT, copy_to_stdout = false) ⇒ true Also known as: set_logger

create and set the logger object. accepts:

log_file

A log file name to be used for logging or STDOUT or nil (no logging). Defaults to STDOUT.

copy_to_stdout

If false, log will only log to file. If true, the logged messages will also be printed to STDOUT. Defaults to false.

Returns:

  • (true)

    always returns true.


26
27
28
29
30
# File 'lib/greactor/logging.rb', line 26

def create_logger log_file = STDOUT, copy_to_stdout = false
	@copy_to_stdout = ( copy_to_stdout ? (::Logger.new(STDOUT)) : false )
	@logger = log_file ? ::Logger.new(log_file) : nil
	true
end

.debug(data, &block) ⇒ String, ...

logs debug info

Returns:

  • (String, Exception, Object)

    always returns the Object sent to the log.


51
52
53
54
55
# File 'lib/greactor/logging.rb', line 51

def debug data, &block
	@logger.debug data, &block if @logger
	@copy_to_stdout.debug data, &block if @copy_to_stdout
	data
end

.eachObject

Allows you to execute a block of code on each IO or handler

i.e.

GReactor.each {|io_wrapper| io_wrapper.params[:handler].on_event :global_event }

If no block is given, the Enumerator returned is a temporal copy of the IO wrappers stack, but NOT the IO stack itself.


78
79
80
81
# File 'lib/greactor/io.rb', line 78

def each
	return (@io_locker.synchronize { @ios.values } .each ) unless block_given?
	@io_locker.synchronize { @ios.values } .each { |io, h| yield io, h }
end

.error(data, &block) ⇒ String, ...

logs errors

Returns:

  • (String, Exception, Object)

    always returns the Object sent to the log.


65
66
67
68
69
# File 'lib/greactor/logging.rb', line 65

def error data, &block
	@logger.error data, &block if @logger
	@copy_to_stdout.error data, &block if @copy_to_stdout
	data
end

.fatal(data, &block) ⇒ String, ...

logs a fatal error

Returns:

  • (String, Exception, Object)

    always returns the Object sent to the log.


72
73
74
75
76
# File 'lib/greactor/logging.rb', line 72

def fatal data, &block
	@logger.fatal data, &block if @logger
	@copy_to_stdout.fatal data, &block if @copy_to_stdout
	data
end

.hijack_io(io) ⇒ GReactor::BasicIO?

Hijacks the IO object from the reactor (the IO is removed from the listening stack) and returns the IO wrapper object if the IO was a member of the reactor's pool.

Returns:


102
103
104
# File 'lib/greactor/io.rb', line 102

def hijack_io io
	@io_locker.synchronize { @ios.delete(io) }
end

.info(data, &block) ⇒ String, ... Also known as: log

logs info

Returns:

  • (String, Exception, Object)

    always returns the Object sent to the log.


43
44
45
46
47
# File 'lib/greactor/logging.rb', line 43

def info data, &block
	@logger.info data, &block if @logger
	@copy_to_stdout.info data, &block if @copy_to_stdout
	data
end

.jointrue, false

Sets signal-traps and waits.

If a block is given, it will be executed AFTER the exit signal is raised but BEFORE stoping the engine.

Returns:

  • (true, false)

    returns true unless the GReactor wasn't running.


72
73
74
75
76
77
78
79
80
# File 'lib/greactor/engine.rb', line 72

def join
	return false unless running?
	set_traps
	fork_server
	sleep rescue true
	set_traps!
	yield if block_given?
	stop rescue true
end

.listen(params = {}, &block) ⇒ true

Starts listening to a port.

Accepts a list of parameters and an optional block (that would be used to initiate the `:handler` parameter).

These Are the optional parameters:

bind

The IP or host name to listen to. Defaults all ip addresses and hosts (0.0.0.0).

port

The port number to listen to. Defaults to 3000 and increments by 1 with every portless listen call (3000, 3001, 3002…).

handler

The object that will handle the connection. Should answer to `#call io`. See the Protocol class for an good use example (or for quickstart).

timeout

The timeout for the connection. Defaults to none (no timeout!).

ssl

Set this parameter to true to listen and react to SSL connections with a self signed certificate.

ssl_key + ssl_cert

Set both these parameters to listen to an SSL connection with a registered certificate. Falls back to a self registered certificate if one of the two is missing.

ssl_protocols

An optional Array of Strings for TSL Protocol Negotiations Extension.

Returns:

  • (true)

    always returns true - even if a port is already used(!) which might cause an error.


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/greactor/io.rb', line 27

def listen params = {}, &block
	# set port if undefined
	if !params[:port] && defined? ARGV
		if ARGV.find_index('-p')
			port_index = ARGV.find_index('-p') + 1
			params[:port] ||= ARGV[port_index].to_i
			ARGV[port_index] = (params[:port] + 1).to_s
		else
			ARGV << '-p'
			ARGV << '3001'
			params[:port] ||= 3000
		end
	end
	params[:handler] ||= block || (Proc.new { |io| io.read; false})
	params[:bind] = nil if params[:bind] == '0.0.0.0'
	exists = @listeners.select { |p,io| p[:port] == params[:port] && p[:bind] == params[:bind] }
	if exists.any?
		puts "Warning, port and binding already in use, returning the existing listener's parameters (modifying them might require restarting GReactor)."
		return exists.first[0]
	end
	@listeners_locker.synchronize { @listeners[params] = nil }
	start_listening if running?
	params
end

.log_raw(line) ⇒ String, ...

writes a raw line to the log

Returns:

  • (String, Exception, Object)

    always returns the Object sent to the log.


35
36
37
38
39
# File 'lib/greactor/logging.rb', line 35

def log_raw line
	@logger << line if @logger
	@copy_to_stdout << line if @copy_to_stdout
	line
end

.loggerLogger?

Gets the active logger. STDOUT is the default logger.

Returns:

  • (Logger, nil)

12
13
14
# File 'lib/greactor/logging.rb', line 12

def logger
	@logger
end

.logger_copyLogger?

gets the active STDOUT copy, if exists

Returns:

  • (Logger, nil)

17
18
19
# File 'lib/greactor/logging.rb', line 17

def logger_copy
	@copy_to_stdout
end

.on_shutdown(*args, &block) ⇒ true

Registers a task to be performed during shutdown.

i.e.:

GR.on_shutdown("Goodbye") {|str| puts str}

Returns:

  • (true)

    always returns true.


93
94
95
96
# File 'lib/greactor/engine.rb', line 93

def on_shutdown *args, &block
	@shutdown_locker.synchronize { @shutdown_queue << [block, args] }
	true
end

.queue(args, job, &block) ⇒ true

Adds a job OR a block to the queue. run_async and callback extend this core method.

This method accepts two arguments and an optional block:

args

An Array of arguments (or an empty array) to be passed on to the executed method.

job

An object that answers to `call`, usually a Proc or Lambda.

block (optional)

This block will be used ONLY if the job argument isn't supplied.

The callback will NOT be called if the executed job failed (raised an exception).

Returns:

  • (true)

    always returns true.

See Also:


46
47
48
49
# File 'lib/greactor/events.rb', line 46

def queue args, job, &block
	@queue_locker.synchronize { @queue << [(job || block), args] }
	true
end

.restart(thread_count = nil) ⇒ Fixnum

Restarts the GReactor using the new number of threads specified (defaults to the same numer already in use).

Shutdown tasks will NOT be performed.

Returns:

  • (Fixnum)

    returns number of threads created.


62
63
64
65
66
67
# File 'lib/greactor/engine.rb', line 62

def restart thread_count = nil
	@thread_count = thread_count ||= @threads.count
	stop_listening
	stop_workers
	start thread_count
end

.run_after(seconds, *args, &block) ⇒ GReactor::TimedEvent

pushes a timed event to the timers's stack

accepts:

seconds

the minimal amount of seconds to wait before calling the handler's `call` method.

*arg

any arguments that will be passed to the handler's `call` method.

&block

the block to execute.

A block is required.

Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistent).

Returns:


55
56
57
# File 'lib/greactor/timers.rb', line 55

def run_after seconds, *args, &block
	timed_job seconds, 1, args, block
end

.run_async(*args, &block) ⇒ true

Accepts a block and runs it asynchronously. This method runs asynchronously and returns immediately.

use:

GReactor.run_async(arg1, arg2, arg3 ...) { |arg1, arg2, arg3...| do_something }

the block will be run within the current context, allowing access to current methods and variables.

Returns:

  • (true)

    always returns true.


16
17
18
# File 'lib/greactor/events.rb', line 16

def run_async *args, &block
	queue args, block
end

.run_at(time, *args, &block) ⇒ GReactor::TimedEvent

pushes a timed event to the timers's stack

accepts:

time

the time at which the job should be executed.

*arg

any arguments that will be passed to the handler's `call` method.

&block

the block to execute.

A block is required.

Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistent).

Returns:


71
72
73
# File 'lib/greactor/timers.rb', line 71

def run_at time, *args, &block
	timed_job( (Time.now - time), 1, args, block)
end

.run_every(seconds, limit = -1,, *args, &block) ⇒ GReactor::TimedEvent

pushes a repeated timed event to the timers's stack

accepts:

seconds

the minimal amount of seconds to wait before calling the handler's `call` method.

limit

the amount of times the event should repeat itself. The event will repeat every x amount of `seconds`. The event will repeat forever if limit is set to false.

*arg

any arguments that will be passed to the handler's `call` method.

&block

the block to execute.

A block is required.

Timed event's time of execution is dependant on the workload and continuous uptime of the process (timed events AREN'T persistent unless you save and reload them yourself).

Returns:


87
88
89
# File 'lib/greactor/timers.rb', line 87

def run_every seconds, limit = -1, *args, &block
	timed_job seconds, limit, args, block
end

.running?Boolean

returns true if the GReactor's engine is running.

Returns:

  • (Boolean)

82
83
84
# File 'lib/greactor/engine.rb', line 82

def running?
	@running
end

.start(thread_count = nil) ⇒ Fixnum

Starts (or restarts) the reactor's engine.

accepts:

thread_count

The number of worker threads the reactor should use. Defaults to 8 OR the current number of threads (if restart is used).

block (optional)

If a block is supplied, the block will be executed and then the main thread will hang, as if `GReactor.join` was called.

Returns:

  • (Fixnum)

    returns number of threads created.


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/greactor/engine.rb', line 42

def start thread_count = nil
	if running?
		restart thread_count
	else
		create_workers thread_count
		start_listening
	end
	Kernel.at_exit { set_traps!; stop rescue true } if @graceful && !@graceful_set
	@graceful_set = true
	@threads.count
	if block_given?
		yield(self)
		join
	end
end

.stoptrue, false

Stops the GReactor and calls any shutdown callbacks.

This method will wait for the GReactor engine to stop and shutdown tasks to complete.

Returns:

  • (true, false)

    returns true unless the GReactor wasn't running.


10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/greactor/engine.rb', line 10

def stop
	return false unless running?
	stop_listening
	stop_workers
	check_timers
	1000.times { break unless do_job }
	@queue.clear
	@ios.keys.each {|io|  close_io(io) }
	@queue.insert -1, *@shutdown_queue
	@shutdown_queue.clear
	check_timers
	1000.times { break unless do_job }
	true
end

.stop!Object

attemps a force-stop of all worker threads and processes.


26
27
28
29
30
31
32
33
# File 'lib/greactor/engine.rb', line 26

def stop!
	@timers.clear
	@queue.clear
	@ios.keys.each {|io|  close_io(io) }
	stop_workers!
	@queue.clear
	@shutdown_queue.clear
end

.warn(data, &block) ⇒ String, ...

logs warning

Returns:

  • (String, Exception, Object)

    always returns the Object sent to the log.


58
59
60
61
62
# File 'lib/greactor/logging.rb', line 58

def warn data, &block
	@logger.warn data, &block if @logger
	@copy_to_stdout.warn data, &block if @copy_to_stdout
	data
end