If only one side of processes crash, the other side of processes do not hang.

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值