How to safely dispose CCR Dispatcher, DispatcherQueue and Interleave

Dec 17, 2012 at 12:20 AM
Edited Dec 17, 2012 at 12:39 AM

Dear All,

I am trying to find the best way to dispose the CCR Dispatcher, DispatcherQueue and Interleave which are interlinked to each other. I have a class say "MyClass" that has a single dispatcher and a single dispatcher queue. The class exposes a single PortSet to which clients can post messages to. In "MyClass", I have created persistent receivers for those messages and attached to the dispatcher queue. I have also added all the receivers as part of a single Interleave. Now when the client thinks it is done using "MyClass" class, I want the client to destroy the class safely. There are three things that needs to destroy here which are the dispatcher, the dispatcher queue and the interleave. What is the best way to do that? I happened to read the discussions in the link http://channel9.msdn.com/shows/Going+Deep/CCR-Programming-Jeffrey-Richter-and-George-Chrysanthakopoulos/. Although, not mentioned explicitly, I inferred that the right way to Dispose is that I need to first post a tear down message to the interleave, wait for the interleave to teardown then dispose the dispatcher queue. Now, the dispose code of my class will look like the following.

void Dispose()
{
var teardownInterleave = new TeardownInterleave();
InternalMessagesPort.PostUnknownType(teardownInterleave);
var done = new ManualResetEvent(false);
Activate(Arbiter.Receive(false, teardownInterleave.CompletionPort,
    emptyValue => done.Set()));
done.WaitOne();
Thread.Sleep(100);

// Dispose the TaskQ
TaskQueue.Dispose();
}
/// <summary>
/// Message posted to any interleave asking it to teardown.
/// </summary>
public sealed class TeardownInterleave
{
    /// <summary>
    /// Gets the completion port.
    /// </summary>
    /// <value>
    /// The completion port.
    /// </value>
    public Port<EmptyValue> CompletionPort { get; private set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="TeardownInterleave" /> class.
    /// </summary>
    public TeardownInterleave()
    {
        CompletionPort = new Port<EmptyValue>();
    }
}
Please clarify if this is the right approach or am I missing something.

Thanks,

Venkat

Coordinator
Dec 17, 2012 at 10:04 AM

Hi Venkat,

If you want to stop the interleave manually, then yes I think that this would be the right way to do this. The question is, if it is really necessary to call teardown. It would be necessary if

  1. any ressources would be held by the interleave that cannot be garbage collected unless you send the teardown message, or if
  2. you want to prevent that any messages that are being posted in the future are going to be processed - I assume this is not necessary in your case, since you dispose the dispatcher queue directly afterwards.

So we only have to deal with question 1. When I started working with the CCR, I was wondering if it was necessary to dispose ports that have receivers attached. I did a little deep dive into the CCR code, and found out that they did a pretty nice job handling references so that you actually don't need to dispose anything (except Dispatcher and DispatcherQueue of course). From how I understand the logic of the interleave, it works like this:

  • When the interleave is created, the first question is where the instance of the Interleave class is stored. Like the with receiver tasks, only the participating ports themselves hold an instance of the interleave (as an ArbiterTask). Additionally, the information if a port is a concurrent or exclusive port is stored in the ArbiterContext property of the port's ReceiverTask.
  • When an item is posted, the port calls the interleave, which looks up if the task should be concurrent or exclusive. The interleave holds counters for the currently running tasks - if concurrent tasks are running and the new task is also concurrent, it is enqueued at the dispatcher queue. If the new task is exclusive, no more concurrent tasks will be started, and the exlusive task will be started as soon as all concurrent tasks are finished.
  • A reason for needing to manually dispose the interleave would be if there was a running execution loop that needed to be stopped - but there isn't. So, how does the interleave start a waiting exclusive task as soon as the concurrent tasks are finished? It installs an ArbiterCleanupHandler to every task, which ensures that the interleave is automatically called after a running task is finished. With that it can again manage the task counters, and execute waiting tasks.
  • Finally, an interesting question is of course what's actually done when a message is sent to the teardown port: This causes all receivers to be unregistered from all ports, and all pending items to be cleaned up. After everything is cleaned up, the teardown task is executed. This is nice if you want to ensure that no more messages are being processed, but otherwise not directly necessary.
  • So how do references stick together:
    • Each port holds a reference to its receiver task
    • Each port also holds a reference to the interleave
    • The interleave holds a reference to the dispatcher queue
    • No reference to the interleave is held anywhere else!!
    • The interleave holds no resources that actively need to be disposed!!

From all this, I conclude that as soon as there are no more references to the participating ports, the receivers and the interleave can also safely be garbage collected. By the way, the appspace also relies on this :-)
I hope this was helpful and understandable!

Best Regards
Thomas