Use the shared memory to implement a queue, and producers can use it to transfer data to consumers. If only one side of processes crash, the other side of processes do not hang.
The interface is:
#ifndef PROTOCOL_H_B85C8C91_F359_415D_BE50_73059A34568B
#define PROTOCOL_H_B85C8C91_F359_415D_BE50_73059A34568B
#ifdef __cplusplus
extern "C" {
#endif
HANDLE InitializeProducers( LPCTSTR ptcSharedName, size_t nItemSize, size_t nMaxItems, size_t nMilliseconds );
HANDLE InitializeConsumers( LPCTSTR ptcSharedName, size_t nMilliseconds );
BOOL DestroyHandle( HANDLE );
/**
* Pushes the specified element into this queue, waiting up to the
* specified wait time if necessary for space to become available.
*
* The return value:
* TRUE -> The element has been pushed to the queue successfully;
* FALSE -> Failed to push the element to the queue. You may call
* GetLastError() to get the error code.
*/
BOOL AddItem( HANDLE, const void * data, size_t nBytes, size_t nMilliseconds );
/*
* Check where there are any consumers.
* ONLY producers are allowed to call the function.
*
* NOTE: in order to make it relyable, the value of the parameter nMilliseconds
* should be more than number_of_producers * 2 * 1000.
*
* The return value:
* TRUE -> there are some consumers.
* FALSE -> cannot find any consumers during the period of time.
*/
BOOL AreThereAnyConsumers( HANDLE, size_t nMilliseconds );
/**
* Retrieves and removes the head of this queue, waiting up to the
* specified wait time if necessary for an element to become available.
*
* The return value:
* TRUE -> A element has been retrieved from the queue successfully;
* FALSE -> Failed to retrieve a element from the queue. You may call
* GetLastError() to get the error code.
*/
BOOL TakeItem( HANDLE, void * buffer, size_t nBufferSize, size_t * pNumberOfBytesRead, size_t nMilliseconds );
/*
* Check where there are any producers.
* ONLY consumers are allowed to call the function.
*
* NOTE: in order to make it reliable, the value of the parameter nMilliseconds
* should be more than number_of_consuers * 2 * 1000.
*
* The return value:
* TRUE -> there are some producers.
* FALSE -> cannot find any producers during the period of time.
*/
BOOL AreThereAnyProducers( HANDLE, size_t nMilliseconds );
BOOL GetRemainingCapacity( HANDLE h, size_t * pNumberOfRemainedItems, size_t * pNumberOfRemainedBytes );
#ifdef __cplusplus
}
#endif
#endif //PROTOCOL_H_B85C8C91_F359_415D_BE50_73059A34568B
The implementation is:
#define _WIN32_WINNT 0x400
#include "../Protocol.h"
#include "../AutoHandle.h"
#include <stdio.h>
#include <assert.h>
#include <process.h>
#define MAGIC 427036969
namespace
{
const UINT_PTR MASK = static_cast<UINT_PTR>( MAGIC );
const DWORD INTERVAL_UNIT = 1000;
struct ItemHeader
{
UINT offset;
UINT bytes;
};
struct SharedMemoryQueueHeader
{
UINT total_bytes;
UINT offset_of_data;
UINT number_of_items;
UINT item_size;
LONG magic;
volatile LONG producer;
volatile LONG consumer;
volatile UINT begin;
volatile UINT end;
volatile UINT count_of_items;
volatile UINT remained_bytes;
volatile UINT offset_of_writting;
UINT end_of_offset;
UINT padding[3];
ItemHeader items[1];
};
struct MyHandle
{
UINT_PTR magic;
HANDLE hf;
HANDLE timer;
//HANDLE done;
BYTE * pointer_to_first_byte_of_data_space;
SharedMemoryQueueHeader * header;
HANDLE empty;
HANDLE saved;
HANDLE mutex;
};
inline static UINT prev_position( UINT i, UINT N )
{
return ( i + N - 1 )%N;
}
inline static UINT next_position( UINT i, UINT N )
{
return ( i + 1 )%N;
}
inline unsigned __int64 get_time_in_milliseconds()
{
return GetTickCount64();
}
DWORD wait( HANDLE handle, DWORD dwMilliseconds )
{
DWORD dwRtn;
for ( DWORD t; ; ) {
t = GetTickCount();
__try {
dwRtn = WaitForSingleObjectEx( handle, dwMilliseconds, TRUE );
} __except( EXCEPTION_EXECUTE_HANDLER ) {
dwRtn = WAIT_FAILED;
}
if ( dwRtn == WAIT_IO_COMPLETION ) {
t = GetTickCount() - t;
if ( dwMilliseconds > t ) {
dwMilliseconds -= t;
} else {
dwRtn = WAIT_TIMEOUT;
break;
}
} else {
break;
}
}
return dwRtn;
}
DWORD wait(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
) {
DWORD dwRtn;
for ( DWORD t; ; ) {
t = GetTickCount();
__try {
dwRtn = WaitForMultipleObjectsEx( nCount, lpHandles, bWaitAll, dwMilliseconds, TRUE );
} __except( EXCEPTION_EXECUTE_HANDLER ) {
dwRtn = WAIT_FAILED;
}
if ( dwRtn == WAIT_IO_COMPLETION ) {
t = GetTickCount() - t;
if ( dwMilliseconds > t ) {
dwMilliseconds -= t;
} else {
dwRtn = WAIT_TIMEOUT;
break;
}
} else {
break;
}
}
return dwRtn;
}
void CALLBACK my_timer_apc_procedure_for_producer(
LPVOID lpArg,
DWORD,
DWORD
) {
SharedMemoryQueueHeader * header = reinterpret_cast<SharedMemoryQueueHeader*>( lpArg );
InterlockedExchange( &header->producer, 1 );
}
void CALLBACK my_timer_apc_procedure_for_consumer(
LPVOID lpArg,
DWORD,
DWORD
) {
SharedMemoryQueueHeader * header = reinterpret_cast<SharedMemoryQueueHeader*>( lpArg );
InterlockedExchange( &header->consumer, 0 );
}
HANDLE create_timer( SharedMemoryQueueHeader * header, PTIMERAPCROUTINE pfnCompletionRoutine, LPCTSTR ptcTimerName = NULL )
{
HANDLE hTimer = CreateWaitableTimer( NULL, FALSE, ptcTimerName);
if ( hTimer != NULL ) {
__int64 qwDueTime;
LARGE_INTEGER liDueTime;
__try {
qwDueTime = -1000 * static_cast<__int64>( INTERVAL_UNIT );
liDueTime.LowPart = static_cast<DWORD>( qwDueTime & 0xFFFFFFFF );
liDueTime.HighPart = static_cast<LONG>( qwDueTime >> 32 );
if (
!SetWaitableTimer(
hTimer,
&liDueTime,
INTERVAL_UNIT,
pfnCompletionRoutine,
header,
FALSE // Do not restore a suspended system
)
) {
CloseHandle( hTimer );
hTimer = NULL;
}
} __except( EXCEPTION_EXECUTE_HANDLER ) {
CloseHandle( hTimer );
hTimer = NULL;
}
}
return hTimer;
}
}
extern "C" HANDLE InitializeProducers( LPCTSTR ptcSharedName, size_t nItemSize, size_t nMaxItems, size_t nMilliseconds )
{
HANDLE return_value = NULL;
SYSTEM_INFO si = {0};
GetSystemInfo( &si );
DWORD dwSize = static_cast<DWORD>( ( nItemSize + sizeof( ItemHeader ) ) * nMaxItems + 256 + si.dwPageSize - 1 );
dwSize = dwSize/si.dwPageSize*si.dwPageSize;
TCHAR buf[ _MAX_PATH << 1 ];
LONG lMaxItems = static_cast<LONG>( nMaxItems );
_sntprintf_s( buf, _TRUNCATE, _T("%s_SEMAPHORE_SAVED"), ptcSharedName );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > saved( CreateSemaphore( NULL, 0, lMaxItems, buf ) );
_sntprintf_s( buf, _TRUNCATE, _T("%s_SEMAPHORE_EMPTY"), ptcSharedName );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > empty( CreateSemaphore( NULL, lMaxItems, lMaxItems, buf ) );
_sntprintf_s( buf, _TRUNCATE, _T("%s_CRITICAL_SECTION"), ptcSharedName );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > mutex( CreateMutex( NULL, FALSE, buf ) );
bool bNew = true;
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > hf( CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, dwSize, ptcSharedName ) );
if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
bNew = false;
dwSize = 0;
}
if (
saved.get() != NULL &&
empty.get() != NULL &&
mutex.get() != NULL &&
hf.get() != NULL
) {
MyHandle * pmh = reinterpret_cast<MyHandle*>( malloc( sizeof( MyHandle ) ) );
if ( pmh != NULL ) {
util::auto_handle<void *, util::HandleDestructor<const void *, UnmapViewOfFile> > pv( MapViewOfFile( hf.get(), FILE_MAP_WRITE|FILE_MAP_READ, 0, 0, dwSize ) );
SharedMemoryQueueHeader * header = reinterpret_cast<SharedMemoryQueueHeader *>( pv.get() );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > timer( pv != NULL ? create_timer( header, my_timer_apc_procedure_for_producer ) : NULL );
if ( pv.get() != NULL && timer.get() != NULL ) {
if ( bNew ) {
header->total_bytes = static_cast<UINT>( dwSize );
header->begin = 0;
header->end = 0;
header->count_of_items = 0;
header->number_of_items = static_cast<UINT>( nMaxItems );
header->offset_of_data = static_cast<UINT>( ( reinterpret_cast<BYTE *>( &header->items[nMaxItems] ) ) - static_cast<BYTE*>( pv.get() ) );
header->remained_bytes = header->total_bytes;
header->offset_of_writting = 0;
header->end_of_offset = dwSize - header->offset_of_data;
header->item_size = static_cast<UINT>( nItemSize );
} else {
DWORD dwPeriodicTimerInterval = ( static_cast<DWORD>( nMilliseconds ) + INTERVAL_UNIT - 1 ) / INTERVAL_UNIT * INTERVAL_UNIT;
for ( ; header->magic != MAGIC && dwPeriodicTimerInterval > INTERVAL_UNIT; dwPeriodicTimerInterval -= INTERVAL_UNIT ) {
Sleep( INTERVAL_UNIT );
}
}
if ( bNew || header->magic == MAGIC ) {
pmh->magic = MASK;
pmh->hf = hf.detach();
pmh->header = header;
pmh->empty = empty.detach();
pmh->saved = saved.detach();
pmh->mutex = mutex.detach();
pmh->timer = timer.detach();
pmh->pointer_to_first_byte_of_data_space = static_cast<BYTE*>( pv.detach() ) + header->offset_of_data;
return_value = reinterpret_cast<HANDLE>( reinterpret_cast<UINT_PTR>( pmh ) ^ MASK );
InterlockedExchange( &pmh->header->magic, MAGIC );
InterlockedExchange( &pmh->header->producer, 1 );
ResumeThread( pmh->timer );
} else {
free( pmh );
}
} else {
free( pmh );
}
}
}
return return_value;
}
extern "C" HANDLE InitializeConsumers( LPCTSTR ptcSharedName, size_t nMilliseconds )
{
HANDLE return_value = NULL;
TCHAR buf[ _MAX_PATH << 1 ];
_sntprintf_s( buf, _TRUNCATE, _T("%s_SEMAPHORE_SAVED"), ptcSharedName );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > saved( OpenSemaphore( SEMAPHORE_ALL_ACCESS, FALSE, buf ) );
_sntprintf_s( buf, _TRUNCATE, _T("%s_SEMAPHORE_EMPTY"), ptcSharedName );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > empty( OpenSemaphore( SEMAPHORE_ALL_ACCESS, FALSE, buf ) );
_sntprintf_s( buf, _TRUNCATE, _T("%s_CRITICAL_SECTION"), ptcSharedName );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > mutex( OpenMutex( MUTEX_ALL_ACCESS, FALSE, buf ) );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > hf( OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, ptcSharedName ) );
if (
saved.get() != NULL &&
empty.get() != NULL &&
mutex.get() != NULL &&
hf.get() != NULL
) {
MyHandle * pmh = reinterpret_cast<MyHandle*>( malloc( sizeof( MyHandle ) ) );
if ( pmh != NULL ) {
util::auto_handle<void *, util::HandleDestructor<const void *, UnmapViewOfFile> > pv( MapViewOfFile( hf.get(), FILE_MAP_WRITE|FILE_MAP_READ, 0, 0, 0 ) );
SharedMemoryQueueHeader * header = reinterpret_cast<SharedMemoryQueueHeader *>( pv.get() );
util::auto_handle<HANDLE, util::HandleDestructor<HANDLE> > timer( pv.get() != NULL ? create_timer( header, my_timer_apc_procedure_for_consumer ) : NULL );
if ( pv.get() != NULL && timer.get() != NULL ) {
for (
DWORD dwPeriodicTimerInterval = ( static_cast<DWORD>( nMilliseconds ) + INTERVAL_UNIT - 1 ) / INTERVAL_UNIT * INTERVAL_UNIT;
header->magic != MAGIC && dwPeriodicTimerInterval > INTERVAL_UNIT;
dwPeriodicTimerInterval -= INTERVAL_UNIT
) {
Sleep( INTERVAL_UNIT );
}
if ( header->magic == MAGIC ) {
pmh->magic = MASK;
pmh->hf = hf.detach();
pmh->header = header;
pmh->empty = empty.detach();
pmh->saved = saved.detach();
pmh->mutex = mutex.detach();
pmh->timer = timer.detach();
pmh->pointer_to_first_byte_of_data_space = static_cast<BYTE*>( pv.detach() ) + header->offset_of_data;
return_value = reinterpret_cast<HANDLE>( reinterpret_cast<UINT_PTR>( pmh ) ^ MASK );
InterlockedExchange( &pmh->header->magic, MAGIC );
InterlockedExchange( &pmh->header->consumer, 0 );
ResumeThread( pmh->timer );
} else {
free( pmh );
}
} else {
free( pmh );
}
}
}
return return_value;
}
extern "C" BOOL DestroyHandle( HANDLE h )
{
BOOL bSuccessful = FALSE;
MyHandle * pmh = reinterpret_cast<MyHandle*>( reinterpret_cast<UINT_PTR>( h ) ^ MASK );
if ( pmh != NULL && pmh->magic == MASK ) {
UnmapViewOfFile( pmh->header );
CloseHandle( pmh->timer );
CloseHandle( pmh->saved );
CloseHandle( pmh->empty );
CloseHandle( pmh->mutex );
CloseHandle( pmh->hf );
pmh->magic = 0;
free( pmh );
bSuccessful = TRUE;
}
return bSuccessful;
}
extern "C" BOOL AddItem( HANDLE h, const void * data, size_t nBytes, size_t dwMilliseconds )
{
BOOL bSuccessful = FALSE;
MyHandle * pmh = reinterpret_cast<MyHandle*>( reinterpret_cast<UINT_PTR>( h ) ^ MASK );
if ( pmh != NULL && pmh->magic == MASK && data != NULL && nBytes != 0 && nBytes <= pmh->header->remained_bytes ) {
DWORD dwRtn = wait( pmh->empty, static_cast<DWORD>( dwMilliseconds ) );
if ( dwRtn == WAIT_OBJECT_0 ) {
dwRtn = wait( pmh->mutex, static_cast<DWORD>( dwMilliseconds ) );
if ( dwRtn == WAIT_OBJECT_0 ) {
SharedMemoryQueueHeader * header = pmh->header;
BYTE * pointer_to_first_byte = pmh->pointer_to_first_byte_of_data_space;
if ( nBytes <= header->remained_bytes ) {
header->items[ header->end ].bytes = static_cast<UINT>( nBytes );
header->items[ header->end ].offset = header->offset_of_writting;
header->remained_bytes -= static_cast<UINT>( nBytes );
UINT first_part = header->end_of_offset - header->offset_of_writting;
if ( first_part >= nBytes ) {
memcpy( pointer_to_first_byte + header->offset_of_writting, data, nBytes );
header->offset_of_writting += static_cast<UINT>( nBytes );
if ( first_part == nBytes ) {
header->offset_of_writting = 0;
}
} else {
const BYTE * pbInput = static_cast<const BYTE *>( data );
memcpy( pointer_to_first_byte + header->offset_of_writting, pbInput, first_part );
nBytes -= first_part;
memcpy( pointer_to_first_byte, pbInput + first_part, nBytes );
header->offset_of_writting = static_cast<UINT>( nBytes );
}
header->end = next_position( header->end, header->number_of_items );
++header->count_of_items;
ReleaseMutex( pmh->mutex );
bSuccessful = TRUE;
if ( !ReleaseSemaphore( pmh->saved, 1, NULL ) ) {
assert( false && "CANNOT handle the kind of error" );
}
} else {
ReleaseMutex( pmh->mutex );
ReleaseSemaphore( pmh->empty, 1, NULL );
SetLastError( ERROR_DESTINATION_ELEMENT_FULL );
}
} else {
ReleaseSemaphore( pmh->empty, 1, NULL );
SetLastError( ERROR_TIMEOUT );
}
} else {
SetLastError( ERROR_TIMEOUT );
}
} else {
if ( nBytes <= pmh->header->remained_bytes ) {
SetLastError( ERROR_INVALID_PARAMETER );
} else {
SetLastError( ERROR_DESTINATION_ELEMENT_FULL );
}
}
return bSuccessful;
}
#if ( defined( _DEBUG ) || defined( DEBUG ) || defined( DBG ) )
bool VerifyString( char * buffer, size_t size )
{
bool bSuccessful = false;
char * begin = buffer;
char * ps = strchr( buffer, ' ' );
if ( ps != NULL ) {
*ps = 0;
for ( char * pi = buffer; *pi != 0; ++pi ) {
assert( isdigit( *pi ) );
}
size_t n = strtoul( buffer, NULL, 10 );
*ps++ = ' ';
char c = 'a' + static_cast<char>( n%26 );
bSuccessful = true;
for ( char i = 'a'; i <= c; ++i, ++ps ) {
if ( ps[0] != c ) {
assert( false );
bSuccessful = false;
break;
}
}
} else {
assert( false );
}
return bSuccessful;
}
#endif
extern "C" BOOL TakeItem( HANDLE h, void * buffer, size_t nBufferSize, size_t * pNumberOfBytesRead, size_t dwMilliseconds )
{
BOOL bSuccessful = FALSE;
MyHandle * pmh = reinterpret_cast<MyHandle*>( reinterpret_cast<UINT_PTR>( h ) ^ MASK );
if ( pmh != NULL && pmh->magic == MASK && buffer != NULL && nBufferSize != 0 && pNumberOfBytesRead != NULL ) {
DWORD dwRtn = wait( pmh->saved, static_cast<DWORD>( dwMilliseconds ) );
if ( dwRtn == WAIT_OBJECT_0 ) {
dwRtn = wait( pmh->mutex, static_cast<DWORD>( dwMilliseconds ) );
if ( dwRtn == WAIT_OBJECT_0 ) {
SharedMemoryQueueHeader * header = pmh->header;
BYTE * pointer_to_first_byte = pmh->pointer_to_first_byte_of_data_space;
if ( header->items[ header->begin ].bytes <= nBufferSize ) {
size_t numberOfBytesRead = header->items[ header->begin ].bytes;
assert( numberOfBytesRead <= header->total_bytes - header->remained_bytes );
*pNumberOfBytesRead = numberOfBytesRead;
header->remained_bytes += static_cast<UINT>( numberOfBytesRead );
UINT offset_of_reading = header->items[ header->begin ].offset;
if ( ( offset_of_reading + numberOfBytesRead ) <= header->end_of_offset ) {
memcpy( buffer, pointer_to_first_byte + offset_of_reading, numberOfBytesRead );
assert( static_cast<char*>( buffer )[numberOfBytesRead - 1] == 0 );
assert( VerifyString( static_cast<char*>( buffer ), numberOfBytesRead ) );
} else {
BYTE * pbOutput = static_cast<BYTE*>( buffer );
size_t first_part = header->end_of_offset - offset_of_reading;
memcpy( pbOutput, pointer_to_first_byte + offset_of_reading, first_part );
numberOfBytesRead -= first_part;
memcpy( pbOutput + first_part, pointer_to_first_byte, numberOfBytesRead );
}
header->begin = next_position( header->begin, header->number_of_items );
--header->count_of_items;
ReleaseMutex( pmh->mutex );
bSuccessful = TRUE;
if ( !ReleaseSemaphore( pmh->empty, 1, NULL ) ) {
assert( false && "CANNOT handle the kind of error" );
}
} else {
ReleaseMutex( pmh->mutex );
ReleaseSemaphore( pmh->saved, 1, NULL );
SetLastError( ERROR_INSUFFICIENT_BUFFER );
}
} else {
ReleaseSemaphore( pmh->saved, 1, NULL );
SetLastError( ERROR_TIMEOUT );
}
} else {
SetLastError( ERROR_TIMEOUT );
}
} else {
SetLastError( ERROR_INVALID_PARAMETER );
}
return bSuccessful;
}
extern "C" BOOL AreThereAnyConsumers( HANDLE h, size_t dwMilliseconds )
{
BOOL bExisted = TRUE;
MyHandle * pmh = reinterpret_cast<MyHandle*>( reinterpret_cast<UINT_PTR>( h ) ^ MASK );
if ( pmh != NULL && pmh->magic == MASK ) {
bExisted = FALSE;
InterlockedExchange( &pmh->header->consumer, 1 );
for ( dwMilliseconds = ( dwMilliseconds + INTERVAL_UNIT - 1 )/INTERVAL_UNIT*INTERVAL_UNIT; dwMilliseconds != 0; dwMilliseconds -= INTERVAL_UNIT ) {
if ( InterlockedCompareExchange( &pmh->header->consumer, 0, 0 ) == 0 ) {
bExisted = TRUE;
break;
} else {
Sleep( INTERVAL_UNIT );
}
}
}
return bExisted;
}
extern "C" BOOL AreThereAnyProducers( HANDLE h, size_t dwMilliseconds )
{
BOOL bExisted = TRUE;
MyHandle * pmh = reinterpret_cast<MyHandle*>( reinterpret_cast<UINT_PTR>( h ) ^ MASK );
if ( pmh != NULL && pmh->magic == MASK ) {
bExisted = FALSE;
InterlockedExchange( &pmh->header->producer, 0 );
for ( dwMilliseconds = ( dwMilliseconds + INTERVAL_UNIT - 1 )/INTERVAL_UNIT*INTERVAL_UNIT; dwMilliseconds != 0; dwMilliseconds -= INTERVAL_UNIT ) {
if ( InterlockedCompareExchange( &pmh->header->producer, 1, 1 ) == 1 ) {
bExisted = TRUE;
break;
} else {
Sleep( INTERVAL_UNIT );
}
}
}
return bExisted;
}
extern "C" BOOL GetRemainingCapacity( HANDLE h, size_t * pNumberOfItems, size_t * pNumberOfBytes )
{
BOOL bSuccessful = FALSE;
MyHandle * pmh = reinterpret_cast<MyHandle*>( reinterpret_cast<UINT_PTR>( h ) ^ MASK );
if ( pmh != NULL && pmh->magic == MASK && pNumberOfItems != NULL && pNumberOfBytes != NULL ) {
if ( WaitForSingleObject( pmh->mutex, 0 ) == WAIT_OBJECT_0 ) {
*pNumberOfItems = pmh->header->number_of_items - pmh->header->count_of_items;
*pNumberOfBytes = pmh->header->total_bytes - pmh->header->remained_bytes;
bSuccessful = TRUE;
}
}
return bSuccessful;
}
The test programs are:
Producer:
#include "../Protocol.h"
#include <openssl/rand.h>
#include <vector>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
extern "C" int CN_base64Encode( const void * data, size_t nSize, char * buf );
inline size_t random( size_t distance )
{
union
{
size_t n;
BYTE ab[ sizeof(size_t) ];
};
RAND_bytes( ab, sizeof( ab ) );
n = static_cast<size_t>( static_cast<double>( rand() * n ) / static_cast<double>( RAND_MAX + 1 ) );
return n % distance;
}
size_t CreateRandomString( size_t index, char * out, size_t size )
{
size_t n = random( size );
if ( n == 0 ) {
n = size/2;
}
std::vector<unsigned char> buffer( n );
std::vector<char> output( ( ( n + 2 ) / 3 ) * 4 + 1 );
RAND_bytes( &buffer[0], static_cast<int>( n ) );
CN_base64Encode( &buffer[0], n, &output[0] );
size_t n1 = strlen( &output[0] );
size_t n2 = size - n1 - 1;
if ( n2 >= n1 ) {
n2 = n1;
} else {
output[ n2 ] = 0;
n2 = strlen( &output[0] );
}
int n3 = _snprintf_s( out, size, _TRUNCATE, "%8u <%3u> %s", index, n2, &output[0] );
return static_cast<size_t>( n3 );
}
#if ( defined( _DEBUG ) || defined( DEBUG ) || defined( DBG ) )
size_t CreateString( size_t index, char * out, size_t size )
{
assert( out != NULL && size >= 80 );
char * begin = out;
char c = 'a' + static_cast<char>( index%26 );
_snprintf_s( out, size, _TRUNCATE, "%u ", index );
out += strlen( out );
for ( char i = 'a'; i <= c; ++i ) {
*out++ = c;
}
*out++ = 0;
return out - begin;
}
#endif
int _tmain( int argc, TCHAR * argv[] )
{
int error_code = -1;
if ( argc >= 2 ) {
LPCTSTR ptcSharedName = argv[1];
size_t nWaitingMilliseconds = 5 * 60 * 1000; // five minutes;
size_t nItemSize = DEFAULT_ITEM_SIZE;
size_t nMaxItems = DEFAULT_NUMBER_OF_ITEMS;
if ( argc >= 3 ) {
nWaitingMilliseconds = _tcstoul( argv[2], NULL, 10 );
nWaitingMilliseconds *= 1000;
}
if ( argc >= 4 ) {
nItemSize = _tcstoul( argv[3], NULL, 10 );
}
if ( argc >= 5 ) {
nMaxItems = _tcstoul( argv[4], NULL, 10 );
}
HANDLE h = InitializeProducers( ptcSharedName, nItemSize, nMaxItems, nWaitingMilliseconds );
if ( h != NULL ) {
try {
error_code = 0;
#if ( defined( _DEBUG ) || defined( DEBUG ) || defined( DBG ) )
std::vector<char> buffer( nItemSize );
for ( size_t nData, i = 0; i < 100000000; ++i ) {
nData = CreateString( i, &buffer[0], buffer.size() );
if ( AddItem( h, &buffer[0], nData, 2 * 1000 ) ) {
fprintf( stdout, "%s\n", &buffer[0] );
} else {
if ( !AreThereAnyConsumers( h, nWaitingMilliseconds ) ) {
fprintf( stdout, "No consumers, exit...\n" );
break;
}
}
}
#else
std::vector<char> buffer( nItemSize );
for ( size_t nData, i = 1; i < 100000000; ++i ) {
nData = CreateRandomString( i, &buffer[0], nItemSize );
if ( AddItem( h, &buffer[0], nData, 2 * 1000 ) ) {
fprintf( stdout, "%s\n", &buffer[0] );
} else {
DWORD dwErrorCode = GetLastError();
switch ( dwErrorCode )
{
case ERROR_INVALID_PARAMETER:
case ERROR_DESTINATION_ELEMENT_FULL:
--i;
continue;
}
if ( !AreThereAnyConsumers( h, nWaitingMilliseconds ) ) {
fprintf( stdout, "No consumers, exit...\n" );
break;
}
}
}
#endif
} catch ( std::exception & e ) {
fprintf( stderr, "Exception: %s\n", e.what() );
}
DestroyHandle( h );
}
} else {
_ftprintf( stderr, _T("Usage: %s <shared name> [<waiting seconds>] [<item size>] [<number of items>]\n"), argv[0] );
}
return error_code;
}
Consumer:
#include "../Protocol.h"
#include <vector>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
int _tmain( int argc, TCHAR * argv[] )
{
int error_code = -1;
if ( argc >= 2 ) {
LPCTSTR ptcSharedName = argv[1];
size_t nWaitingMilliseconds = 5 * 60 * 1000; // five minutes;
if ( argc >= 3 ) {
nWaitingMilliseconds = _tcstoul( argv[2], NULL, 10 );
nWaitingMilliseconds *= 1000;
}
HANDLE h = InitializeConsumers( ptcSharedName, nWaitingMilliseconds );
if ( h != NULL ) {
try {
error_code = 0;
std::vector<char> buffer( 4096 );
buffer.back() = 0;
for ( size_t nNumberOfBytesRead = 0; ; nNumberOfBytesRead = 0 ) {
if ( TakeItem( h, &buffer[0], buffer.size() - 1, &nNumberOfBytesRead, 2 * 1000 ) ) {
buffer[nNumberOfBytesRead] = 0;
fprintf( stdout, "%s\n", &buffer[0] );
} else {
DWORD dwErrorCode = GetLastError();
switch ( dwErrorCode )
{
case ERROR_INVALID_PARAMETER:
case ERROR_INSUFFICIENT_BUFFER:
buffer.resize( buffer.size() * 2 );
continue;
}
if ( !AreThereAnyProducers( h, nWaitingMilliseconds ) ) {
fprintf( stdout, "No producers, exit...\n" );
break;
}
}
}
} catch ( std::exception & e ) {
fprintf( stderr, "Exception: %s\n", e.what() );
}
DestroyHandle( h );
}
} else {
_ftprintf( stderr, _T("Usage: %s <shared name> [<waiting seconds>]\n"), argv[0] );
}
return error_code;
}