HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Basic, single-threaded implementation of SynchronizationContext

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
singlesynchronizationcontextimplementationthreadedbasic

Problem

I am trying to write a SynchronizationContext in C# that represents a message queue, to be pumped from a main loop.

Edit: I see that I have forgotten to say - I need the message loop executions to be done a single, pre-determined thread. (In my case I want to push updates to an OpenGL context.)

Here is the class I have written:

```
public class SingleThreadedSynchronizationContext : SynchronizationContext
{
private sealed class WorkItem
{
private readonly SendOrPostCallback _callback;
private readonly object _state;
private readonly ManualResetEventSlim _reset;

public WorkItem (SendOrPostCallback callback, object state, ManualResetEventSlim reset)
{
if (callback == null)
throw new ArgumentNullException ("callback");

_callback = callback;
_state = state;
_reset = reset;
}

public void Execute ()
{
_callback (_state);
if (_reset != null) {
_reset.Set ();
}
}
}

private readonly ConcurrentQueue _workItems = new ConcurrentQueue ();
private readonly Thread _executingThread;

public SingleThreadedSynchronizationContext (Thread executingThread)
{
if (executingThread == null)
throw new ArgumentNullException ("executingThread");
_executingThread = executingThread;
}

internal bool HasWorkItems {
get {
return !_workItems.IsEmpty;
}
}

private WorkItem ExecuteAndReturnNextWorkItem ()
{
WorkItem currentItem;
if (_workItems.TryDequeue (out currentItem)) {
currentItem.Execute ();
}
return currentItem;
}

private void ExecuteNextWorkItem ()
{
ExecuteAndReturnNextWorkItem ();
}

public override void Post (SendOrPostCallback d, object state)
{
_workItems.Enqueue (new WorkItem (d, state, null));
}

Solution


  • You don't need to implement Dispose pattern if you have no unmanaged resources to dispose. Unless you get paid per line of code, that is :)



-
You should not dispose objects you did not create (unless you implement a wrapper). In your case, you should not dispose reset event in WorkItem class, since you did not create it there. Instead, you should use

using(var reset = new ManualResetEventSlim())
{
    _workItems.Enqueue (new WorkItem (d, state, reset));
    ...
}


-
this code:

while (true) 
{
    synchronizationContext.Send(_ => Console.WriteLine("Hello!"), null)

    while (synchronizationContext.HasWorkItems) {
        synchronizationContext.ExecuteWorkItem ();
    }
}


will hang your application. You should either remove resetEvent from Send method and simply execute all queued WorkItems there (bad approach) or move this loop:

while (synchronizationContext.HasWorkItems) {
        synchronizationContext.ExecuteWorkItem ();
    }


to sepatrate thread (better approach). Might look like:

public sealed class CustomSynchronizationContext : SynchronizationContext, IDisposable
{
     public CustomSynchronizationContext()
     {
         _thread = new Thread(() => 
               {
                   while(true)
                   {
                       _itemQueuedEvent.WaitOne();
                       if (_disposed) return;
                       ExecuteWorkItem();
                   }
               });
         _thread.Start();
     }

 ........
}


You will probably encounter some synchronization issues, but nothing that can not be solved.

Code Snippets

using(var reset = new ManualResetEventSlim())
{
    _workItems.Enqueue (new WorkItem (d, state, reset));
    ...
}
while (true) 
{
    synchronizationContext.Send(_ => Console.WriteLine("Hello!"), null)

    while (synchronizationContext.HasWorkItems) {
        synchronizationContext.ExecuteWorkItem ();
    }
}
while (synchronizationContext.HasWorkItems) {
        synchronizationContext.ExecuteWorkItem ();
    }
public sealed class CustomSynchronizationContext : SynchronizationContext, IDisposable
{
     public CustomSynchronizationContext()
     {
         _thread = new Thread(() => 
               {
                   while(true)
                   {
                       _itemQueuedEvent.WaitOne();
                       if (_disposed) return;
                       ExecuteWorkItem();
                   }
               });
         _thread.Start();
     }

 ........
}

Context

StackExchange Code Review Q#31820, answer score: 3

Revisions (0)

No revisions yet.