Thread’s Creation-Windows Programming-Lecture Notes, Study notes of Windows Programming

This lecture handout is for Windows Programming course. It was provided by Prof. Jaimini Chinmay at Ambedkar University, Delhi. It includes: Thread, Creation, Synchronization, Parameter, Pointer, Variable, Null, Function, Return

Typology: Study notes

2011/2012

Uploaded on 08/07/2012

anishay
anishay 🇮🇳

4.2

(25)

118 documents

1 / 21

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Threads and Synchronization 2
26.1 Thread’s Creation
The CreateThread function creates a thread to execute within the virtual address space
of the calling process.
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
lpThreadAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines
whether the returned handle can be inherited by child processes. If lpThreadAttributes is
NULL, the handle cannot be inherited.
The lpSecurityDescriptor member of the structure specifies a security descriptor
for the new thread. If lpThreadAttributes is NULL, the thread gets a default
security descriptor. The ACLs in the default security descriptor for a thread come
from the primary or impersonation token of the creator.
dwStackSize: Initial size of the stack, in bytes. The system rounds this value to the nearest
page. If this parameter is zero, the new thread uses the default size for the executable.
lpStartAddress: Pointer to the application-defined function to be executed by the thread
and represents the starting address of the thread.
lpParameter: Pointer to a variable to be passed to the thread.
dwCreationFlags: Flags that control the creation of the thread. If the
CREATE_SUSPENDED flag is specified, the thread is created in a suspended state, and
will not run until the ResumeThread function is called. If this value is zero, the thread
runs immediately after creation.
lpThreadId: Pointer to a variable that receives the thread identifier. If this parameter is
NULL, the thread identifier is not returned.
Return value: If the function succeeds, the return value is a handle to the new thread.
If the function fails, the return value is NULL.
The number of threads a process can create is limited by the available virtual memory. By
default, every thread has one megabyte of stack space. Therefore, you can create at most
2028 threads. If you reduce the default stack size, you can create more threads. However,
docsity.com
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15

Partial preview of the text

Download Thread’s Creation-Windows Programming-Lecture Notes and more Study notes Windows Programming in PDF only on Docsity!

26.1 Thread’s Creation

The CreateThread function creates a thread to execute within the virtual address space of the calling process.

HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes , SIZE_T dwStackSize , LPTHREAD_START_ROUTINE lpStartAddress , LPVOID lpParameter , DWORD dwCreationFlags , LPDWORD lpThreadId );

lpThreadAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpThreadAttributes is NULL, the handle cannot be inherited.

The lpSecurityDescriptor member of the structure specifies a security descriptor for the new thread. If lpThreadAttributes is NULL, the thread gets a default security descriptor. The ACLs in the default security descriptor for a thread come from the primary or impersonation token of the creator.

dwStackSize: Initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is zero, the new thread uses the default size for the executable.

lpStartAddress: Pointer to the application-defined function to be executed by the thread and represents the starting address of the thread.

lpParameter: Pointer to a variable to be passed to the thread.

dwCreationFlags: Flags that control the creation of the thread. If the CREATE_SUSPENDED flag is specified, the thread is created in a suspended state, and will not run until the ResumeThread function is called. If this value is zero, the thread runs immediately after creation.

lpThreadId: Pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.

Return value: If the function succeeds, the return value is a handle to the new thread.

If the function fails, the return value is NULL.

The number of threads a process can create is limited by the available virtual memory. By default, every thread has one megabyte of stack space. Therefore, you can create at most 2028 threads. If you reduce the default stack size, you can create more threads. However,

your application will have better performance if you create one thread per processor and build queues of requests for which the application maintains the context information. A thread would process all requests in a queue before processing requests in the next queue.

The new thread handle is created with the THREAD_ALL_ACCESS access right. If a security descriptor is not provided, the handle can be used in any function that requires a thread object handle. When a security descriptor is provided, an access check is performed on all subsequent uses of the handle before access is granted. If the access check denies access, the requesting process cannot use the handle to gain access to the thread. If the thread impersonates a client, then calls CreateThread with a NULL security descriptor, the thread object created has a default security descriptor which allows access only to the impersonation token's TokenDefaultDacl owner or members.

The thread execution begins at the function specified by the lpStartAddress parameter. If this function returns, the DWORD return value is used to terminate the thread in an implicit call to the ExitThread function. Use the GetExitCodeThread function to get the thread's return value.

The thread is created with a thread priority of THREAD_PRIORITY_NORMAL. Use the GetThreadPriority and SetThreadPriority functions to get and set the priority value of a thread.

When a thread terminates, the thread object attains a signaled state, satisfying any threads that were waiting on the object.

The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle.

The ExitProcess , ExitThread , CreateThread , CreateRemoteThread functions, and a process that is starting (as the result of a call by CreateProcess ) are serialized between each other within a process. Only one of these events can happen in an address space at a time. This means that the following restrictions hold:

Do not create a thread while impersonating another user. The call will succeed, however the newly created thread will have reduced access rights to itself when calling GetCurrentThread. The access rights granted are derived from the access rights that the impersonated user has to the process. Some access rights including THREAD_SET_THREAD_TOKEN and THREAD_GET_CONTEXT may not be present, leading to unexpected failures.

  • During process startup and DLL initialization routines, new threads can be created, but they do not begin execution until DLL initialization is done for the process.
  • Only one thread in a process can be in a DLL initialization or detach routine at a time.

EnterCriticalSection(&cs); SelectObject(hDC, hBrushRectangle); Rectangle(hDC, 50, 1, rand()%300, rand()%100); GetLocalTime(&st); LeaveCriticalSection(&cs); Sleep(10); } }

26.3 Synchronization

Using threads we can use lot of shared variables. These shared variables maybe used by a single thread further more these variables may also be used and changed by several parralle threads. If there are several threads operating at the same time then a particular DC handle can be used in one of the threads only. If we want to use a single DC handle in more then one thread, we use synchronization objects. Synchronization objects prevent other threads to use the shared data at the same.

To synchronize access to a resource, use one of the synchronization objects in one of the wait functions. The state of a synchronization object is either signaled or nonsignaled. The wait functions allow a thread to block its own execution until a specified nonsignaled object is set to the signaled state.

26.3.1 Overlapped Input and Output

You can perform either synchronous or asynchronous (or overlapped) I/O operations on files, named pipes, and serial communications devices. The WriteFile , ReadFile , DeviceIoControl , WaitCommEvent , ConnectNamedPipe , and TransactNamedPipe functions can be performed either synchronously or asynchronously. The ReadFileEx and WriteFileEx functions can be performed asynchronously only.

When a function is executed synchronously, it does not return until the operation has been completed. This means that the execution of the calling thread can be blocked for an indefinite period while it waits for a time-consuming operation to finish. Functions called for overlapped operation can return immediately, even though the operation has not been completed. This enables a time-consuming I/O operation to be executed in the background while the calling thread is free to perform other tasks. For example, a single thread can perform simultaneous I/O operations on different handles, or even simultaneous read and write operations on the same handle.

To synchronize its execution with the completion of the overlapped operation, the calling thread uses the GetOverlappedResult function or one of the wait functions to determine when the overlapped operation has been completed. You can also use the HasOverlappedIoCompleted macro to poll for completion.

To cancel all pending asynchronous I/O operations, use the CancelIo function. This function only cancels operations issued by the calling thread for the specified file handle.

Overlapped operations require a file, named pipe, or communications device that was created with the FILE_FLAG_OVERLAPPED flag. To call a function to perform an overlapped operation, the calling thread must specify a pointer to an OVERLAPPED structure. If this pointer is NULL, the function return value may incorrectly indicate that the operation completed. The system sets the state of the event object to nonsignaled when a call to the I/O function returns before the operation has been completed. The system sets the state of the event object to signaled when the operation has been completed.

When a function is called to perform an overlapped operation, it is possible that the operation will be completed before the function returns. When this happens, the results are handled as if the operation had been performed synchronously. If the operation was not completed, however, the function's return value is FALSE, and the GetLastError function returns ERROR_IO_PENDING.

A thread can manage overlapped operations by either of two methods:

  • Use the GetOverlappedResult function to wait for the overlapped operation to be completed.
  • Specify a handle to the OVERLAPPED structure's manual-reset event object in one of the wait functions and then call GetOverlappedResult after the wait function returns. The GetOverlappedResult function returns the results of the completed overlapped operation, and for functions in which such information is appropriate, it reports the actual number of bytes that were transferred.

When performing multiple simultaneous overlapped operations, the calling thread must specify an OVERLAPPED structure with a different manual-reset event object for each operation. To wait for any one of the overlapped operations to be completed, the thread specifies all the manual-reset event handles as wait criteria in one of the multiple-object wait functions. The return value of the multiple-object wait function indicates which manual-reset event object was signaled, so the thread can determine which overlapped operation caused the wait operation to be completed.

If no event object is specified in the OVERLAPPED structure, the system signals the state of the file, named pipe, or communications device when the overlapped operation has been completed. Thus, you can specify these handles as synchronization objects in a wait function, though their use for this purpose can be difficult to manage. When performing simultaneous overlapped operations on the same file, named pipe, or communications device, there is no way to know which operation caused the object's state to be signaled. It is safer to use a separate event object for each overlapped operation.

A thread uses the EnterCriticalSection or TryEnterCriticalSection function to request ownership of a critical section. It uses the LeaveCriticalSection function to release ownership of a critical section. If the critical section object is currently owned by another thread, EnterCriticalSection waits indefinitely for ownership. In contrast, when a mutex object is used for mutual exclusion, the wait functions accept a specified time-out interval. The TryEnterCriticalSection function attempts to enter a critical section without blocking the calling thread.

Once a thread owns a critical section, it can make additional calls to EnterCriticalSection or TryEnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns. To release its ownership, the thread must call LeaveCriticalSection once for each time that it entered the critical section.

A thread uses the InitializeCriticalSectionAndSpinCount or SetCriticalSectionSpinCount function to specify a spin count for the critical section object. On single-processor systems, the spin count is ignored and the critical section spin count is set to 0. On multiprocessor systems, if the critical section is unavailable, the calling thread will spin dwSpinCount times before performing a wait operation on a semaphore associated with the critical section. If the critical section becomes free during the spin operation, the calling thread avoids the wait operation.

Any thread of the process can use the DeleteCriticalSection function to release the system resources that were allocated when the critical section object was initialized. After this function has been called, the critical section object can no longer be used for synchronization.

When a critical section object is owned, the only other threads affected are those waiting for ownership in a call to EnterCriticalSection. Threads that are not waiting are free to continue running.

26.4 Wait Functions

The wait functions to allow a thread to block its own execution. The wait functions do not return until the specified criteria have been met. The type of wait function determines the set of criteria used. When a wait function is called, it checks whether the wait criteria have been met. If the criteria have not been met, the calling thread enters the wait state. It uses no processor time while waiting for the criteria to be met.

There are four types of wait functions:

  • single-object
  • multiple-object
  • alertable
  • registered

Single-object Wait Functions

The SignalObjectAndWait , WaitForSingleObject , and WaitForSingleObjectEx functions require a handle to one synchronization object. These functions return when one of the following occurs:

  • The specified object is in the signaled state.
  • The time-out interval elapses. The time-out interval can be set to INFINITE to specify that the wait will not time out.

The SignalObjectAndWait function enables the calling thread to atomically set the state of an object to signaled and wait for the state of another object to be set to signaled.

Multiple-object Wait Functions

The WaitForMultipleObjects , WaitForMultipleObjectsEx , MsgWaitForMultipleObjects , and MsgWaitForMultipleObjectsEx functions enable the calling thread to specify an array containing one or more synchronization object handles. These functions return when one of the following occurs:

  • The state of any one of the specified objects is set to signaled or the states of all objects have been set to signaled. You control whether one or all of the states will be used in the function call.
  • The time-out interval elapses. The time-out interval can be set to INFINITE to specify that the wait will not time out.

The MsgWaitForMultipleObjects and MsgWaitForMultipleObjectsEx function allow you to specify input event objects in the object handle array. This is done when you specify the type of input to wait for in the thread's input queue.

For example, a thread could use MsgWaitForMultipleObjects to block its execution until the state of a specified object has been set to signaled and there is mouse input available in the thread's input queue. The thread can use the GetMessage or PeekMessage function to retrieve the input.

When waiting for the states of all objects to be set to signaled, these multiple-object functions do not modify the states of the specified objects until the states of all objects have been set signaled. For example, the state of a mutex object can be signaled, but the calling thread does not get ownership until the states of the other objects specified in the array have also been set to signaled. In the meantime, some other thread may get ownership of the mutex object, thereby setting its state to nonsignaled.

Alertable Wait Functions

The MsgWaitForMultipleObjectsEx , SignalObjectAndWait , WaitForMultipleObjectsEx, and WaitForSingleObjectEx functions differ from the

26.5 Synchronization Objects

A synchronization object is an object whose handle can be specified in one of the wait functions to coordinate the execution of multiple threads. More than one process can have a handle to the same synchronization object, making interprocess synchronization possible.

The following object types are provided exclusively for synchronization.

Type Description

Event Notifies one or more waiting threads that an event has occurred.

Mutex Can be owned by only one thread at a time, enabling threads to coordinate mutually exclusive access to a shared resource.

Semaphore Maintains a count between zero and some maximum value, limiting the number of threads that are simultaneously accessing a shared resource.

Waitable timer Notifies one or more waiting threads that a specified time has arrived.

Though available for other uses, the following objects can also be used for synchronization.

Object Description

Change notification

Created by the FindFirstChangeNotification function, its state is set to signaled when a specified type of change occurs within a specified directory or directory tree.

Console input

Created when a console is created. The handle to console input is returned by the CreateFile function when CONIN$ is specified, or by the GetStdHandle function. Its state is set to signaled when there is unread input in the console's input buffer, and set to nonsignaled when the input buffer is empty.

Job

Created by calling the CreateJobObject function. The state of a job object is set to signaled when all its processes are terminated because the specified end-of-job time limit has been exceeded.

Memory resource notification

Created by the CreateMemoryResourceNotification function. Its state is set to signaled when a specified type of change occurs within physical memory.

Process

Created by calling the CreateProcess function. Its state is set to nonsignaled while the process is running, and set to signaled when the process terminates.

Thread

Created when a new thread is created by calling the CreateProcess , CreateThread , or CreateRemoteThread function. Its state is set to nonsignaled while the thread is running, and set to signaled when the thread terminates.

In some circumstances, you can also use a file, named pipe, or communications device as a synchronization object; however, their use for this purpose is discouraged. Instead, use asynchronous I/O and wait on the event object set in the OVERLAPPED structure. It is safer to use the event object because of the confusion that can occur when multiple simultaneous overlapped operations are performed on the same file, named pipe, or communications device. In this situation, there is no way to know which operation caused the object's state to be signaled.

26.5.1 Mutex Object

The CreateMutex function creates or opens a named or unnamed mutex object.

HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes ,/null if default security attributes/ BOOL** bInitialOwner , /is the current thread is the initialize owner/ LPCTSTR** lpName /name of the named mutex object/ );

lpMutexAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpMutexAttributes is NULL, the handle cannot be inherited.

The lpSecurityDescriptor member of the structure specifies a security descriptor for the new mutex. If lpMutexAttributes is NULL, the mutex gets a default security descriptor. The ACLs in the default security descriptor for a mutex come from the primary or impersonation token of the creator.

bInitialOwner: If this value is TRUE and the caller created the mutex, the calling thread obtains initial ownership of the mutex object. Otherwise, the calling thread does not obtain ownership of the mutex.

lpName: Pointer to a null-terminated string specifying the name of the mutex object. The name is limited to MAX_PATH characters. Name comparison is case sensitive.

If lpName matches the name of an existing named mutex object, this function requests the MUTEX_ALL_ACCESS access right. In this case, the bInitialOwner parameter is ignored because it has already been set by the creating process. If the lpMutexAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security-descriptor member is ignored.

If lpName is NULL, the mutex object is created without a name.

If lpName matches the name of an existing event, semaphore, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.

  • A process can specify the mutex-object handle in a call to the DuplicateHandle function to create a duplicate handle that can be used by another process.
  • A process can specify the name of a mutex object in a call to the OpenMutex or CreateMutex function.

Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.

26.6 Thread Example Using Mutex Object

hThread1= CreateThread(NULL, 0, drawThread, (LPVOID)RECTANGLE, CREATE_SUSPENDED, &dwThread1);

hThread2 = .. .. ..

