|< 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:
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;
}

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;
}

// 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;
}

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;
}

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;
}

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().

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.

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;
}

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;