Skip to content

Instantly share code, notes, and snippets.

@seanlilmateus
Last active January 12, 2018 16:11
Show Gist options
  • Save seanlilmateus/4134454 to your computer and use it in GitHub Desktop.
Save seanlilmateus/4134454 to your computer and use it in GitHub Desktop.
Rubymotion GCD Future; with lazy evaluation
#!/usr/bin/env macruby -wKU
framework 'Foundation'
module Dispatch
class Promise < BasicObject
# MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
def self.new(&block)
::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Promise without a block") unless block_given?
::Kernel.raise(::ArgumentError, "Dispatch::Promise, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
self.alloc.initialization(block)
end
def when_done(call_back)
@group.notify(::Thread.current[:futures]) { call_back[@value] }
end
# setup Grand Central Dispatch concurrent Queue and Group
def initialization(block)
init
@computation = block
@value = @success = false
self
end
def inspect
value.inspect
end
def done?
!!@value
end
def value
# Each thread gets its own FIFO queue upon which we will dispatch
# the delayed computation passed in the &block variable.
::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
# Groups are just simple layers on top of semaphores.
@group = ::Dispatch::Group.new
# Asynchronously dispatch the future to the thread-local queue.
::Thread.current[:futures].async(@group) { @value = @computation.call }
# Wait fo the computation to finish. If it has already finished, then
# just return the value in question.
@group.wait
@value
end
def method_missing(method_name, *args, &block)
self.value.send(method_name, *args, &block) if value.respond_to?(method_name)
end
def respond_to_missing?(method_name, include_private = false)
value.respond_to?(method_name, include_private) || super
end
# like method_missing for objc
# without this 'promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }' will not work
# NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
# since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
def forwardingTargetForSelector(sel)
value if value.respond_to?(sel)
end
alias_method :description, :inspect
end
class Future < BasicObject
# MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
def self.new(&block)
::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Future without a block") unless block_given?
::Kernel.raise(::ArgumentError, "Dispatch::Future, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
self.alloc.initialization(block)
end
def initialization(block)
init
@promise = Promise.new(&block)
@promise.value
self
end
def when_done(&block); @promise.when_done(block);end
def description
if @promise.done?
value.inspect
else
NSString.stringWithString(super.gsub(/>/, ' running...>'))
end
end
def value
@promise.value
end
alias_method :inspect, :description
end
end
# Dispatch Future
the_future = Dispatch::Future.new { sleep 1; 6 / 2 }
the_future.when_done { |value| value = 10 }
p the_future # <Future: 0x400cf57c0 running...>
calculation = Dispatch::Future.new { sleep 2; 4 * 4 }
p calculation # <Future: 0x400cf6ee0 running...>
calculation.when_done { |value| puts value + 10 } # 26
p calculation.value + 2 # 18
# Dispatch Promise
promise = Dispatch::Promise.new { 12 }
promise2 = Dispatch::Promise.new { sleep 2; 10 }
result = promise2 + promise
p result
file_name = "/usr/share/dict/words"
promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }
string = NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
p string
@seanlilmateus
Copy link
Author

changed from ::Thread.current[:futures] to @futures_queue to make it thread safe
example below used not to work, should be working now:

future = Dispatch::Future.new { :HALLLLO }
Dispatch::Queue.concurrent(:high).async { puts future.value }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment