lib/eventbox/boxable.rb in eventbox-0.1.0 vs lib/eventbox/boxable.rb in eventbox-1.0.0
- old
+ new
@@ -34,28 +34,28 @@
# Define a threadsafe method for asynchronous (fire-and-forget) calls.
#
# The created method can be safely called from any thread.
# All method arguments are passed through the {Sanitizer}.
- # Arguments prefixed by a € sign are automatically passed as {Eventbox::WrappedObject}.
+ # Arguments prefixed by a +€+ sign are automatically passed as {Eventbox::ExternalObject}.
#
# The method itself might not do any blocking calls or expensive computations - this would impair responsiveness of the {Eventbox} instance.
# Instead use {action} in these cases.
#
# In contrast to {sync_call} it's not possible to call external blocks or proc objects from {async_call} methods.
#
# The method always returns +self+ to the caller.
def async_call(name, &block)
unbound_method = self.instance_method(name)
wrapper = ArgumentWrapper.build(unbound_method, name)
- with_block_or_def(name, block) do |*args, &cb|
+ with_block_or_def(name, block) do |*args, **kwargs, &cb|
if @__event_loop__.event_scope?
# Use the correct method within the class hierarchy, instead of just self.send(*args).
# Otherwise super() would start an infinite recursion.
- unbound_method.bind(eventbox).call(*args, &cb)
+ unbound_method.bind(eventbox).call(*args, **kwargs, &cb)
else
- @__event_loop__.async_call(eventbox, name, args, cb, wrapper)
+ @__event_loop__.async_call(eventbox, name, args, kwargs, cb, wrapper)
end
self
end
end
@@ -68,24 +68,24 @@
#
# It's possible to call external blocks or proc objects from {sync_call} methods.
# Blocks are executed by the same thread that calls the {sync_call} method to that time.
#
# All method arguments as well as the result value are passed through the {Sanitizer}.
- # Arguments prefixed by a € sign are automatically passed as {Eventbox::WrappedObject}.
+ # Arguments prefixed by a +€+ sign are automatically passed as {Eventbox::ExternalObject}.
#
# The method itself might not do any blocking calls or expensive computations - this would impair responsiveness of the {Eventbox} instance.
# Instead use {action} in these cases.
def sync_call(name, &block)
unbound_method = self.instance_method(name)
wrapper = ArgumentWrapper.build(unbound_method, name)
- with_block_or_def(name, block) do |*args, &cb|
+ with_block_or_def(name, block) do |*args, **kwargs, &cb|
if @__event_loop__.event_scope?
- unbound_method.bind(eventbox).call(*args, &cb)
+ unbound_method.bind(eventbox).call(*args, **kwargs, &cb)
else
answer_queue = Queue.new
- sel = @__event_loop__.sync_call(eventbox, name, args, cb, answer_queue, wrapper)
- @__event_loop__.callback_loop(answer_queue, sel)
+ sel = @__event_loop__.sync_call(eventbox, name, args, kwargs, cb, answer_queue, wrapper)
+ @__event_loop__.callback_loop(answer_queue, sel, name)
end
end
end
# Define a method for calls with deferred result.
@@ -104,50 +104,51 @@
#
# It's possible to call external blocks or proc objects from {yield_call} methods up to the point when the result was yielded.
# Blocks are executed by the same thread that calls the {yield_call} method to that time.
#
# All method arguments as well as the result value are passed through the {Sanitizer}.
- # Arguments prefixed by a € sign are automatically passed as {Eventbox::WrappedObject}.
+ # Arguments prefixed by a +€+ sign are automatically passed as {Eventbox::ExternalObject}.
#
# The method itself as well as the Proc object might not do any blocking calls or expensive computations - this would impair responsiveness of the {Eventbox} instance.
# Instead use {action} in these cases.
def yield_call(name, &block)
unbound_method = self.instance_method(name)
wrapper = ArgumentWrapper.build(unbound_method, name)
with_block_or_def(name, block) do |*args, **kwargs, &cb|
if @__event_loop__.event_scope?
- @__event_loop__.safe_yield_result(args, name)
- args << kwargs unless kwargs.empty?
- unbound_method.bind(eventbox).call(*args, &cb)
+ @__event_loop__.internal_yield_result(args, name)
+ unbound_method.bind(eventbox).call(*args, **kwargs, &cb)
self
else
answer_queue = Queue.new
sel = @__event_loop__.yield_call(eventbox, name, args, kwargs, cb, answer_queue, wrapper)
- @__event_loop__.callback_loop(answer_queue, sel)
+ @__event_loop__.callback_loop(answer_queue, sel, name)
end
end
end
# Threadsafe write access to instance variables.
- def attr_writer(name)
- async_call(define_method("#{name}=") do |value|
- instance_variable_set("@#{name}", value)
- end)
+ def attr_writer(*names)
+ super
+ names.each do |name|
+ async_call(:"#{name}=")
+ end
end
# Threadsafe read access to instance variables.
- def attr_reader(name)
- sync_call(define_method("#{name}") do
- instance_variable_get("@#{name}")
- end)
+ def attr_reader(*names)
+ super
+ names.each do |name|
+ sync_call(:"#{name}")
+ end
end
# Threadsafe read and write access to instance variables.
#
- # Attention: Be careful with read-modify-write operations - they are *not* atomic but are executed as two independent operations.
+ # Attention: Be careful with read-modify-write operations like "+=" - they are *not* atomic but are executed as two independent operations.
#
- # This will lose counter increments, since `counter` is incremented in a non-atomic manner:
+ # This will lose counter increments, since +counter+ is incremented in a non-atomic manner:
# attr_accessor :counter
# async_call def start
# 10.times { do_something }
# end
# action def do_something
@@ -162,13 +163,16 @@
# increment 1
# end
# async_call def increment(by)
# @counter += by
# end
- def attr_accessor(name)
- attr_reader name
- attr_writer name
+ def attr_accessor(*names)
+ super
+ names.each do |name|
+ async_call(:"#{name}=")
+ sync_call(:"#{name}")
+ end
end
# Define a private method for asynchronous execution.
#
# The call to the action method returns immediately after starting a new action.
@@ -190,11 +194,11 @@
# end
# action def do_something(str, action)
# str # => "value1"
# action.current? # => true
# # `action' can be passed to event scope or external scope,
- # # in order to send a signals per Action#raise
+ # # in order to send a signal per Action#raise
# end
#
def action(name, &block)
unbound_method = self.instance_method(name)
with_block_or_def(name, block) do |*args, &cb|
@@ -217,20 +221,20 @@
end
end
# An Action object is thin wrapper for a Ruby thread.
#
- # It is returned by {Eventbox#action} and optionally passed as last argument to action methods.
+ # It is returned by {Eventbox::Boxable#action action methods} and optionally passed as last argument to action methods.
# It can be used to interrupt the program execution by an exception.
#
# However in contrast to ruby's builtin threads, any interruption must be explicit allowed.
# Exceptions raised to an action thread are delayed until a code block is reached which explicit allows interruption.
# The only exception which is delivered to the action thread by default is {Eventbox::AbortAction}.
# It is raised by {Eventbox#shutdown!} and is delivered as soon as a blocking operation is executed.
#
# An Action object can be used to stop the action while blocking operations.
- # It should be made sure, that the `rescue` statement is outside of the block to `handle_interrupt`.
+ # It should be made sure, that the +rescue+ statement is outside of the block to +handle_interrupt+.
# Otherwise it could happen, that the rescuing code is interrupted by the signal.
# Sending custom signals to an action works like:
#
# class MySignal < Interrupt
# end
@@ -264,14 +268,13 @@
# The signal must be kind of Exception.
# See {Action} about asynchronous delivery of signals.
#
# This method does nothing if the action is already finished.
#
- # If {raise} is called within the action (#current? returns `true`), all exceptions are delivered immediately.
- # This happens regardless of the current interrupt mask set by `Thread.handle_interrupt`.
+ # If {raise} is called within the action ({#current?} returns +true+), all exceptions are delivered immediately.
+ # This happens regardless of the current interrupt mask set by +Thread.handle_interrupt+.
def raise(*args)
- # ignore raise, if sent from the action thread
if AbortAction === args[0] || (Module === args[0] && args[0].ancestors.include?(AbortAction))
::Kernel.raise InvalidAccess, "Use of Eventbox::AbortAction is not allowed - use Action#abort or a custom exception subclass"
end
if @event_loop.event_scope?
@@ -291,8 +294,13 @@
end
# @private
def join
@thread.join
+ end
+
+ # @private
+ def terminate
+ @thread.terminate
end
end
end