|< Windows Processes & Threads: Synchronization 3 | Main | Windows Processes & Threads: Synchronization 5 >| Site Index | Download |


 

MODULE AA

PROCESSES AND THREADS: SYNCHRONIZATION

Part 4: Program Examples

 

 

My Training Period:          hours

 

Note:

..Continuation from previous Module...Program examples compiled using an empty Win32 console mode application of the Visual C++ .Net (Visual studio .Net 2003).  It is low-level programming and the .Net used is Unmanaged (/clr is not set: Project menu → your_project_name Properties… sub menu → Configuration Properties folder → General subfolder → Used Managed Extension setting set to No).  This also applied to other program examples in other Modules of tenouk.com Tutorial that mentioned “compiled using Visual C++ .Net”.  Other settings are default.  Machine’s OS is standalone Win Xp SP2.  Program examples have been tested for Non Destructive Test :o).  All information recomposed for Win 2000 (NT5.0) above.

 

WARNING

 

Wrongly modified and run the program examples presented here might collapse your Windows machine or disturb its integrity.  So for your Windows machine safety, make sure you fulfill the following conditions:

 

  1. Already read, understood and agreed to tenouk.com small disclaimer.
  2. Must be familiar with Windows OSes mainly NT5 (Windows 2000 above).
  3. Must be fluent in the following C topics: Function, Array, Pointer, String and Structure.
  4. Then, the basic knowledge of the Microsoft C Run-Time (CRT) and Win32 Programming using C.
  5. Do not mess up your system (Get a test machine! And run the program examples on the test machine).
  6. Must understand what you are going to do with the Windows APIs.
  7. Functions and structure used in the program examples were dumped at Win32 functions & structures.
  8. Other related and required information (if any) not available in no. 7 can be found at MSDN Online documentation.

 

Abilities

 

             Able to understand synchronization objects such as mutex, semaphore, event, critical section and waitable timer.

             Able to understand, use, build programs that use synchronization objects.

 

Note: For Multithreaded program examples, you have to set your project to Multithread project type.

 

Index

 

The following simple examples try to demonstrate how to use the synchronization objects:

 

          Using named objects – Mutex & Semaphore.

          Waiting for multiple objects.

          Waiting in a message loop.

          Using mutex objects.

          Using semaphore objects.

 

Using Named Objects

 

The following examples illustrate the use of object names by creating and opening named objects.

 

Mutex

 

The first process uses the CreateMutex() function to create the mutex object. Note that the function succeeds even if there is an existing object with the same name.

 

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

// One process creates the mutex object.

HANDLE hMutex;

char * MName = "MyMutex";

 

hMutex = CreateMutex(

    NULL,     // no security descriptor

    FALSE,    // mutex not owned

    MName);   // object name

 

if (hMutex == NULL)

    printf("CreateMutex(): error: %d.\n", GetLastError());

else

{

    if (GetLastError() == ERROR_ALREADY_EXISTS)

       printf("CreateMutex(): opened existing %s mutex.\n", MName);

    else

       printf("CreateMutex(): new %s mutex successfully created.\n", MName);

}

 

 return 0;

}

 

Process & Thread synchronization C program example: Mutex

 

 

The second process uses the OpenMutex() function to open a handle to the existing mutex. This function fails if a mutex object with the specified name does not exist. The access parameter requests full access to the mutex object, which is necessary for the handle to be used in any of the wait functions.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hMutex1;

 

hMutex1 = OpenMutex(

    MUTEX_ALL_ACCESS, // request full access

    FALSE,            // handle not inheritable

    MName);           // object name

 

if (hMutex1 == NULL)

printf("OpenMutex(): error: %d.\n", GetLastError());

else

       printf("OpenMutex(): %s mutex opened successfully.\n", MName);

 

return 0;

}

 

 

Process & Thread synchronization C program example: OpenMutex()

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

// One process creates the mutex object.

HANDLE hMutex;

char * MName = "MyMutex";

 

hMutex = CreateMutex(

    NULL,     // no security descriptor

    FALSE,    // mutex not owned

    MName);   // object name

 

if (hMutex == NULL)

    printf("CreateMutex(): error: %d.\n", GetLastError());

else

{

    if (GetLastError() == ERROR_ALREADY_EXISTS)

       printf("CreateMutex(): opened existing %s mutex.\n", MName);

    else

       printf("CreateMutex(): new %s mutex successfully created.\n", MName);

}

 

//=================================================================

HANDLE hMutex1;

 

hMutex1 = OpenMutex(

    MUTEX_ALL_ACCESS, // request full access

    FALSE,            // handle not inheritable

    MName);           // object name

 

if (hMutex1 == NULL)

       printf("OpenMutex(): error: %d.\n", GetLastError());

else

       printf("OpenMutex(): %s mutex opened successfully.\n", MName);

 

 return 0;

}

 

Process & Thread synchronization C program example: CreateMutex()

 

Semaphore

 

The following example uses the CreateSemaphore() function to illustrate a named-object creation operation that fails if the object already exists.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

 

HANDLE CreateNewSemaphore(LPSECURITY_ATTRIBUTES lpsa,

    LONG cInitial, LONG cMax, LPTSTR lpszName)

{

    HANDLE hSem;

 

    // Create or open a named semaphore.

    hSem = CreateSemaphore(

        lpsa,         // security attributes, NULL = handle cannot be inherited

        cInitial,     // initial count

        cMax,         // maximum count

        lpszName);    // semaphore name

 

    // Close handle, and return NULL if existing semaphore opened.

    if (hSem == NULL && GetLastError() == ERROR_ALREADY_EXISTS)

    {

        CloseHandle(hSem);

        return NULL;

    }

       else

       {

              printf("Checking the last error: %d.\n", GetLastError());

              printf("CreateNewSemaphore(): New semaphore was created successfully.\n");

       }

 

    // If new semaphore was created, return the handle.

    return hSem;

}

 

int main()

{

       LPSECURITY_ATTRIBUTES lpsa = NULL;

       LONG cInitial = 0;

       LONG cMax = 10;

       LPTSTR lpszName = "MySemaphore";

 

 

        HANDLE hSemaphore = CreateNewSemaphore(lpsa, cInitial, cMax, lpszName);

 

       return 0;

}

 

Process & Thread synchronization C program example: Semaphore

 

Waiting for Multiple Objects

 

The following example uses the CreateEvent() function to create two event objects. It then uses the WaitForMultipleObjects() function to wait for the state of one of the objects to be set to signaled.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hEvents[2];

DWORD i, dwEvent;

 

// Create two event objects.

for (i = 0; i < 2; i++)

{

    hEvents[i] = CreateEvent(

        NULL,   // no security attributes

        FALSE,  // auto-reset event object

        FALSE,  // initial state is non-signaled (suspended)

        NULL);  // unnamed object

 

    if (hEvents[i] == NULL)

    {

        printf("CreateEvent() error: %d\n", GetLastError());

        ExitProcess(0);

    }

    else

        printf("CreateEvent() for event #%d is OK.\n", i);

}

 

// The creating thread waits for other threads or processes

// to signal the event objects.

dwEvent = WaitForMultipleObjects(

    2,       // number of objects in array

    hEvents, // array of objects

    FALSE,   // wait for any object instead of all...

    4000);   // 4000 ms wait....

 

printf("Some verification, the return value is: %d.\n", dwEvent);

printf("Another verification, the last error is: %d.\n", GetLastError());

 

// Return value indicates which event is signaled.

switch (dwEvent)

