Click here to Skip to main content
15,881,757 members
Articles / Mobile Apps / Windows Mobile

Windows Mobile Native Thread Synchronization for .NET

Rate me:
Please Sign up or sign in to vote.
4.07/5 (8 votes)
3 Nov 2008CPOL11 min read 37.3K   313   41   2
An article introducting the native synchronization objects suppied by Windows Mobile and explanation of how to use them.

Introduction

In the MSDN forums for Smart Device development, I have come across a few requests for guidance for which native synchronization objects would be part of the solution. Having encountered the questions on more than one occasion, I've written this introduction to Native Events on Windows Mobile devices. This paper is specifically written for managed code developers, though a native code developer should not experience challenges in interpreting the code examples in this article.

What is a Synchronization Object and Why do I Need Them?

Synchronization objects are used to coordinate operations being performed by more than one thread. In their simplest form, a synchronization object would be used to ensure that multiple threads are not altering the same object simultaneously, though they can also be used to facilitate communication among programs. The .NET Compact Framework contains a number of managed classes that can be used for thread level synchronization, these classes can be found in the Threading namespace.

ClassFramework Version
Interlocked2.0
ManualResetEvent1.0
Monitor1.0
WaitHandle1.0
EventWaitHandle1.0

These classes work within the context of the same process, and are for coordinating the actions of threads within that process. Most of the Operating System supplied functionality that I discuss here works across process boundaries. I won't be discussing the managed synchronization classes here, only the native synchronization functions. At the time of this writing, the Operating System supplied synchronization objects are not directly supported by the .NET framework, so we will need to use P/Invoke to access them. I will be covering the following objects:

ObjectDescription
EventUsed to send signals to threads to continue execution
SemaphoreLimits the number of threads that can access a resource
MutexUsed to ensure that only one thread is accessing a resource at a time

I've wrapped the native function calls for the native synchronization objects into separate classes. The abstract class SyncBase contains the functionality that is common to all three of the synchronization objects defined here. Each one of the three synchronization classes derives from this abstract class. Take note that these classes implement IDisposable. These classes hold onto Windows handles (which are system resources), and these handles should not be held longer than necessary.

Image 1

While you are free to use this code as you like, I would highly encourage you to study the documentation in MSDN for the synchronization functions. My use of the available functions is non-exhaustive, and targets what I think to be the most commonly used functionality. So, there are other capabilities that the synchronization functions have that I do not mention here.

Events

Events are the simplest of the synchronization objects. They are like traffic signals; code that waits on an event will halt until the event changes to a signaled state. An event can optionally have a name. An event with a name can be shared among several processes. Since there is no native support in the .NET Framework for system events, we will need to use P/Invoke to interact with them. The CreateEvent function is used to create events, and it is defined in CoreDLL. An event can be a manual reset event or an auto-reset event. For a manual reset event, once the event is set to a signaled state, it will remain in that state until it is explicitly changed to a non-signaled state using the ResetEvent function. While the event is in a signaled state, every thread that attempts to wait on the event will continue to run without being blocked. For an auto-reset, once an event is set to signaled state, it will remain in that state until a thread waits on it. Once a thread waits, it will be allowed to continue, and the event will be automatically reset back to a non-signaled state. For momentarily setting an event to a signaled state, there is a function named PulseEvent. The behaviour of PulseEvent is dependent on whether it is being used against a manual reset or auto-reset event. When called against a manual reset event, all threads which can currently be unblocked are unblocked, and the event changes back to a non-signaled state. For auto-reset events, a single thread is released from being blocked, and the event then returns to a non-signaled state.

The calling signature for CreateEvent is defined in the C++ header as follows:

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES
  lpEventAttributes,
  BOOL bManualReset,
  BOOL InitialState,
  LPTSTR lpName
);
ParameterDescription
LPSECURITY_ATTRIBUTESThis should always be null.
bManualResetControls whether a manual reset or auto-reset event is created.
InitialStateSpecifies whether or not the event will be in a signaled state when it is created.
lpNameThe name of the event to create. If no name is specified, then an unnamed event is created, and the event will only have meaning within the process in which it was created.

Calling it will require the use of a P/Invoke:

C#
[DllImport("CoreDLL", SetLastError=true)]
public static extern IntPtr CreateEvent(
   IntPtr AlwaysNull0, 
   [In, MarshalAs(UnmanagedType.Bool)]  bool ManualReset, 
   [In, MarshalAs(UnmanagedType.Bool)]  bool bInitialState,
   [In, MarshalAs(UnmanagedType.BStr)]  string Name
);

