Listing 2: CommsChannel implementation
#define STRICT #include <windows.h> #include <memory.h> #include <stdlib.h> #include "channel.hpp" struct CommsData { int bufferSize; int fsStart; int fsNext; bool fsEmpty; int tsStart; int tsNext; bool tsEmpty; int fsOffset; int tsOffset; }; // general delayed open CommsChannel::CommsChannel() : opened(false), error(objectClosed) { } // server open CommsChannel::CommsChannel(string name, int size) : opened(false) { open(name, size); } // client open CommsChannel::CommsChannel(string name) : opened(false) { open(name); } CommsChannel::statusEnum CommsChannel::open(string name, int size) { if (opened) { return objectAlreadyOpen; } channelName = name; opened = true; error = ok; string mapName = "CommsChannel-" + channelName; string mutexName = "CommsChannelMutex-" + channelName; string fsNotEmptySemName = "CommsChannelFsNotEmptySem-" + channelName; string fsNotFullSemName = "CommsChannelFsNotFullSem-" + channelName; string tsNotEmptySemName = "CommsChannelTsNotEmptySem-" + channelName; string tsNotFullSemName = "CommsChannelTsNotFullSem-" + channelName; // get and own the mutex first so there isn't a race condition error = ok; generalMutex = NULL; // need to be NULL so can close on error fsNotEmptySem = NULL; fsNotFullSem = NULL; tsNotEmptySem = NULL; tsNotFullSem = NULL; data = NULL; handle = NULL; if (((generalMutex = CreateMutex( NULL, true, mutexName.c_str())) == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS) ) { error = mutexCreationError; } else if (((fsNotEmptySem = CreateSemaphore( /* start out empty */ NULL, 0, 1, fsNotEmptySemName.c_str())) == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS) ) { error = mutexCreationError; } else if (((fsNotFullSem = CreateSemaphore( /* start not full */ NULL, 1, 1, fsNotFullSemName.c_str())) == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS) ) { error = mutexCreationError; } else if (((tsNotEmptySem = CreateSemaphore( /* start out empty */ NULL, 0, 1, tsNotEmptySemName.c_str())) == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS) ) { error = mutexCreationError; } else if (((tsNotFullSem = CreateSemaphore( /* start not full */ NULL, 1, 1, tsNotFullSemName.c_str())) == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS) ) { error = mutexCreationError; } if (error == ok) { handle = CreateFileMapping( (HANDLE)0xffffffff, NULL, PAGE_READWRITE, 0, sizeof(CommsData) + (size + 4) * 2, mapName.c_str()); if (handle == NULL) { error = channelCreationError; } else if (GetLastError() == ERROR_ALREADY_EXISTS) { // someone has already opened this channel! error = channelAlreadyExists; } } if (error == ok) { data = (CommsData*)MapViewOfFile( handle, FILE_MAP_WRITE, 0, 0, 0); if (data == NULL) { error = channelMappingError; } else { // initialise things server = true; // align on double word boundary data->fsOffset = (sizeof(CommsData) + 3) & 0xfffffffc; data->tsOffset = (data->fsOffset + size + 7) & 0xfffffffc; fsBuffer = reinterpret_cast<char*>(data + data->fsOffset); tsBuffer = reinterpret_cast<char*>(data + data->tsOffset); data->bufferSize = size; data->fsStart = 0; data->tsStart = 0; data->fsNext = 0; data->tsNext = 0; data->fsEmpty = true; data->tsEmpty = true; } } if (error != ok) { if (data) {UnmapViewOfFile(data);} if (handle) {CloseHandle(handle);} if (fsNotEmptySem) {CloseHandle(fsNotEmptySem);} if (fsNotFullSem) {CloseHandle(fsNotFullSem);} if (tsNotEmptySem) {CloseHandle(tsNotEmptySem);} if (tsNotFullSem) {CloseHandle(tsNotFullSem);} if (generalMutex) { ReleaseMutex(generalMutex); CloseHandle(generalMutex); } } else { ReleaseMutex(generalMutex); } return error; } CommsChannel::~CommsChannel() { close(); } CommsChannel::statusEnum CommsChannel::close() { if (!opened) { return objectClosed; } if (!error) { UnmapViewOfFile(data); CloseHandle(handle); ReleaseMutex(generalMutex); CloseHandle(generalMutex); } error = objectClosed; opened = false; return ok; } // not shown: client side open() function // ... int CommsChannel::write(char* buffer, int size, bool chunk, DWORD timeout) { int roomAvailable; int status = -1; bool wasEmpty; if (error < 0) { // if bad channel don't get mutex return -1; } char* channel = server ? fsBuffer : tsBuffer; int& start = server ? data->fsStart : data->tsStart; int& next = server ? data->fsNext : data->tsNext; bool& empty = server ? data->fsEmpty : data->tsEmpty; HANDLE& notEmptySem = server ? fsNotEmptySem : tsNotEmptySem; HANDLE& notFullSem = server ? fsNotFullSem : tsNotFullSem; roomAvailable = blockOnChannel(size, chunk, timeout, false, generalMutex, notFullSem, start, next); if (!error) { // have mutex and semaphore at this point and know not full wasEmpty = (start == next); // remember if channel // was empty size = min(size, roomAvailable); // can only write status = size; // what's available int moveSize = min(size, data->bufferSize - next); memcpy(&channel[next], buffer, moveSize); next += moveSize; // if copy done then start updated size -= moveSize; if (size > 0) { memcpy(channel, &buffer[moveSize], size); next = size; // next wasn't quite right } if (next == start) { // update the full/empty indicators // must keep the not-full semaphore because it is // no longer true } else { ReleaseSemaphore(notFullSem, 1, NULL); } if (wasEmpty && (status > 0)) { // we've written something // so now must release the not-empty semaphore because // it is now true ReleaseSemaphore(notEmptySem, 1, NULL); empty = false; } } // Release the mutex! ReleaseMutex(generalMutex); return status; } int CommsChannel::blockOnChannel(int size, bool chunk, DWORD timeout, bool reading, HANDLE& mutex, HANDLE& semaphore, int& start, int& next) { DWORD timeStart = GetTickCount(); DWORD timeLeft = timeout; DWORD timePassed; DWORD waitResult; int amount; bool enoughData = false; HANDLE twoHandles[2] = {mutex, semaphore}; do { waitResult = WaitForMultipleObjects(2, twoHandles, true, timeLeft); if (waitResult == WAIT_FAILED) { error = waitError; } else if (waitResult == WAIT_TIMEOUT) { error = waitTimeout; } else { // we are in with the mutex error = ok; } if (!error) { // now we have the mutex and know that the buffer // is not empty if (reading) { if (start == next) {amount = data->bufferSize;} else if (next > start) {amount = next - start;} else {amount = data->bufferSize - start + next;} } else { if (start == next) {amount = data->bufferSize;} else if (start > next) {amount = start - next;} else {amount = data->bufferSize - next + start;} } if (!chunk || amount >= size) { // have enough data - get out of the loop enoughData = true; } else { // not enough data - have to reblock ReleaseSemaphore(semaphore, 1, NULL); ReleaseMutex(mutex); Sleep(0); // force yield if (timeout != INFINITE) { // Don't accumulate instead recalculate for precision DWORD timeNow = GetTickCount(); if (timeNow < timeStart) { // this doesn't seem too portable but I guess a // DWORD will never change!! timePassed = (timeNow + (0xffffffffL - timeStart)); } else { timePassed = timeNow - timeStart; } timeLeft = timeout - timePassed; } } } } while (!error && !enoughData && (timeout > timePassed)); if (!error) { if (enoughData) { return amount; } else { // ran out of time error = waitTimeoutChunkTooSmall; return -1; } } else { return -1; } } // not shown: read() member function, roomToWrite(), bytesToRead() // ... string CommsChannel::noName = "No Name"; const string& CommsChannel::name() const { if (!opened) { return noName; } else { return channelName; } } BOOL WINAPI DllEntryPoint(HINSTANCE,DWORD,LPVOID); __declspec(dllexport) BOOL WINAPI DllEntryPoint(HINSTANCE /*myHandle*/, DWORD reason, LPVOID /*reserved*/) { bool success = true; switch (reason) { case DLL_PROCESS_ATTACH: break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; default: success = false; break; } return success; } //End of File