{

    // hEvent[0] was signaled.

    case WAIT_OBJECT_0 + 0: // 0 + 0 = 0

printf("Event #%d is signaled.\n", dwEvent);

       // TODO: Perform tasks required by this event...

       break;

    // hEvent[1] was signaled.

    case WAIT_OBJECT_0 + 1: // 0 + 1 = 1

printf("Event #%d is signaled.\n", dwEvent);

       // TODO: Perform tasks required by this event...

       break;

             

    // Return value is invalid.

    default:

printf("Wait error: %d\n", GetLastError());

// Exit peacefully or use customize error message...

ExitProcess(0);

}

     return 0;

}

 

Process & Thread synchronization C program example: Event, CreateEvent()

 

Well our program run successfully but from the output there is error!  The WaitForMultipleObjects() returned unexpected value 258…I don’t know why :o), the GetLastError() returns is OK.  If you change the CreateEvent()’s third parameter to TRUE, the program is OK as shown in the following figure but our intention not right anymore because the event already in signaled state before calling the WaitForMultipleObjects().

 

Process & Thread synchronization C program example: Event, CreateEvent()

 

 

Another example: Obtaining Directory Change Notifications

 

An application can monitor the contents of a directory and its subdirectories by using change notifications. Waiting for a change notification is similar to having a read operation pending against a directory and, if necessary, its subdirectories. When something changes within the directory being watched, the read operation is completed. For example, an application can use these functions to update a directory listing whenever a file name within the monitored directory changes.

An application can specify a set of conditions that trigger a change notification by using the FindFirstChangeNotification() function. The conditions include changes to file names, directory names, attributes, file size, time of last write, and security. This function also returns a handle that can be waited on by using the wait functions. If the wait condition is satisfied, FindNextChangeNotification() can be used to provide a notification handle to wait on subsequent changes. The FindCloseChangeNotification() function closes the notification handle. However, these functions do not indicate the actual change that satisfied the wait condition.

To retrieve information about the specific change as part of the notification, use the ReadDirectoryChangesW() function. This function also enables you to provide a completion routine.

The following example monitors the directory tree starting at F:\ for directory name changes and F:\myproject directory for file name changes. The example uses the FindFirstChangeNotification() function to create two notification handles and the WaitForMultipleObjects() function to wait on the handles. Whenever a directory is created or deleted in the tree starting at F:\, the example updates the entire directory tree. Whenever a file is created or deleted in the F:\myproject directory, the example refreshes the directory.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main(int argc, char argv[])

{

DWORD dwWaitStatus;

HANDLE dwChangeHandles[2];

char * DirName = "F:\\myproject"; // file

char * DirName1 = "F:\\";         // directory

 

// Watch the F:\myproject directory for file creation and deletion.

dwChangeHandles[0] = FindFirstChangeNotification(

    DirName,                       // directory to watch

    FALSE,                         // do not watch the subtree

    FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes

 

if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)

       ExitProcess(GetLastError());

else

       printf("FindFirstChangeNotification() for file change is OK.\n");

 

// Watch the F:\\ subtree for directory creation and deletion.

dwChangeHandles[1] = FindFirstChangeNotification(

    DirName1,                      // directory to watch

    TRUE,                          // watch the subtree

    FILE_NOTIFY_CHANGE_DIR_NAME);  // watch directory name changes

 

if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)

    ExitProcess(GetLastError());

else

    printf("FindFirstChangeNotification() for directory change is OK.\n");

 

// Again...some messages

if (dwChangeHandles[0] != INVALID_HANDLE_VALUE && dwChangeHandles[1] != INVALID_HANDLE_VALUE)

{

       printf("\nI'm monitoring any file deletion/creation in %s and\n", DirName);

       printf("I'm monitoring any directory deletion/creation in %s.\n", DirName1);

}

 

// Change notification is set. Now wait on both notifications

// handles and refresh accordingly.

while (TRUE)

