Last active
November 25, 2017 20:36
-
-
Save shanecelis/e5d76ead850df257f11a679920a5d851 to your computer and use it in GitHub Desktop.
Substitute a function deep in the belly of the beast for this proxy when you need to.
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
/* Original code Copyright (c) 2017 Shane Celis[1] | |
Licensed under the MIT License[2] | |
Original code posted here[3]. | |
This comment generated by code-cite[4]. | |
[1]: https://github.com/shanecelis | |
[2]: https://opensource.org/licenses/MIT | |
[3]: https://gist.github.com/shanecelis/e5d76ead850df257f11a679920a5d851 | |
[4]: https://github.com/shanecelis/code-cite | |
*/ | |
using System; | |
using System.Threading; | |
using System.Collections.Concurrent; | |
/** | |
How many times has this happened to you? You're using a library that provides | |
hooks to do something, let's say, optimize a function's value. The library | |
wants a function, and if your function has one or more of the following | |
properties you're golden. | |
* Your function is pure. | |
* Your function can compute the needed value within its stack frame. | |
* This value is available on demand. | |
But maybe your function isn't pure and you _can't_ stop the world to | |
compute its value. Some times you need to wait for something to happen | |
without halting the main thread. For instance, you might need to let the | |
physics engine run for a few ticks. | |
Sometimes the library calling this function isn't so deeply embedded that you | |
can't just break the library's execution in two: 1. before function | |
evaluation and 2. after function evaluation. It's not the prettiest solution, | |
and it leaks a lot of internals, but it puts you in the drivers seat. | |
If you'd like to maintain the integrity of the library's API and work around | |
this limitation, you can use this little class: QueueProxy. The purpose of | |
this class is to allow you to stick a proxy in your function's stead. Run the | |
library in another thread, and its inputs will be available to some worker | |
thread. | |
``` | |
// Proxy for a function that accepts a int[] and returns a double. | |
var proxy = new QueueProxy<int[], double>(); | |
// Run the optimization algorithm in another thread. | |
Task.Run(() => { | |
optimizer.SetEvaluator((int[] genotype) => proxy.EnqueueAndWait(genotype)); | |
optimizer.Run(); | |
}); | |
``` | |
Then in some way that's conducive to not blocking the world, you can do the | |
work you need to do elsewhere unconstrained by the libraries needs. Read the | |
input. | |
``` | |
int[] genotype; | |
if (proxy.input.TryDequeue(out genotype)) { | |
// Instantiate thing. | |
phenotype = CreatePhenotype(genotype); | |
} | |
``` | |
And write the output when you can. | |
``` | |
if (physics.ticks > 100) { | |
double result = phenotype.position.x; | |
proxy.output.Enqueue(result); | |
} | |
``` | |
Note: This doesn't totally decouple the serial nature of the optimization | |
algorithm, and it in fact relies on it. The optimization, if single | |
threaded, will block waiting for a result. | |
*/ | |
public class QueueProxy<TInput, TOutput> { | |
public ConcurrentQueue<TInput> inputs = new ConcurrentQueue<TInput>(); | |
public ConcurrentQueue<TOutput> outputs = new ConcurrentQueue<TOutput>(); | |
// XXX One could also throw exceptions through the proxy. | |
// public ConcurrentQueue<Exception> error; | |
public TOutput EnqueueAndWait(TInput input) { | |
inputs.Enqueue(input); | |
TOutput result; | |
while (! outputs.TryDequeue(out result)) | |
Thread.Sleep(100); | |
return result; | |
} | |
public bool TryRespond(Func<TInput, TOutput> f) { | |
TInput input; | |
if (inputs.TryDequeue(out input)) { | |
outputs.Enqueue(f(input)); | |
return true; | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment