0 Comments

I was having some problems with Caliburn.Micro’s coroutines when working with a new Windows Phone 7 project. The execution of the coroutine stopped in the middle of its process, and where I should have seen a popup dialog, I didn’t see anything. The execution just stopped. Turns out, this was a threading issue. I didn’t realize that after the yield returnstatement, the execution continues from the thread where the previous task has completed.

For example, here’s a method hooked up to a button:

        public IEnumerable<IResult> Run()
        {
            Debug.WriteLine("Current thread: {0}", Thread.CurrentThread.ManagedThreadId);

            yield return new MyService();

            Debug.WriteLine("Current thread: {0}", Thread.CurrentThread.ManagedThreadId);

            yield return new MyService();

            Debug.WriteLine("Current thread: {0}", Thread.CurrentThread.ManagedThreadId);
        }

And here’s the MyService-implementation:

    public class MyService : IResult
    {
        public void Execute(ActionExecutionContext context)
        {
            ThreadPool.QueueUserWorkItem(x => RunLongRunningProcess());
        }

        private void RunLongRunningProcess()
        {
            Thread.Sleep(1000);

            Completed(this, new ResultCompletionEventArgs());
        }

        public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
    }

MyService executes a long running operation so it starts a new thread. In real world it’s frequent that your IResult-class wraps a service which does some threading for you. Now, when the Run-method is executed, the output looks like this:

Current thread: 258736214

Current thread: 252444786

Current thread: 255262834

Because we raise the Completed-event in a new thread every time, the Run-method will continue from that same thread. In my problematic case, I started the coroutine from a UI-thread and expected to stay on it until the end. But somewhere in the middle I  started some task with a new thread, causing my next execution of ShowDialog to fail because I wasn’t in the UI-thread anymore.

You can easily fix this situation by some thread marshaling. Instead of just raising the Completed-event, raise it in the UI-thread:

        private void RunLongRunningProcess()
        {
            Thread.Sleep(1000);

            Caliburn.Micro.Execute.OnUIThread( () => Completed(this, new ResultCompletionEventArgs()));
        }

With this small change, our Run-method stays in the UI-thread until end. Here’s the output:

Current thread: 235012186

Current thread: 235012186

Current thread: 235012186