/** * semaphore.cpp * * This is an example of how to use a semaphore to regulate access to * a resource. Semaphores allow for limiting concurrent access. * * This example demonstrates the use of: * CreateSemaphore * ReleaseSemaphore * WaitForSingleObject * WaitForMultipleObjects * CreateThread * SetEvent * GetCurrentThreadId * CloseHandle * GetLastError * * PRE-REQUISITE: need to run the file_create example to produce the * file(s) needed for access. It is assumed that the file(s) already * exist. * * The example extends the file_create, signal_event, simple_thread * examples. * * The Main function spawns a number of threads. Each thread will * access a file on disk and print the first record to standard out. * Each thread will identify its output by prefixing the output with * its thread id. * * There will be spawned twice as many threads than the count * associated with the semaphore. This will demonstrate that only * the allowed number of threads will be able to access the resource * (the file) at one time. * * To build using Visual Studio: * create a new Win32 Console project * replace the main project file with this file. * * You can use the examples and the source code any way you want, but * please include a reference to where it comes from if you use it in * your own products or services. Also note that this software is * provided by the author "as is", with no expressed or implied * warranties. * In no event shall the author be liable for any direct or indirect * damages arising in any way out of the use of this software. * * Copyright 2006 www.codebound.com * */ #include "stdafx.h" #include <windows.h> #include <process.h> #include <stdlib.h> #include <iostream> #include <string> using namespace std; using std::string; /* change this to increase the number of threads to read concurrently */ const long SEMAPHORE_COUNT = 0x00000003; /* change this to increase or decrease the number of files to read */ const long NBR_OF_FILES = 0x00000003; /* change this to increase or decrease the number of threads to spawn */ const long MAX_THREADS = 0x00000006; /* NOTE: this base name MUST match the BASE_NAME in the file_create example */ const char BASE_NAME[] = "base_name_"; /* NOTE: this BUF_SIZE shoud match the BUF_SIZE in the file_create example */ const long BUF_SIZE = 0x00000064; /* do NOT change this constant */ const long LTOA_BUF_SIZE = 0x00000020; /* handles */ HANDLE event_handle; HANDLE semaphore_handle; /** * static_func() * this is a static function that hosts the spawned thread. * the thread waits on a signal before begining work. * the thread will open a file and read the first record of the * file. the thread will prefix the output of the first record * with this threads id. */ DWORD WINAPI static_func(LPVOID pArg) { DWORD wrc; char buf[BUF_SIZE]; /* * get the argument passed in, and set our local variable */ int local_count = *(static_cast < int * > (pArg)); /* * wait on the signal from the parent to begin. */ wrc = WaitForSingleObject(event_handle, INFINITE); /* wait forever */ /* * if the wait failed, terminate the thread. */ if (wrc != WAIT_OBJECT_0) { DWORD err = GetLastError(); printf("!!! static_func(%d)::WaitForSingleObject failed: %x/n", GetCurrentThreadId(), err); return (1); } /* set the base of the file(s) name */ string base_name = BASE_NAME; /* * do file processing w/ in the constraints of the semaphore. */ for (int i = 0; i < local_count; ++i) { /* * get the semaphore */ wrc = WaitForSingleObject(semaphore_handle, INFINITE); /* wait forever */ /* * if the wait failed, exit the thread. */ if (wrc != WAIT_OBJECT_0) { DWORD err = GetLastError(); printf( "!!! static_func(%d)::WaitForSingleObject (semaphore) failed: %x/n", GetCurrentThreadId(), err); return (1); } /* open the file */ FILE *fh; errno_t err; char c[LTOA_BUF_SIZE]; _ltoa_s(i, c, (LTOA_BUF_SIZE - 1), 10); /* setup the filename, and concatenate the number */ string filename = base_name; filename += c; /* open the file, if error display message and exit */ if ((err = fopen_s(&fh, filename.c_str(), "r")) != 0) { printf("static_func(%d) error opening(%s)/n", GetCurrentThreadId(), filename.c_str()); } /* if we opened ok, then read the file and close */ if (!err) { /* zero out the buffer */ memset(buf, 0, BUF_SIZE); /* read the first record */ fread(buf, sizeof(char), (BUF_SIZE - 1), fh); /* close the file */ fclose(fh); /* print the record (truncated) with the prefix of this thread's id */ printf("(%d): %.40s/n", GetCurrentThreadId(), buf); } /* * release the semaphore. increase count by 1 */ if (!ReleaseSemaphore(semaphore_handle, 1, 0)) /* ptr to previous count */ { DWORD err = GetLastError(); printf("!!! static_func(%d)::ReleaseSemaphore failed:%d /n", GetCurrentThreadId(), err); return (1); } } return (0); } /* static_func() */ /** * main() * test for the simple thread example */ int main(int argc, char **argv) { DWORD nbr_of_handles = 0; DWORD count = NBR_OF_FILES; long semaphore_count = SEMAPHORE_COUNT; DWORD thread_id[MAX_THREADS] = {} ; HANDLE thread_handles[MAX_THREADS] = {} ; /* * create the event used to signal the threads to begin work * for the CreateEvent function the arguments are respectively: * 0 - security attributes * true - true for manual reset, false for auto-reset * false - initial state, false for non-signaled * 0 - name, if named event */ event_handle = CreateEvent(0, true, false, 0); if (!event_handle) { DWORD err = GetLastError(); printf("!!! main()::CreateEvent failed:%d /n", err); return (1); } /* * Create the semaphore with desired concurrency. * for the CreateSemaphore function the arguments are respectively: * 0 - security attributes * semaphore_count -initial count * semaphore_count - max count * 0 - name, if named semaphore */ semaphore_handle = CreateSemaphore(0, semaphore_count, semaphore_count, 0); if (!semaphore_handle) { DWORD err = GetLastError(); printf("!!! main()::CreateSemaphore failed: %x/n", GetLastError()); return (1); } /* * spawn the thread */ printf("main(): spawning thread/n"); for (int i = 0; i < MAX_THREADS; ++i) { /* * for the CreateThread function the arguments are respectively: * 0 - Security Attributes * 0 - Stack Size * static_func - thread function * &count - argument to pass to thread function * 0 - flags * &thread_id[nbr_of_handles] - thread id returned */ thread_handles[nbr_of_handles] = CreateThread(0, 0, static_func, &count, 0, &thread_id[nbr_of_handles]); /* Check the return value for success */ if (thread_handles[nbr_of_handles] == NULL) { cout << "Error: failed to spawn a thread, fatal error exiting..." << endl; ExitProcess(nbr_of_handles); } nbr_of_handles++; } /* * signal the thread to begin work */ if (!SetEvent(event_handle)) { DWORD err = GetLastError(); printf("!!! main()::SetEvent failed: %x/n", GetLastError()); return (1); } /* * Wait until all threads terminate. * for the WaitForMultipleObjects function the arguments are respectively: * nbr_of_handles - number of handles to wait on * thread_handles - array of handles * TRUE - wait flag * INFINITE - how long to wait */ DWORD wrc = WaitForMultipleObjects(nbr_of_handles, thread_handles, TRUE, INFINITE); if (wrc == WAIT_FAILED) { DWORD err = GetLastError(); printf("!!! WaitForMultipleObjects failed: %x/n", err); } /* * Close the thread handles */ for (DWORD i = 0; i < nbr_of_handles; ++i) { CloseHandle(thread_handles[i]); } /* * Close the handle(s) */ CloseHandle(semaphore_handle); CloseHandle(event_handle); return 0; } /* main() */