wait for task completion outside a task...

Every once in a while I run into this problem and though it seems simple I can't quite figure out the pattern to solve it.

In my service Start() method I want to spawn a task iterator and wait for it to complete. My current braindead approach is something along the lines of:

void Start() {
bool done=false;
SpawnIterator(initializeMethod);
while (!done) { // do nothing }
}

IEnumerator<ITask> initializeMethod() {
// do stuff
done=true;
}

Because Start() is not executing in the context of a task, I can't call yield return on initializeMethod, and afaik calling Activate() with any kind of primitive (e.g. ExecuteToCompletion or Receive on a port) will likewise run initializeMethod concurrently and continue execution of Start(). Is there a straightforward way to execute a set of Tasks outside the context of the dispatcher?

[878 byte] By [RobSim--Braintech] at [2008-2-15]
# 1

You can define a port to receive the results of the spawn method and do something based on that.

Code Snippet

protected override void Start()

{

Port<bool> successPort = new Port<bool>();

SpawnIterator(successPort, ConfigureHardware);

Activate(Arbiter.Receive(false, successPort,

delegate(bool success)

{

if (!success)

{

LogError("Service failed to start. Shutting down...");

Shutdown();

}

else

{

base.Start();

// Do something

}

}

));

}

private IEnumerator<ITask> ConfigureHardware(Port<bool> successPort)

{

bool success = false;

// Do initialization step 1

yield return (Arbiter.Choice(_initializationPort.InitHardware(),

delegate(DefaultSubmitResponseType r)

{

success = true;

},

delegate(Fault f)

{

LogError("Error occured");

}));

if (!success)

{

successPort.Post(false);

yield break;

}

success = false;

// Do initialization step 2

yield return (Arbiter.Choice(_initializationPort.CompleteInit(),

delegate(DefaultSubmitResponseType r)

{

success = true;

},

delegate(Fault f)

{

LogError("Error occured");

}));

successPort.Post(success);

yield break;

}

OmidK.Rad at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...
# 2
Thanks Omid,

Won't the Activate(Arbiter.Receive(...)) call run concurrent once it's activated? (ie, Start() will not block). I have always assumed that Activate() implies spawning a task and returning immediately but maybe I'm wrong. Does it depend on what's being activated?

Maybe I should abandon this idea of blocking from the context of Start() and put the remaining things to do inside the Receive delegate handler. It seems to me that there's no easy way to block on the completion of a task until you're inside the context of the dispatcher (ie inside an ITask). (Unless, of course, I'm wrong about Activate()).

cheers,
R

RobSim--Braintech at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...
# 3

Always both calls to SpawnIterator and Activate return immediately and the spawned handler will run concurrently. You can think of the ConfigureHardware above as an iterative Start() method and when you need to do the initialization of the service in steps one after another, you can use this pattern. You can ignore the (success == true) case in the Receive delegate and do all the work inside the spawned method, or in case of (success == true) call another SpawnIterator.

OmidK.Rad at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...
# 4

Why do you need to block in your start routine, if your init method is an iterator? cant it just yield, spawn more iterators etc? Start() can just spwn your initialize method and go away, this is common in our samples.

thanx

g

GeorgeChrysanthakopoulos at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...
# 5
correct, for the CCR to scale and be consistent, if you want to "block" for io, you have to eatiher "nest" code in the receiver delegate or yield inside an iterator
GeorgeChrysanthakopoulos at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...
# 6
It's one of those cases of having two chunks of code and wanting to merge them with a minimum of pain. Most of my init is in Start() but there was a second routine that was spawned and used the interleave to complete initialization. This was leading to race conditions with partnered services, so I removed the message passing from the spawned code and wanted to just call it as a blocking method from Start() before initializing the interleave. Now I'm leaning more towards moving everything from Start() into the spawned iterator.

I guess every once in a while I get trapped in old-school sequential programming mentality- Start() should usually just contain a sequential set of calls without any concurrency, and when I have a previously implemented ITask method I just want to call it as if it were a regular function. Coding in the context of the CCR does have the downside that the code becomes less portable, since everything is wrapped in ITasks. That said, if I start enforcing that my Start() method is always simply { SpawnIterator(initializeMethod); } then I'll never have reason to complain about it. :-)

RobSim--Braintech at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...
# 7

One more thing: in the above sample it is better to put the call to base.Start() inside the success path when all the initialization is completed. This allows activation of service handlers when the service is ready to use. I modified the code to show this.

OmidK.Rad at 2007-10-2 > top of Msdn Tech,Microsoft Robotics Studio,Microsoft Robotics - Concurrency and Coordination Runtime (CCR)...

Microsoft Robotics Studio

Site Classified