{

    // Wait for notification.

    dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE, INFINITE);

 

    switch (dwWaitStatus)

    {

        case 0: // WAIT_OBJECT_0

            // A file was created or deleted in F:\myproject.

            // Refresh this directory and restart the change notification.

            // May call application define function here...

            if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE)

                ExitProcess(GetLastError());

            else

               printf("File created/deleted in %s.\n", DirName);

            break;

 

        case 1: // WAIT_OBJECT_0 + 1

 

            // A directory was created or deleted in F:\.

            // Refresh the directory tree and restart the change notification.

            // May call application define function here...

            if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)

                ExitProcess(GetLastError());

            else

               printf("Directory was deleted/created in %s.\n", DirName1);

            break;

 

            default:

              printf("FindNextChangeNotification(): Invalid return value.\n");

              ExitProcess(GetLastError());

       }

}

 

     // May close the handles...

     // FindCloseChangeNotification(dwChangeHandles[0]);

     // FindCloseChangeNotification(dwChangeHandles[1]);

     return 0;

}

 

To see the effect, firstly run the program (in this example the program was run at command prompt).  Next, create a directory in F:\, then create a file in F:\myproject and finally delete the directory and file that just created.  The output is shown below.

 

 

Windows Xp Pro and 2000 server family Process & Thread synchronization C program example: Wait objects

 

Waiting in a Message Loop

 

The following code fragment illustrates the use of the MsgWaitForMultipleObjects() function in a message loop.  This is C++ and MFC codes.

 

int MessageLoop (

    HANDLE* lphObjects,  // handles that need to be waited on

    int     cObjects     // number of handles to wait on

  )

{

    // The message loop lasts until we get a WM_QUIT message,

    // upon which we shall return from the function.

    while (TRUE)

    {

        // block-local variable

        DWORD result;

        MSG msg;

 

        // Read all of the messages in this next loop,

        // removing each message as we read it.

        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

        {

            // If it is a quit message, exit.

            if (msg.message == WM_QUIT)

                return 1;

            // Otherwise, dispatch the message.

            DispatchMessage(&msg);

        } // End of PeekMessage while loop.

 

        // Wait for any message sent or posted to this queue

        // or for one of the passed handles be set to signaled.

        result = MsgWaitForMultipleObjects(cObjects, lphObjects,

                 FALSE, INFINITE, QS_ALLINPUT);

 

        // The result tells us the type of event we have.

        if (result == (WAIT_OBJECT_0 + cObjects))

        {

            // New messages have arrived.

            // Continue to the top of the always while loop to

            // dispatch them and resume waiting.

            continue;

        }

        else

        {

            // One of the handles became signaled.

            DoStuff (result – WAIT_OBJECT_0);

        } // End of else clause.

    } // End of the always while loop.

} // End of function.

 

Using Mutex Objects

 

You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes. Each thread must wait for ownership of the mutex before it can execute the code that accesses the shared resource. For example, if several threads share access to a database, the threads can use a mutex object to permit only one thread at a time to write to the database.  In the following example, a process uses the CreateMutex() function to create a named mutex object or open a handle to an existing mutex object.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

int main()

{

HANDLE hMutex;

 

// Create a mutex with no initial owner...

hMutex = CreateMutex(

       NULL,           // no security attributes

       FALSE,          // initially not owned

       "MutexToProtectSomeData"); // name of mutex

 

if (hMutex == NULL)

{

// Check for error...

printf("CreateMutex() failed, error: %d.\n", GetLastError());

}

else

              printf("CreateMutex() is OK.\n");

      

return 0;

}

 

Process & Thread synchronization C program example: CreateMutex()

 

When a thread of this process writes to the database, as in the next example, it first requests ownership of the mutex. If it gets ownership, the thread writes to the database and then releases its ownership.

The example uses structured exception-handling syntax to ensure that the thread properly releases the mutex object. The __finally block of code is executed no matter how the __try block terminates unless the __try block includes a call to the TerminateThread() function. This prevents the mutex object from being abandoned inadvertently.

 

// For WinXp

#define _WIN32_WINNT 0x0501

#include <windows.h>

#include <stdio.h>

 

BOOL FunctionToWriteSomeData(HANDLE hMutex)

{

    DWORD dwWaitResult;