EDIT: It kind of occurred to me too late (?) that all the code I posted in my first update to this question was way too much for most readers. I've actually gone ahead and written a blog post about this topic for anyone who cares to read it.
In the meantime, I've left the original question in place, to give a brief glimpse at the problem I'd like to solve.
I'll also just note that the code I have posted (on my blog) has, thus far, stood up pretty well to testing. But I'm still interested in any and all feedback people are willing to give me on how clean/robust/performant* it is.
*I love how that word doesn't really mean what we think, but we developers use it all the time anyway.
Original Question
Sorry for the vague question title -- not sure how to encapsulate what I'm asking below succinctly. (If someone with editing privileges can think of a more descriptive title, feel free to change it.)
The behavior I need is this. I am envisioning a worker class that accepts a single delegate task in its constructor (for simplicity, I would make it immutable -- no more tasks can be added after instantiation). I'll call this task T. The class should have a simple method, something like GetToWork, that will exhibit this behavior:
- If the worker is not currently running
T, then it will start doing so right now. - If the worker is currently running
T, then once it is finished, it will startTagain immediately. GetToWorkcan be called any number of times while the worker is runningT; the simple rule is that, during any execution ofT, ifGetToWorkwas called at least once,Twill run again upon completion (and then ifGetToWorkis called whileTis running that time, it will repeat itself again, etc.).
Now, this is pretty straightforward with a boolean switch. But this class needs to be thread-safe, by which I mean, steps 1 and 2 above need to comprise atomic operations (at least I think they do).
There is an added layer of complexity. I have need of a "worker chain" class that will consist of many of these workers linked together. As soon as the first worker completes, it essentially calls GetToWork on the worker after it; meanwhile, if its own GetToWork has been called, it restarts itself as well. Logically calling GetToWork on the chain is essentially the same as calling GetToWork on the first worker in the chain (I would fully intend that the chain's workers not be publicly accessible).
One way to imagine how this hypothetical "worker chain" would behave is by comparing it to a team in a relay race. Suppose there are four runners, W1 through W4, and let the chain be called C. If I call C.StartWork(), what should happen is this:
- If
W1is at his starting point (i.e., doing nothing), he will start running towardsW2. - If
W1is already running towardsW2(i.e., executing his task), then once he reachesW2, he will signal toW2to get started, immediately return to his starting point and, sinceStartWorkhas been called, start running towardsW2again. - When
W1reachesW2's starting point, he'll immediately return to his own starting point.- If
W2is just sitting around, he'll start running immediately towardsW3. - If
W2is already off running towardsW3, thenW2will simply go again once he's reachedW3and returned to his starting point.
- If
The above is probably a little convoluted and written out poorly. But hopefully you get the basic idea. Obviously, these workers will be running on their own threads.
Also, I guess it's possible this functionality already exists somewhere? If that's the case, definitely let me know!