If you attempt to make the appropriate P/Invoke statements for SetEvent, PulseEvent, and ResetEvent, they will all fail. They fail because none of these functions exist in any Windows Mobile DLL. If you look in the C++ header file named "kfuncs.h", you will see that these functions are defined as inline calls to EventModify. The first parameter in the call to EventModify is the handle to the event that we are using, and the second parameter is a numeric value that indicates what is to be done with the event. In my code, I made a P/Invoke statement for EventModify, and declared appropriate calls to it for SetEvent, ResetEvent, and PulseEvent.

You almost have enough information to begin using events. The one last missing piece is what you must do with an event handle when you are no longer using it. When you no longer need an event, release it by calling CloseHandle. Now, on to our first code example. I've already declared the P/Invoke statements that we will be using to access the functions mentioned above, in a single project called NativeSync. If you open CoreDLL.cs, you will find P/Invokes for CreateEvent, EventModify, and CloseHandle. I've also created a class named SystemEvent to package the event related functions. The class implements IDisposable so that the handle can be released when the event is no longer being used.

The first code example is in a project named "SoundOnEvent". The program has two threads. In addition to the GUI thread, there is a thread with an entry point in the method SoundLoop whose definition is given below. SoundLoop will wait on an event and play a sound when the event is signaled. The form in which this program is hosted only has a single button. The event handler for that button calls SetEvent on another event object. Note that the instance of the Event object being used by the button's event handler is different than the instance in the SoundLoop method. Since both instances were created with the same name, the Operating System will give both of these object instances the same kernel object.

C#
SystemEvent _soundEvent;

private void Form1_Load(object sender, EventArgs e)
{
    _soundEvent = new SystemEvent("PlaySound", false, false);
    Thread t = new Thread(new ThreadStart(SoundLoop));
    t.Start();
}
 
void SoundLoop()
{
 using (SystemEvent evt = new SystemEvent("PlaySound", false, false))
 {
     while (_continuePlaying)
     {
         evt.Wait();
         if (_continuePlaying)
         {
             SndPlaySync(SoundPath, 0);
         }
     }
 }
}

private void cmdCreateEvent_Click(object sender, EventArgs e)
{
   _soundEvent.SetEvent();
}
Source code from program SoundOnEvent

The while loop looks notoriously similar to an antipattern that is frequently discouraged, known as a busy spin or spinning on a variable. Although the code looks like a busy spin, it is not; in a busy spin, a code block is unnecessarily repeated, performing no real work while burning CPU cycles. In the above code, the thread completely halts until it is time to do work, and doesn't waste CPU cycles.

While a handle to one event can't be directly passed to another process (a handle only has meaning within the context of a specific process), if another process attempts to make an event with the same name, it will receive its own handle to the already existing object. If we had another program that called SetEvent for this same event, this first program would react to it ( no code modifications needed!).

C#
using System;

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using NativeSync;

namespace SoundOnEventTrigger
{
    public partial class Form1 : Form
    {
        SystemEvent _soundEvent = new SystemEvent("PlaySound", false, false);

        public Form1()
        {
            InitializeComponent();
        }

        private void cmdCreateEvent_Click(object sender, EventArgs e)
        {
            _soundEvent.SetEvent();
        }

        private void miExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void Form1_Closing(object sender, CancelEventArgs e)
        {
            _soundEvent.Dispose();
        }
    }
}
Source code for SoundOnEventTrigger, which sends events to SoundOnEvent

Waiting on Multiple Objects

When you have a collection of objects and would like to wait for any one of them to be signaled, the method WaitForMultipleObjects can be used. On the desktop version of Windows, this function can be used to wait for all events to be signaled, or wait for any one of them to be signaled. For the Windows CE Operating System, this function can only be used to wait on one of any events to occur. The native prototype for the function is as follows:

C++
DWORD WINAPI WaitForMultipleObjects(
  __in  DWORD nCount,
  __in  const HANDLE *lpHandles,
  __in  BOOL bWaitAll,
  __in  DWORD dwMilliseconds
);

The first parameter is the number of synchronization objects being passed, and the second parameter is an array of object handles. The third parameter must always be false (on the desktop OS, if the third parameter were true, then the method would wait on all of the objects to be signaled). The last parameter will be the number of milliseconds to wait for one of the objects to be signaled, or -1 for no timeout. In my code, I have simplified the calling signature to only take a timeout value and the list of synchronization objects on which to wait. The method is implemented on the SyncBase class, since the system event is not the only type of object on which a program can wait (we will cover the other types of objects shortly).

The example code demonstrating WaitForMultipleObjects is similar to the first example. For my wrapper to the WaitOnMultipleObjects function, I've had the method return a reference to the object that was signaled and caused the thread to be unblocked. The example program will use information on which event was signaled to decide which sound to play. A user can signal one of the three system event objects through three buttons on the interface. There is also a fourth event which is not accessible through a button on this program's UI. The fourth event has the same name as the event used on the first example program, so the SoundOnEventTrigger can be used to trigger the fourth event.

C#
void SoundLoop()
{
    using (SystemEvent evt = 
           new SystemEvent("PlaySound", false, false))
    {
        int target;
        while (_continuePlaying)
        {
            SystemEvent signaledEvent =  SyncBase.WaitForMultipleObjects(
                                                _event1, 
                                                _event2, 
                                                _event3,
                                                _playSoundEvent
                                         ) as SystemEvent;
            if (_continuePlaying)
            {
                target = 4;
                if (signaledEvent == _event1)
                    target = 1;
                if (signaledEvent == _event2)
                    target = 2;
                if (signaledEvent == _event3)
                    target = 3;
                SndPlaySync(String.Format(SoundPath, target), 0);
            }
        }
    }
}

Once you understand how to use the event object, you have a foundation for using mutexes and semaphores; they both extend on the concept of the event.

Mutex

A mutex is used to ensure that only one thread has access to a resource. A mutex is in a signaled state until a thread waits on it. When a thread waits on a mutex and if no other threads are waiting on the mutex, then it is granted ownership of the mutex, its wait function returns, and the mutex goes to an unsignaled state to ensure that no other threads waiting on the same semaphore can continue. As subsequent threads attempt to wait on the mutex object, they will be blocked. When a thread is done using a mutex, it can call ReleaseMutex, and the next blocked thread is unblocked and granted ownership of the mutex. The program that illustrates the use of the matrix has four threads that do nothing more than count. Each thread's count is displayed in a different textbox.

Image 2

The source code that each one of the four threads executes is given below. I've inserted a call to Sleep into the code to cause it to run longer so that you can more easily observe the user interface as the threads execute.

C#
void StartCounting()
{
    int index = Interlocked.Increment(ref lastIndex);
    using (NativeSync.Mutex m = new NativeSync.Mutex(MutexName,false))
    {
        mutex.Wait();
        for (int i = 0; (i < 50) && (_continueRunning); ++i)
        {
            SetTextCount(index, i);
            Thread.Sleep(100);
        }
        mutex.ReleaseMutex();
    }
}

When the code is run, only one of the counters executes at a time. Once it completes its work, the next counter will begin since the mutex will only allow one thread into the counting loop at a time.

Semaphores

Semaphores are similar to mutexes. It also limits the number of threads that can access a resource. Unlike the mutex, you can use semaphores to allow more than one thread to access a resource. A semaphore has a count attribute. The count can be 0 or greater, up to a maximum amount that is decided when the semaphore is created. When the count is greater than zero, the semaphore is in a signaled state. When a thread waits on a semaphore, if the semaphore is in a signaled state (which would mean the count is greater than zero), then the count is decremented. When the thread is done using the resource, it should call ReleaseSemaphore to increment the count.

To demonstrate the use of a semaphore, I've taken the same program that waits on a mutex and modified it to wait on a semaphore instead. The semaphore used in this code example allows up to two threads to access it. So, when this program is run, you will see two counters working at a time.

What's next

The Windows Mobile Operating System offers a lot more synchronization functionality than described here. I only wanted to concentrate on events and their derivatives in this paper. The synchronization functions also support interprocess communication, monitoring a process for termination, and many other items of functionality. (See the MSDN documentation.)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
I attended Southern Polytechnic State University and earned a Bachelors of Science in Computer Science and later returned to earn a Masters of Science in Software Engineering. I've largely developed solutions that are based on a mix of Microsoft technologies with open source technologies mixed in. I've got an interest in astronomy and you'll see that interest overflow into some of my code project articles from time to time.



Twitter:@j2inet

Instagram: j2inet


Comments and Discussions

 
QuestionImage problem Pin
dvign723-Nov-08 21:20
professionaldvign723-Nov-08 21:20 
GeneralRe: Image problem Pin
Joel Ivory Johnson4-Nov-08 2:55
professionalJoel Ivory Johnson4-Nov-08 2:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.