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