Last active
January 12, 2018 16:11
-
-
Save seanlilmateus/4134454 to your computer and use it in GitHub Desktop.
Rubymotion GCD Future; with lazy evaluation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
# Groups are just simple layers on top of semaphores. | |
@group = ::Dispatch::Group.new | |
# 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}") # | |
@value = @success = false | |
self | |
end | |
def inspect | |
value.inspect | |
end | |
def done? | |
!!@value | |
end | |
def __force__ | |
# Asynchronously dispatch the future to the thread-local queue. | |
::Thread.current[:futures].async(@group) { @value = @computation.call } | |
end | |
def value | |
# Wait fo the computation to finish. If it has already finished, then | |
# just return the value in question. | |
self.__force__ unless self.done? | |
@group.wait | |
@value | |
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 | |
alias_method :description, :inspect | |
private | |
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 | |
def forwardingTargetForSelector(sel) | |
value if value.respond_to?(sel) | |
end | |
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.__force__ | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
changed from ::Thread.current[:futures] to @futures_queue to make it thread safe
example below used not to work, should be working now: