Last active
October 1, 2015 05:41
-
-
Save scztt/20f82ab05a6a11fc3a05 to your computer and use it in GitHub Desktop.
This file contains 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
Deferred { | |
var value, error, resolved=\unresolved, waitingThreads; | |
*new { | |
^super.new.init | |
} | |
init { | |
waitingThreads = Array(2); | |
} | |
wait { | |
this.prWait(); | |
if (resolved == \error) { | |
error.throw; | |
}; | |
^value | |
} | |
hasValue { | |
^(resolved == \value) | |
} | |
hasError { | |
^(resolved == \error) | |
} | |
value { | |
if (resolved == \unresolved) { | |
AccessingDeferredValueError().throw; | |
}; | |
^value; | |
} | |
value_{ | |
|inValue| | |
if (resolved == \unresolved) { | |
resolved = \value; | |
error = nil; | |
this.prResume(); | |
} { | |
ResettingDeferredValueError(value, error).throw; | |
} | |
} | |
error { | |
if (resolved == \unresolved) { | |
AccessingDeferredValueError().throw; | |
}; | |
^error; | |
} | |
error_{ | |
|inError| | |
if (resolved == \unresolved) { | |
resolved = \error; | |
error = inError; | |
this.prResume(); | |
} { | |
ResettingDeferredValueError(value, error).throw; | |
} | |
} | |
valueCallback { | |
^{ | |
|value| | |
this.value = value; | |
} | |
} | |
errorCallback { | |
^{ | |
|error| | |
this.error = error; | |
} | |
} | |
using { | |
|function| | |
// We want to catch errors that happen when function is called, but AVOID catching errors | |
// that may occur during the this.value set operation. | |
var funcResult = \noValue; | |
forkIfNeeded { | |
try { | |
funcResult = function.value; | |
} { | |
|error| | |
this.error = error; | |
}; | |
if (funcResult != \noValue) { | |
this.value = funcResult | |
} | |
} | |
} | |
then { | |
|valueFunc, errorFunc, clock| | |
var newDeferred; | |
// If not specified, just default to returning / rethrowing whatevers passed in | |
valueFunc = valueFunc ?? {|v| v}; | |
errorFunc = errorFunc ?? {|v| v.throw}; | |
newDeferred = Deferred(); | |
{ | |
this.prWait; | |
if (this.hasValue) { | |
newDeferred.using({ | |
var result = valueFunc.(this.value); | |
if (result.isKindOf(Deferred)) { | |
result.wait | |
} { | |
result | |
} | |
}) | |
} { | |
newDeferred.using({ | |
var result = errorFunc.(this.error); | |
if (result.isKindOf(Deferred)) { | |
result.wait | |
} { | |
result | |
} | |
}) | |
} | |
}.fork(clock ?? thisThread.clock); | |
^newDeferred | |
} | |
onComplete { | |
|function, clock| | |
^this.then(function, nil, clock); | |
} | |
onError { | |
|function, clock| | |
^this.then(nil, function, clock); | |
} | |
prWait { | |
if (resolved == \unresolved) { | |
waitingThreads = waitingThreads.add(thisThread.threadPlayer); | |
\hang.yield; | |
} | |
} | |
prResume { | |
var time = thisThread.seconds; | |
var tempWaitingThreads = waitingThreads; | |
waitingThreads = nil; | |
tempWaitingThreads.do { | |
|thread| | |
thread.clock.sched(0, thread); | |
}; | |
// If our error is not going to be caught by a wait somewhere, we need to | |
// throw now else it's lost. | |
if (tempWaitingThreads.isEmpty && resolved == \error) { | |
error.throw; | |
} | |
} | |
} | |
ResettingDeferredValueError { | |
var value, error; | |
*new { | |
|value, error| | |
^super.newCopyArgs(value, error); | |
} | |
errorString { | |
^"ERROR: Trying to set a Deferred value after it has already been resolved. (value:%, error:%)".format(value, error) | |
} | |
} | |
AccessingDeferredValueError { | |
errorString { | |
^"ERROR: Trying to access a Deferred value that has not yet been resolved. Use .wait to wait for the value instead."; | |
} | |
} | |
+Synth { | |
*doNew { | |
|...args| | |
^Deferred().using({ | |
var obj; | |
obj = Synth(*args); | |
obj.server.sync; | |
obj; | |
}); | |
} | |
} | |
+Group { | |
*doNew { | |
|...args| | |
^Deferred().using({ | |
var obj; | |
obj = Group(*args); | |
obj.server.sync; | |
obj; | |
}) | |
} | |
} | |
+SynthDef { | |
doAdd { | |
|...args| | |
^Deferred().using({ | |
this.add(*args); | |
Server.default.sync; | |
this; | |
}) | |
} | |
} | |
+Bus { | |
*doNew { | |
|...args| | |
^Deferred().using({ | |
var obj; | |
obj = Bus(*args); | |
obj.server.sync; | |
obj; | |
}) | |
} | |
doGet { | |
var deferred = Deferred(); | |
this.get(deferred.valueCallback); | |
^deferred | |
} | |
} | |
+Buffer { | |
*doAlloc { | |
|...args| | |
^Deferred().using({ | |
var obj = Buffer.alloc(*args); | |
obj.server.sync; | |
obj; | |
}) | |
} | |
*doRead { | |
|...args| | |
^Deferred().using({ | |
var obj = Buffer.read(*args); | |
obj.server.sync; | |
obj; | |
}) | |
} | |
} | |
+Server { | |
doBoot { | |
|...args| | |
var deferred = Deferred(); | |
this.waitForBoot({ | |
deferred.value = this | |
}); | |
^deferred | |
} | |
} |
This file contains 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
// Success | |
( | |
fork { | |
d = Deferred(); | |
{ d.value = "done" }.defer(1); | |
d.wait.postln; | |
} | |
) | |
// Error | |
( | |
fork { | |
d = Deferred(); | |
{ d.error = Error("error") }.defer(1); | |
try { | |
~result = d.wait; | |
} { | |
|e| | |
"Deferred had an error: %".format(e).postln; | |
} | |
} | |
) | |
// Using w/ value | |
( | |
fork { | |
d = Deferred(); | |
d.using({ | |
1.wait; | |
"the result"; | |
}); | |
d.wait.postln; | |
} | |
) | |
// Using w/ error | |
( | |
fork { | |
d = Deferred(); | |
d.using({ | |
1.wait; | |
Error("an error").throw; | |
}) | |
try { | |
d.wait.postln; | |
} { | |
|e| | |
"Deferred had an error: %".format(e).postln; | |
} | |
} | |
) | |
// Using onComplete | |
( | |
fork { | |
d = Deferred(); | |
{ d.value = "onComplete'd"}.defer(1); | |
d.onComplete({ | |
|val| | |
val.postln; | |
}) | |
} | |
) | |
// Using onError | |
( | |
fork { | |
d = Deferred(); | |
{ d.error = Error("onError'd") }.defer(1); | |
d.onError({ | |
|err| | |
"Deferred had an error: %".format(err).postln; | |
}) | |
} | |
) | |
// Using then | |
( | |
fork { | |
d = Deferred(); | |
{ | |
if ([true, false].choose) { | |
d.value = "success'd" | |
} { | |
d.error = Error("error'd") | |
} | |
}.defer(1); | |
d.then({ | |
|val| | |
val.postln; | |
}, { | |
|err| | |
"Deferred had an error: %".format(err).postln; | |
}) | |
} | |
) | |
// Value chaining | |
( | |
fork { | |
d = Deferred(); | |
{ d.value = "chained" }.defer(1); | |
d.then({ | |
|val| | |
"got a: %".format(val).postln; | |
"apple" | |
}).then({ | |
|val| | |
"got a: %".format(val).postln; | |
"orange" | |
}).then({ | |
|val| | |
var newD; | |
"got a: %".format(val).postln; | |
newD = Deferred(); | |
{ newD.value = "deferred fruit salad" }.defer(1); | |
newD | |
}).then({ | |
|val| | |
"got a: %".format(val).postln; | |
Error("Don't like fruit salad.").throw | |
}).onError({ | |
|err| | |
"got an error: %".format(err.errorString).postln; | |
"It's okay, neither do I" | |
}).onComplete({ | |
|val| | |
val.postln | |
}); | |
} | |
) | |
// Server example | |
( | |
Server.default.doBoot.then({ | |
|s| | |
var bus, buffer; | |
buffer = Buffer.doAlloc(s, 2048).wait; | |
"buffer allocated".postln; | |
buffer.sine1([1]); | |
SynthDef(\test, { Out.ar(0, Osc.ar(buffer.bufnum, 440)) }).doAdd.wait; | |
"def added".postln; | |
Synth.doNew(\test).wait; | |
"synth sent".postln; | |
"done now".postln; | |
}) | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment