WaitForAllObjects to wait on more than MAXIMUM_WAIT_OBJECTS handles

One of the painful limitations of WaitForMultipleObjects function is that you can use it to wait for a maximum of MAXIMUM_WAIT_OBJECTS number of object handles. Value of MAXIMUM_WAIT_OBJECTS is 64. If you need to wait on more than MAXIMUM_WAIT_OBJECTS handles, you can either create a separate thread to wait on MAXIMUM_WAIT_OBJECTS and then do a wait on these threads to finish. Using this method you can create MAXIMUM_WAIT_OBJECTS threads each of those can wait for MAXIMUM_WAIT_OBJECTS object handles. Few months ago I needed code for doing this and couldn't find it on the internet. Here is how I implemented WaitForAllObjects.

typedef
struct _WAIT_THREAD_INFORMATION
{
    DWORD      dwWaitObjectCount;
    PHANDLE    pWaitHandles;
    DWORD      dwWaitMSeconds;
} WAIT_THREAD_INFORMATION, *PWAIT_THREAD_INFORMATION;

VOID
WaitForAllObjects(
    DWORD     dwWaitHandleCount,
    HANDLE *  pWaitHandles,
    DWORD     dwMaxWaitMSeconds
)
{
    HANDLE *                  pThreadWaitHandles    = NULL;
    WAIT_THREAD_INFORMATION * pWaitInformationArray = NULL;

    DWORD                     dwThreadCount         = 0;
    DWORD                     dwLastWaitCount       = 0;
    DWORD                     dwWaitHandleIndex     = 0;
    DWORD                     dwMaximumWaitObjects  = MAXIMUM_WAIT_OBJECTS;

    ASSERT( dwWaitHandleCount    >  0 );
    ASSERT( pWaitHandles         != NULL );
    ASSERT( dwMaxWaitMSeconds    >= 1000 );
    ASSERT( dwMaximumWaitObjects <= MAXIMUM_WAIT_OBJECTS );

    if( dwWaitHandleCount <= dwMaximumWaitObjects )
    {
        WaitForMultipleObjects( dwWaitHandleCount,
                                pWaitHandles,
                                TRUE,
                                dwMaxWaitMSeconds );
    }
    else
    {
        //
        // Create separate threads to wait on maximum wait objects
        // and then make this thread wait on thread handles
        //
        dwThreadCount   = dwWaitHandleCount / dwMaximumWaitObjects;
        dwLastWaitCount = dwWaitHandleCount % dwMaximumWaitObjects;

        if( dwLastWaitCount > 0 )
        {
            dwThreadCount++;
        }

        //
        // This function can handle a maximum of
        // MAXIMUM_WAIT_OBJECTS * MAXIMUM_WAIT_OBJECT handles
        //
        if( dwThreadCount > dwMaximumWaitObjects )
        {
            dwThreadCount     = dwMaximumWaitObjects;
            dwLastWaitCount   = 0;
        }

        pThreadWaitHandles    = new HANDLE[ dwThreadCount ];
        pWaitInformationArray = new WAIT_THREAD_INFORMATION[ dwThreadCount ];

        if( pThreadWaitHandles == NULL || pWaitInformationArray == NULL )
        {
            //
            // Failure
            //
            goto Finished;
        }

        for( DWORD count = 0; count < dwThreadCount; count++)
        {
            //
            // Set information for the thread
            //
            pWaitInformationArray[ count ].dwWaitMSeconds =
                dwMaxWaitMSeconds;
            pWaitInformationArray[ count ].pWaitHandles =
                &pWaitHandles[ dwWaitHandleIndex ];

            if( count != dwThreadCount - 1 || dwLastWaitCount == 0 )
            {
                pWaitInformationArray[ count ].dwWaitObjectCount =
                    dwMaximumWaitObjects;
                dwWaitHandleIndex += dwMaximumWaitObjects;
            }
            else
            {
                pWaitInformationArray[count].dwWaitObjectCount =
                    dwLastWaitCount;
                dwWaitHandleIndex += dwLastWaitCount;
            }

            pThreadWaitHandles[ count ] =
                CreateThread( NULL,
                              0,
                              WaitForMultipleObjectsThread,
                              &pWaitInformationArray[ count ],
                              0,
                              NULL );
            if( pThreadWaitHandles[ count ] == NULL )
            {
                //
                // Not able to create threads break from the loop
                // and wait for threads
we already created.
                // dwThreadCount doesnt include this iteration
                //
                dwThreadCount = count;
                break;
            }
        }

        //
        // Failure is ignored
        //
        WaitForMultipleObjects( dwThreadCount,
                                pThreadWaitHandles,
                                TRUE,
                                dwMaxWaitMSeconds );
    }

Finished:

    if( pThreadWaitHandles != NULL )
    {
        for( DWORD count = 0; count < dwThreadCount; count++)
        {
            if( pThreadWaitHandles[ count ] != NULL )
            {
                CloseHandle( pThreadWaitHandles[ count ] );
                pThreadWaitHandles[ count ] = NULL;
            }
        }

        delete [] pThreadWaitHandles;
        pThreadWaitHandles = NULL;
    }

    if( pWaitInformationArray != NULL )
    {
        delete [] pWaitInformationArray;
        pWaitInformationArray = NULL;
    }

    return;
}

//static
DWORD
WINAPI
WaitForMultipleObjectsThread(
    LPVOID lpParameter = NULL
)
//
// Thread routine which calls WaitForMultipleObjects
// This is used to wait for more than MAXIMUM_WAIT_OBJECTS objects
//
{
    DWORD                    dwReturnValue      = 0;
    PWAIT_THREAD_INFORMATION pThreadInformation = NULL;

    ASSERT( lpParameter != NULL );

    pThreadInformation = ( PWAIT_THREAD_INFORMATION )lpParameter;

    ASSERT(pThreadInformation->dwWaitObjectCount <= MAXIMUM_WAIT_OBJECTS );
    ASSERT(pThreadInformation->pWaitHandles      != NULL );
    ASSERT(pThreadInformation->dwWaitMSeconds    != INFINITE );
    ASSERT(pThreadInformation->dwWaitMSeconds    >= 1000 );

    dwReturnValue = WaitForMultipleObjects(
                        pThreadInformation->dwWaitObjectCount,
                        pThreadInformation->pWaitHandles,
                        TRUE,
                        pThreadInformation->dwWaitMSeconds );

    return dwReturnValue;

}

Sample usage of this method will look like below.

//
// Allocate memory for wait handle array
//
pWaitHandles = new HANDLE[ dwWaitHandleCount ];

...

//
// Call WaitForAllObjects
//
WaitForAllObjects( dwWaitHandleCount,
                   pWaitHandles,
                   dwMaximumWait );

...

//
// Free memory held by pWaitHandles
//
if( pWaitHandles != NULL )
{
    delete [] pWaitHandles;
    pWaitHandles = NULL;
}

Hope this helps.
Kanwal

Leave a Reply

Your email address will not be published. Required fields are marked *