static RVOID
    kernelModeDiff
    (
        rEvent isTimeToStop
    )
{
    RU32 i = 0;
    RU32 nScratch = 0;
    RU32 nProcessEntries = 0;
    KernelAcqProcess new_from_kernel[ 200 ] = { 0 };
    processEntry tracking_user[ MAX_SNAPSHOT_SIZE ] = { 0 };
    
    while( !rEvent_wait( isTimeToStop, 1000 ) )
    {
        nScratch = ARRAY_N_ELEM( new_from_kernel );
        rpal_memory_zero( new_from_kernel, sizeof( new_from_kernel ) );
        if( !kAcq_getNewProcesses( new_from_kernel, &nScratch ) )
        {
            rpal_debug_warning( "kernel acquisition for new processes failed" );
            g_is_kernel_failure = TRUE;
            break;
        }

        for( i = 0; i < nScratch; i++ )
        {
            notifyOfProcess( new_from_kernel[ i ].pid,
                             new_from_kernel[ i ].ppid,
                             TRUE,
                             new_from_kernel[ i ].path,
                             new_from_kernel[ i ].cmdline,
                             new_from_kernel[ i ].uid,
                             new_from_kernel[ i ].ts );

            if( nProcessEntries >= ARRAY_N_ELEM( tracking_user ) - 1 )
            {
                continue;
            }

            tracking_user[ nProcessEntries ].pid = new_from_kernel[ i ].pid;
            tracking_user[ nProcessEntries ].ppid = new_from_kernel[ i ].ppid;
            nProcessEntries++;
        }

        for( i = 0; i < nProcessEntries; i++ )
        {
            if( !processLib_isPidInUse( tracking_user[ i ].pid ) )
            {
                notifyOfProcess( tracking_user[ i ].pid, 
                                 tracking_user[ i ].ppid, 
                                 FALSE, 
                                 NULL, 
                                 NULL,
                                 KERNEL_ACQ_NO_USER_ID,
                                 0 );
                if( nProcessEntries != i + 1 )
                {
                    rpal_memory_memmove( &(tracking_user[ i ]), &(tracking_user[ i + 1 ]), nProcessEntries - i + 1 );
                }
                nProcessEntries--;
            }
        }
    }
}
static RPVOID
    processDiffThread
    (
        rEvent isTimeToStop,
        RPVOID ctx
    )
{
    processEntry* currentSnapshot = g_snapshot_1;
    processEntry* previousSnapshot = g_snapshot_2;
    processEntry* tmpSnapshot = NULL;
    RBOOL isFirstSnapshots = TRUE;
    RU32 i = 0;
    RU32 j = 0;
    RBOOL isFound = FALSE;
    RU32 nThLoop = 0;
    RU32 currentTimeout = 0;
    UNREFERENCED_PARAMETER( ctx );

    while( !rEvent_wait( isTimeToStop, currentTimeout ) )
    {
        tmpSnapshot = currentSnapshot;
        currentSnapshot = previousSnapshot;
        previousSnapshot = tmpSnapshot;

        if( getSnapshot( currentSnapshot ) )
        {
            if( isFirstSnapshots )
            {
                isFirstSnapshots = FALSE;
                continue;
            }

            // Diff to find new processes
            for( i = 0; i < MAX_SNAPSHOT_SIZE; i++ )
            {
                isFound = FALSE;

                if( 0 == currentSnapshot[ i ].pid )
                {
                    break;
                }

                for( j = 0; j < MAX_SNAPSHOT_SIZE; j++ )
                {
                    if( 0 == previousSnapshot[ j ].pid )
                    {
                        break;
                    }

                    if( previousSnapshot[ j ].pid == currentSnapshot[ i ].pid )
                    {
                        isFound = TRUE;
                        break;
                    }
                }

                if( !isFound )
                {
                    if( !notifyOfProcess( currentSnapshot[ i ].pid, 
                                          currentSnapshot[ i ].ppid,
                                          TRUE ) )
                    {
                        rpal_debug_warning( "error reporting new process: %d", 
                                            currentSnapshot[ i ].pid );
                    }
                }
            }

            // Diff to find terminated processes
            for( i = 0; i < MAX_SNAPSHOT_SIZE; i++ )
            {
                isFound = FALSE;

                if( 0 == previousSnapshot[ i ].pid )
                {
                    break;
                }

                for( j = 0; j < MAX_SNAPSHOT_SIZE; j++ )
                {
                    if( 0 == currentSnapshot[ j ].pid )
                    {
                        break;
                    }

                    if( previousSnapshot[ i ].pid == currentSnapshot[ j ].pid )
                    {
                        isFound = TRUE;
                        break;
                    }
                }

                if( !isFound )
                {
                    if( !notifyOfProcess( previousSnapshot[ i ].pid,
                                          previousSnapshot[ i ].ppid,
                                          FALSE ) )
                    {
                        rpal_debug_warning( "error reporting terminated process: %d",
                                            previousSnapshot[ i ].pid );
                    }
                }
            }
        }

        nThLoop++;
        if( 0 == nThLoop % 20 )
        {
#ifdef RPAL_PLATFORM_WINDOWS
            // The Windows API is much more efficient than on Nix so we can affort
            // going faster between our diffs.
            currentTimeout = libOs_getUsageProportionalTimeout( 500 ) + 100;
#else
            currentTimeout = libOs_getUsageProportionalTimeout( 800 ) + 200;
#endif
        }
    }

    return NULL;
}
static RVOID
    userModeDiff
    (
        rEvent isTimeToStop
    )
{
    processEntry snapshot_1[ MAX_SNAPSHOT_SIZE ] = { 0 };
    processEntry snapshot_2[ MAX_SNAPSHOT_SIZE ] = { 0 };
    processEntry* currentSnapshot = snapshot_1;
    processEntry* previousSnapshot = snapshot_2;
    processEntry* tmpSnapshot = NULL;
    RBOOL isFirstSnapshots = TRUE;
    RU32 i = 0;
    RBOOL isFound = FALSE;
    RU32 nTmpElem = 0;
    RU32 nCurElem = 0;
    RU32 nPrevElem = 0;
    LibOsPerformanceProfile perfProfile = { 0 };

    perfProfile.enforceOnceIn = 1;
    perfProfile.sanityCeiling = MSEC_FROM_SEC( 10 );
    perfProfile.lastTimeoutValue = 100;
    perfProfile.targetCpuPerformance = 0;
    perfProfile.globalTargetCpuPerformance = GLOBAL_CPU_USAGE_TARGET;
    perfProfile.timeoutIncrementPerSec = 10;

    while( !rEvent_wait( isTimeToStop, 0 ) &&
           ( !kAcq_isAvailable() ||
             g_is_kernel_failure ) )
    {
        libOs_timeoutWithProfile( &perfProfile, FALSE );

        tmpSnapshot = currentSnapshot;
        currentSnapshot = previousSnapshot;
        previousSnapshot = tmpSnapshot;

        nTmpElem = nCurElem;
        nCurElem = nPrevElem;
        nPrevElem = nTmpElem;

        if( getSnapshot( currentSnapshot, &nCurElem ) )
        {
            if( isFirstSnapshots )
            {
                isFirstSnapshots = FALSE;
                continue;
            }

            // Diff to find new processes
            for( i = 0; i < nCurElem; i++ )
            {
                isFound = FALSE;

                if( (RU32)( -1 ) != rpal_binsearch_array( previousSnapshot,
                                                          nPrevElem,
                                                          sizeof( processEntry ),
                                                          &(currentSnapshot[ i ].pid),
                                                          (rpal_ordering_func)rpal_order_RU32 ) )
                {
                    isFound = TRUE;
                }

                if( !isFound )
                {
                    if( !notifyOfProcess( currentSnapshot[ i ].pid,
                                          currentSnapshot[ i ].ppid,
                                          TRUE,
                                          NULL,
                                          NULL,
                                          KERNEL_ACQ_NO_USER_ID,
                                          0 ) )
                    {
                        rpal_debug_warning( "error reporting new process: %d",
                                            currentSnapshot[ i ].pid );
                    }
                }
            }

            // Diff to find terminated processes
            for( i = 0; i < nPrevElem; i++ )
            {
                isFound = FALSE;

                if( (RU32)( -1 ) != rpal_binsearch_array( currentSnapshot,
                                                          nCurElem,
                                                          sizeof( processEntry ),
                                                          &(previousSnapshot[ i ].pid),
                                                          (rpal_ordering_func)rpal_order_RU32 ) )
                {
                    isFound = TRUE;
                }

                if( !isFound )
                {
                    if( !notifyOfProcess( previousSnapshot[ i ].pid,
                                          previousSnapshot[ i ].ppid,
                                          FALSE,
                                          NULL,
                                          NULL,
                                          KERNEL_ACQ_NO_USER_ID,
                                          0 ) )
                    {
                        rpal_debug_warning( "error reporting terminated process: %d",
                                            previousSnapshot[ i ].pid );
                    }
                }
            }
        }

        libOs_timeoutWithProfile( &perfProfile, TRUE );
    }
}