#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <memory.h>
#include <process.h>

#define WAIT_MULTIPLE_ITERATIONS 10000
HANDLE WaitHandles[64];

//
// Define local types.
//

typedef struct _PERFINFO {
    LARGE_INTEGER StartTime;
    LARGE_INTEGER StopTime;
    ULONG ContextSwitches;
    ULONG FirstLevelFills;
    ULONG SecondLevelFills;
    ULONG SystemCalls;
    PCHAR Title;
    ULONG Iterations;
} PERFINFO, *PPERFINFO;

VOID
FinishBenchMark (
    IN PPERFINFO PerfInfo
    )

{

    ULONG ContextSwitches;
    LARGE_INTEGER Duration;
    ULONG FirstLevelFills;
    ULONG Length;
    ULONG Performance;
    ULONG SecondLevelFills;
    NTSTATUS Status;
    ULONG SystemCalls;
    SYSTEM_PERFORMANCE_INFORMATION SystemInfo;


    //
    // Print results and announce end of test.
    //

    NtQuerySystemTime((PLARGE_INTEGER)&PerfInfo->StopTime);
    Status = NtQuerySystemInformation(SystemPerformanceInformation,
                                      (PVOID)&SystemInfo,
                                      sizeof(SYSTEM_PERFORMANCE_INFORMATION),
                                      NULL);

    if (NT_SUCCESS(Status) == FALSE) {
        printf("Failed to query performance information, status = %lx\n", Status);
        return;
    }

    Duration = RtlLargeIntegerSubtract(PerfInfo->StopTime, PerfInfo->StartTime);
    Length = Duration.LowPart / 10000;
    printf("        Test time in milliseconds %d\n", Length);
    printf("        Number of iterations      %d\n", PerfInfo->Iterations);

    Performance = PerfInfo->Iterations * 1000 / Length;
    printf("        Iterations per second     %d\n", Performance);

    ContextSwitches = SystemInfo.ContextSwitches - PerfInfo->ContextSwitches;
    FirstLevelFills = SystemInfo.FirstLevelTbFills - PerfInfo->FirstLevelFills;
    SecondLevelFills = SystemInfo.SecondLevelTbFills - PerfInfo->SecondLevelFills;
    SystemCalls = SystemInfo.SystemCalls - PerfInfo->SystemCalls;
    printf("        First Level TB Fills      %d\n", FirstLevelFills);
    printf("        Second Level TB Fills     %d\n", SecondLevelFills);
    printf("        Total Context Switches    %d\n", ContextSwitches);
    printf("        Number of System Calls    %d\n", SystemCalls);

    printf("*** End of Test ***\n\n");
    return;
}

VOID
StartBenchMark (
    IN PCHAR Title,
    IN ULONG Iterations,
    IN PPERFINFO PerfInfo
    )

{

    NTSTATUS Status;
    SYSTEM_PERFORMANCE_INFORMATION SystemInfo;

    //
    // Announce start of test and the number of iterations.
    //

    printf("*** Start of test ***\n    %s\n", Title);
    PerfInfo->Title = Title;
    PerfInfo->Iterations = Iterations;
    NtQuerySystemTime((PLARGE_INTEGER)&PerfInfo->StartTime);
    Status = NtQuerySystemInformation(SystemPerformanceInformation,
                                      (PVOID)&SystemInfo,
                                      sizeof(SYSTEM_PERFORMANCE_INFORMATION),
                                      NULL);

    if (NT_SUCCESS(Status) == FALSE) {
        printf("Failed to query performance information, status = %lx\n", Status);
        return;
    }

    PerfInfo->ContextSwitches = SystemInfo.ContextSwitches;
    PerfInfo->FirstLevelFills = SystemInfo.FirstLevelTbFills;
    PerfInfo->SecondLevelFills = SystemInfo.SecondLevelTbFills;
    PerfInfo->SystemCalls = SystemInfo.SystemCalls;
    return;
}


VOID
WaitMultipleTest()
{
    PERFINFO PerfInfo;
    int i;

    StartBenchMark(
        "Wait Single Object",
        WAIT_MULTIPLE_ITERATIONS,
        &PerfInfo
        );

    for ( i=0;i<WAIT_MULTIPLE_ITERATIONS;i++) {
        WaitForSingleObject(WaitHandles[0],INFINITE);   // Wait Single Object
        }

    FinishBenchMark(&PerfInfo);

    StartBenchMark(
        "Wait Multiple Test 1 Object",
        WAIT_MULTIPLE_ITERATIONS,
        &PerfInfo
        );

    for ( i=0;i<WAIT_MULTIPLE_ITERATIONS;i++) {
        WaitForMultipleObjects(1,WaitHandles,FALSE,INFINITE);   // Wait Any, 1 Object
        }

    FinishBenchMark(&PerfInfo);

    StartBenchMark(
        "Wait Multiple Test 8 Objects",
        WAIT_MULTIPLE_ITERATIONS,
        &PerfInfo
        );

    for ( i=0;i<WAIT_MULTIPLE_ITERATIONS;i++) {
        WaitForMultipleObjects(8,WaitHandles,FALSE,INFINITE);   // Wait Any, 8 Objects
        }

    FinishBenchMark(&PerfInfo);

    StartBenchMark(
        "Wait Multiple Test 16 Objects",
        WAIT_MULTIPLE_ITERATIONS,
        &PerfInfo
        );

    for ( i=0;i<WAIT_MULTIPLE_ITERATIONS;i++) {
        WaitForMultipleObjects(16,WaitHandles,FALSE,INFINITE);   // Wait Any, 16 Objects
        }

    FinishBenchMark(&PerfInfo);

    StartBenchMark(
        "Wait Multiple Test 32 Objects",
        WAIT_MULTIPLE_ITERATIONS,
        &PerfInfo
        );

    for ( i=0;i<WAIT_MULTIPLE_ITERATIONS;i++) {
        WaitForMultipleObjects(32,WaitHandles,FALSE,INFINITE);   // Wait Any, 32 Objects
        }

    FinishBenchMark(&PerfInfo);

    StartBenchMark(
        "Wait Multiple Test 64 Objects",
        WAIT_MULTIPLE_ITERATIONS,
        &PerfInfo
        );

    for ( i=0;i<WAIT_MULTIPLE_ITERATIONS;i++) {
        WaitForMultipleObjects(64,WaitHandles,FALSE,INFINITE);   // Wait Any, 64 Objects
        }

    FinishBenchMark(&PerfInfo);
}

DWORD
_cdecl
main(
    int argc,
    char *argv[],
    char *envp[]
    )
{

    int i;

    for(i=0;i<64;i++){
        WaitHandles[i] = CreateEvent(NULL,TRUE,TRUE,NULL);
        }
    WaitMultipleTest();
    return 0;
}