Skip to content

Instantly share code, notes, and snippets.

@kirel
Last active December 10, 2015 01:09
Show Gist options
  • Select an option

  • Save kirel/4356686 to your computer and use it in GitHub Desktop.

Select an option

Save kirel/4356686 to your computer and use it in GitHub Desktop.
Because I wanted to play with Threads...
module Deferred
class Proxy < BasicObject
def initialize(deferred, callback, errback)
@deferred, @callback, @errback = deferred, callback, errback
end
def method_missing(name, *args, &block)
::Thread.new do
begin
@callback.call(@deferred.send(name, *args, &block))
rescue => e
@errback.call(e)
end
end
end
end
# returns an object where all methods calls are async and return to either callback or errback
def deferred(callback = proc {}, errback = proc {})
Proxy.new(self, callback, errback)
end
end
module Synchronized
class Proxy < BasicObject
def initialize synchronized
@synchronized = synchronized
end
def method_missing(name, *args, &block)
@return, @err = nil
callback = ::Kernel.lambda { |ret| @return = ret }
errback = ::Kernel.lambda { |err| @err = err }
@synchronized.send(name, *args, callback, errback, &block).join
raise @err if @err
return @return
end
end
# returns an object on which async methods can be called like sync methods (as long as the async method returns the thread running the async method)
def synchronized
Proxy.new(self)
end
end
module Future
class Promise
include Deferred
def initialize &calculation
@calculation = calculation
@return, @err = nil
callback = lambda { |ret| @return = ret }
errback = lambda { |err| @err = err }
@deferred = deferred(callback, errback).call
end
def value
@deferred.join
raise @err if @err
return @return
end
def call
@calculation.call
end
end
class Proxy < BasicObject
def initialize target
@target = target
end
def method_missing(name, *args, &block)
Promise.new do
@target.send(name, *args, &block)
end
end
end
# returns an object where all method calls return promises that can be forced when calling value on it
def future
Proxy.new(self)
end
end
class Obj
include Deferred
include Synchronized
include Future
def call arg
sleep 1
puts "#{Thread.current} slept 1s"
sleep 1
puts "#{Thread.current} slept 2s"
sleep 1
return "#{Thread.current} #{arg}"
end
def panic
raise 'panic'
end
def async_call(arg, callback, errback)
deferred(callback, errback).call(arg)
end
def async_panic(callback, errback)
deferred(callback, errback).panic
end
def sync_call(arg)
synchronized.async_call(arg)
end
def future_call(arg)
future.call(arg)
end
def future_panic
future.panic
end
end
# Tests
callback = lambda { |arg| puts "callback: #{arg}" }
errback = lambda { |arg| puts "errback: #{arg}" }
promise = Obj.new.future_call('fin') # runs in background
Obj.new.async_call('fin', callback, errback) # runs in background and calls callback when finished
Obj.new.async_panic(callback, errback) # runs in background and calls errback
puts "synchronized #{Obj.new.sync_call('fin')}" # blocks and returns 'fin'
puts "promise: #{promise.value}" # blocks until promise is done and returns 'fin'
puts "promise: #{Obj.new.future_panic.value}" # raises panic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment