Skip to content

Instantly share code, notes, and snippets.

@scztt
Last active October 1, 2015 05:41
Show Gist options
  • Save scztt/20f82ab05a6a11fc3a05 to your computer and use it in GitHub Desktop.
Save scztt/20f82ab05a6a11fc3a05 to your computer and use it in GitHub Desktop.
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
}
}
// 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