hBrushRectangle = CreateSolidBrush(RGB(170,220,160)); hBrushEllipse=CreateHatchBrush(HS_BDIAGONAL,RGB(175,180,225));

hMutex=CreateMutex(NULL, 0, NULL);

srand( (unsigned)time(NULL) ); ResumeThread(hThread2);

for(i=0; i<10000; ++i) { Switch(WaitForSingleObject(hMutex, INFINITE)) { case WAIT_OBJECT_0: SelectObject(hDC, hBrushRectangle); Rectangle(hDC, 50, 1, rand()%300, rand()%100); GetLocalTime(&st); ReleaseMutex(hMutex); Sleep(10);

};

26.7 Checking if the previous application is running

Using Named Mutex object you can check the application instance whether it is already running or not. Recreating the named mutex open the previous mutex object but set last error to ERROR_ALREADY_EXIST. You can check GetLastError if it is ERROR_ALREADY_EXIST, then it is already running.

26.8 Event Object

The CreateEvent function creates or opens a named or unnamed event object.

HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes , /null for the default security / BOOL bManualReset , /manual reset or automatically reset its state/ BOOL** bInitialState , /set initialize state signaled or unsignalled/ LPCTSTR** lpName /* nanme of the event object*/ );

lpEventAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpEventAttributes is NULL, the handle cannot be inherited.

The lpSecurityDescriptor member of the structure specifies a security descriptor for the new event. If lpEventAttributes is NULL, the event gets a default security descriptor. The ACLs in the default security descriptor for an event come from the primary or impersonation token of the creator.

bManualReset: If this parameter is TRUE, the function creates a manual-reset event object which requires use of the ResetEvent function set the state to nonsignaled. If this parameter is FALSE, the function creates an auto-reset event object, and system automatically resets the state to nonsignaled after a single waiting thread has been released.

bInitialState: If this parameter is TRUE, the initial state of the event object is signaled; otherwise, it is nonsignaled.

lpName: Pointer to a null-terminated string specifying the name of the event object. The name is limited to MAX_PATH characters. Name comparison is case sensitive.

If lpName matches the name of an existing named event object, this function requests the EVENT_ALL_ACCESS access right. In this case, the bManualReset and bInitialState parameters are ignored because they have already been set by the creating process. If the lpEventAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security-descriptor member is ignored.

If lpName is NULL, the event object is created without a name.

If lpName matches the name of an existing semaphore, mutex, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.

26.8.1 Using Event Object ( Example )

Applications use event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, overlapped I/O operations on files, named pipes, and communications devices use an event object to signal their completion.

In the following example, an application uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent function to create a manual-reset event object. The master thread sets the event object to non-signaled when it is writing to the buffer and then resets the object to signaled when it has finished writing. Then it creates several reader threads and an auto-reset event object for each thread. Each reader thread sets its event object to signaled when it is not reading from the buffer.

#define NUMTHREADS 4

HANDLE hGlobalWriteEvent;

void CreateEventsAndThreads(void) { HANDLE hReadEvents[NUMTHREADS], hThread; DWORD i, IDThread;

// Create a manual-reset event object. The master thread sets // this to nonsignaled when it writes to the shared buffer.

hGlobalWriteEvent = CreateEvent( NULL, // no security attributes TRUE, // manual-reset event TRUE, // initial state is signaled "WriteEvent" // object name );

if (hGlobalWriteEvent == NULL) { // error exit }

// Create multiple threads and an auto-reset event object // for each thread. Each thread sets its event object to // signaled when it is not reading from the shared buffer.

for(i = 1; i <= NUMTHREADS; i++) { // Create the auto-reset event. hReadEvents[i] = CreateEvent( NULL, // no security attributes FALSE, // auto-reset event TRUE, // initial state is signaled NULL); // object not named

if (hReadEvents[i] == NULL) {

// Error exit. }

hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadFunction, &hReadEvents[i], // pass event handle 0, &IDThread); if (hThread == NULL) { // Error exit. } } }

Before the master thread writes to the shared buffer, it uses the ResetEvent function to set the state of hGlobalWriteEvent (an application-defined global variable) to nonsignaled. This blocks the reader threads from starting a read operation. The master then uses the WaitForMultipleObjects function to wait for all reader threads to finish any current read operations. When WaitForMultipleObjects returns, the master thread can safely write to the buffer. After it has finished, it sets hGlobalWriteEvent and all the reader-thread events to signaled, enabling the reader threads to resume their read operations.

VOID WriteToBuffer(VOID) { DWORD dwWaitResult, i;

// Reset hGlobalWriteEvent to nonsignaled, to block readers.

if (! ResetEvent(hGlobalWriteEvent) ) { // Error exit. }

// Wait for all reading threads to finish reading.

dwWaitResult = WaitForMultipleObjects( NUMTHREADS, // number of handles in array hReadEvents, // array of read-event handles TRUE, // wait until all are signaled INFINITE); // indefinite wait

switch (dwWaitResult) { // All read-event objects were signaled. case WAIT_OBJECT_0: // Write to the shared buffer. break;

// An error occurred. default: printf("Wait error: %d\n", GetLastError()); ExitProcess(0); }

} }

26.9 Semaphore Object

The CreateSemaphore function creates or opens a named or unnamed semaphore object.

HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes , LONG lInitialCount , LONG lMaximumCount , LPCTSTR lpName );

lpSemaphoreAttributes: Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpSemaphoreAttributes is NULL, the handle cannot be inherited.

The lpSecurityDescriptor member of the structure specifies a security descriptor for the new semaphore. If lpSemaphoreAttributes is NULL, the semaphore gets a default security descriptor. The ACLs in the default security descriptor for a semaphore come from the primary or impersonation token of the creator.

lInitialCount: Initial count for the semaphore object. This value must be greater than or equal to zero and less than or equal to lMaximumCount. The state of a semaphore is signaled when its count is greater than zero and nonsignaled when it is zero. The count is decreased by one whenever a wait function releases a thread that was waiting for the semaphore. The count is increased by a specified amount by calling the ReleaseSemaphore function.

lMaximumCount: Maximum count for the semaphore object. This value must be greater than zero.

lpName: Pointer to a null-terminated string specifying the name of the semaphore object. The name is limited to MAX_PATH characters. Name comparison is case sensitive.

If lpName matches the name of an existing named semaphore object, this function requests the SEMAPHORE_ALL_ACCESS access right. In this case, the lInitialCount and lMaximumCount parameters are ignored because they have already been set by the creating process. If the lpSemaphoreAttributes parameter is not NULL, it determines whether the handle can be inherited, but its security- descriptor member is ignored.

If lpName is NULL, the semaphore object is created without a name.

If lpName matches the name of an existing event, mutex, waitable timer, job, or file-mapping object, the function fails and the GetLastError function returns

ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.

Return Values:

If the function succeeds, the return value is a handle to the semaphore object. If the named semaphore object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS.

If the function fails, the return value is NULL. To get extended error information, call GetLastError.

The handle returned by CreateSemaphore has the SEMAPHORE_ALL_ACCESS access right and can be used in any function that requires a handle to a semaphore object.

Any thread of the calling process can specify the semaphore-object handle in a call to one of the wait functions. The single-object wait functions return when the state of the specified object is signaled. The multiple-object wait functions can be instructed to return either when any one or when all of the specified objects are signaled. When a wait function returns, the waiting thread is released to continue its execution.

The state of a semaphore object is signaled when its count is greater than zero, and nonsignaled when its count is equal to zero. The lInitialCount parameter specifies the initial count. Each time a waiting thread is released because of the semaphore's signaled state, the count of the semaphore is decreased by one. Use the ReleaseSemaphore function to increment a semaphore's count by a specified amount. The count can never be less than zero or greater than the value specified in the lMaximumCount parameter.

Multiple processes can have handles of the same semaphore object, enabling use of the object for interprocess synchronization. The following object-sharing mechanisms are available:

  • A child process created by the CreateProcess function can inherit a handle to a semaphore object if the lpSemaphoreAttributes parameter of CreateSemaphore enabled inheritance.
  • A process can specify the semaphore-object handle in a call to the DuplicateHandle function to create a duplicate handle that can be used by another process.
  • A process can specify the name of a semaphore object in a call to the OpenSemaphore or CreateSemaphore function.

Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The semaphore object is destroyed when its last handle has been closed.