/*++ Copyright (c) 2000 Microsoft Corporation Module Name: conidle.c Abstract: This module builds a console test program to stress idle detection, and registration/unregistration mechanisms. The quality of the code for the test programs is as such. Author: Cenk Ergan (cenke) Environment: User Mode --*/ #include #include #include #include #include #include #include #include #include #include "idlrpc.h" #include "idlecomn.h" // // Note that the following code is test quality code. // DWORD RegisterIdleTask ( IN IT_IDLE_TASK_ID IdleTaskId, OUT HANDLE *ItHandle, OUT HANDLE *StartEvent, OUT HANDLE *StopEvent ); DWORD UnregisterIdleTask ( IN HANDLE ItHandle, IN HANDLE StartEvent, IN HANDLE StopEvent ); DWORD ProcessIdleTasks ( VOID ); #define NUM_TEST_TASKS 3 typedef enum _WORKTYPE { CpuWork, DiskWork, MaxWorkType } WORKTYPE, *PWORKTYPE; typedef struct _TESTTASK { HANDLE ThreadHandle; ULONG No; IT_IDLE_TASK_ID Id; IT_HANDLE ItHandle; HANDLE StartEvent; HANDLE StopEvent; } TESTTASK, *PTESTTASK; typedef struct _TESTWORK { ULONG No; WORKTYPE Type; DWORD WorkLength; HANDLE StopEvent; } TESTWORK, *PTESTWORK; TESTTASK g_Tasks[NUM_TEST_TASKS]; BOOLEAN g_ProcessingIdleTasks = FALSE; HANDLE g_ProcessedIdleTasksEvent = NULL; #define MAX_WAIT_FOR_START 20000 #define MAX_WORK_LENGTH 5000 #define MAX_READ_SIZE (64 * 1024) DWORD WINAPI DoWorkThreadProc( LPVOID lpParameter ) { PTESTWORK Work; DWORD EndTime; DWORD WaitResult; DWORD ErrorCode; DWORD RunTillTime; HANDLE DiskHandle; PVOID ReadBuffer; BOOL ReadResult; ULONG NumBytesRead; LARGE_INTEGER VolumeSize; LARGE_INTEGER SeekPosition; ULONG ReadIdx; DISK_GEOMETRY DiskGeometry; ULONG BytesReturned; static LONG DiskNumber; // // Initialize locals. // Work = lpParameter; EndTime = GetTickCount() + Work->WorkLength; DiskHandle = NULL; ReadBuffer = NULL; // // Do initialization for performing specified work. // switch (Work->Type) { case DiskWork: // // Open disk. Maybe we could open different physical drives // each time. // DiskHandle = CreateFile(L"\\\\.\\PHYSICALDRIVE0", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0); if (!DiskHandle) { ErrorCode = GetLastError(); printf("W%d: Failed open PHYSICALDRIVE0.\n", Work->No); goto cleanup; } // // Get volume size. // if (!DeviceIoControl(DiskHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, sizeof(DiskGeometry), &BytesReturned, NULL)) { ErrorCode = GetLastError(); printf("W%d: Failed GET_DRIVE_GEOMETRY.\n", Work->No); goto cleanup; } VolumeSize.QuadPart = DiskGeometry.Cylinders.QuadPart * DiskGeometry.TracksPerCylinder * DiskGeometry.SectorsPerTrack * DiskGeometry.BytesPerSector; // // Allocate buffer. // ReadBuffer = VirtualAlloc(NULL, MAX_READ_SIZE, MEM_COMMIT, PAGE_READWRITE); if (!ReadBuffer) { ErrorCode = ERROR_NOT_ENOUGH_MEMORY; printf("W%d: Failed VirtualAlloc.\n", Work->No); goto cleanup; } break; default: // // Nothing to prepare. // break; } while (GetTickCount() < EndTime) { // // Check if we are asked to stop. // WaitResult = WaitForSingleObject(Work->StopEvent, 0); if (WaitResult == WAIT_OBJECT_0) { ErrorCode = ERROR_SUCCESS; goto cleanup; } // // Do a unit of work that should not take more than several // tens of milliseconds. // switch (Work->Type) { case CpuWork: RunTillTime = GetTickCount() + 10; while (GetTickCount() < RunTillTime) ; break; case DiskWork: // // Seek to random position. // SeekPosition.QuadPart = rand() * 4 * 1024; SeekPosition.QuadPart %= VolumeSize.QuadPart; if (!SetFilePointerEx(DiskHandle, SeekPosition, NULL, FILE_BEGIN)) { printf("W%d: Failed SetFilePointerEx.\n", Work->No); ErrorCode = GetLastError(); goto cleanup; } // // Issue read. // ReadResult = ReadFile(DiskHandle, ReadBuffer, MAX_READ_SIZE, &NumBytesRead, NULL); if (!ReadResult) { printf("W%d: Failed ReadFile.\n", Work->No); ErrorCode = GetLastError(); goto cleanup; } break; default: printf("W%d: Not valid work type %d!\n", Work->No, Work->Type); ErrorCode = ERROR_INVALID_PARAMETER; goto cleanup; } } ErrorCode = ERROR_SUCCESS; cleanup: if (DiskHandle) { CloseHandle(DiskHandle); } if (ReadBuffer) { VirtualFree(ReadBuffer, 0, MEM_RELEASE); } printf("W%d: Exiting with error code: %d\n", Work->No, ErrorCode); return ErrorCode; } DWORD WINAPI TaskThreadProc( LPVOID lpParameter ) { PTESTTASK Task = lpParameter; TESTWORK Work; DWORD ErrorCode; WORKTYPE Type; DWORD WaitResult; DWORD WaitForStart; HANDLE WorkerThreadHandle; DWORD WorkLength; HANDLE Events[2]; DWORD ElapsedTime; DWORD StartTime; ULONG TryIdx; BOOLEAN RegisteredIdleTask; // // Initialize locals. // RegisteredIdleTask = FALSE; WorkerThreadHandle = NULL; RtlZeroMemory(&Work, sizeof(Work)); // // Initialize work structure. // Work.StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!Work.StopEvent) { goto cleanup; } Work.No = Task->No; // // Loop registering, running, unregistering idle tasks. // while (TRUE) { // // If we are force-processing all tasks, usually wait for all tasks // to complete before queueing a new task. // if (g_ProcessingIdleTasks) { if ((rand() % 3) != 0) { printf("%d: Waiting for g_ProcessedIdleTasksEvent\n", Task->No); WaitResult = WaitForSingleObject(g_ProcessedIdleTasksEvent, INFINITE); if (WaitResult != WAIT_OBJECT_0) { ErrorCode = GetLastError(); printf("%d: Failed wait for g_ProcessedIdleTasksEvent=%x\n", Task->No, ErrorCode); goto cleanup; } } } // // Register the idle task. // ErrorCode = RegisterIdleTask(Task->Id, &Task->ItHandle, &Task->StartEvent, &Task->StopEvent); if (ErrorCode != ERROR_SUCCESS) { printf("%d: Could not register: %d\n", Task->No, ErrorCode); goto cleanup; } RegisteredIdleTask = TRUE; // // Determine task parameters. // Type = rand() % MaxWorkType; WaitForStart = rand() % MAX_WAIT_FOR_START; WorkLength = rand() % MAX_WORK_LENGTH; // // Update work item. // Work.Type = Type; Work.WorkLength = WorkLength; printf("%d: NewTask Type=%d,WStart=%d,Length=%d,Handle=%p\n", Task->No, Type, WaitForStart, WorkLength, Task->ItHandle); do { // // Wait to be signaled. // printf("%d: Waiting for start\n", Task->No); WaitResult = WaitForSingleObject(Task->StartEvent, WaitForStart); if (WaitResult == WAIT_TIMEOUT) { printf("%d: Timed out wait for start. Re-registering\n", Task->No); break; } // // Spawn the work. // ResetEvent(Work.StopEvent); StartTime = GetTickCount(); WorkerThreadHandle = CreateThread(NULL, 0, DoWorkThreadProc, &Work, 0, NULL); if (!WorkerThreadHandle) { ErrorCode = GetLastError(); printf("%d: Failed spawn work: %d\n", Task->No, ErrorCode); goto cleanup; } // // Wait for stop event to be signaled or the work to be // completed. // Events[0] = WorkerThreadHandle; Events[1] = Task->StopEvent; printf("%d: Waiting for stop or workdone\n", Task->No); WaitResult = WaitForMultipleObjects(2, Events, FALSE, INFINITE); if (WaitResult == WAIT_OBJECT_0) { // // Break out if the work was done. // printf("%d: Work done.\n", Task->No); CloseHandle(WorkerThreadHandle); WorkerThreadHandle = NULL; break; } else if (WaitResult == WAIT_OBJECT_0 + 1) { // // We were told to stop. Signal the worker thread and // wait. // printf("%d: Stopped, Waiting for thread to exit\n", Task->No); SetEvent(Work.StopEvent); WaitForSingleObject(WorkerThreadHandle, INFINITE); CloseHandle(WorkerThreadHandle); WorkerThreadHandle = NULL; // // This is not really the time we worked (e.g. we may be // switched out etc.) We want to keep rolling and this is // what we can get easily. // ElapsedTime = GetTickCount() - StartTime; if (ElapsedTime > Work.WorkLength) { // // We've gone too long with this work. Unregistester // this task and pick another one. // break; } Work.WorkLength -= ElapsedTime; // // Loop on until we pass enough time with this work. // } else { // // There was an error. // ErrorCode = GetLastError(); printf("%d: WaitForMultipleObjects failed: %d\n", Task->No, ErrorCode); goto cleanup; } } while (TRUE); ASSERT(RegisteredIdleTask); UnregisterIdleTask(Task->ItHandle, Task->StartEvent, Task->StopEvent); RegisteredIdleTask = FALSE; } cleanup: if (RegisteredIdleTask) { UnregisterIdleTask(Task->ItHandle, Task->StartEvent, Task->StopEvent); } if (WorkerThreadHandle) { SetEvent(Work.StopEvent); WaitForSingleObject(WorkerThreadHandle, INFINITE); CloseHandle(WorkerThreadHandle); } if (Work.StopEvent) { CloseHandle(Work.StopEvent); } return ErrorCode; } int __cdecl main(int argc, char* argv[]) { DWORD ErrorCode; ULONG TaskIdx; IT_IDLE_DETECTION_PARAMETERS Parameters; PTESTTASK Task; INPUT MouseInput; ULONG SleepTime; // // Initialize locals. // RtlZeroMemory(&MouseInput, sizeof(MouseInput)); MouseInput.type = INPUT_MOUSE; MouseInput.mi.dwFlags = MOUSEEVENTF_MOVE; // // Initialize globals. // g_ProcessingIdleTasks = FALSE; g_ProcessedIdleTasksEvent = NULL; // // Initialize random. // srand((unsigned)time(NULL)); // // Create an manual reset event that will be signaled when we finish // processing all tasks after telling the server to process all tasks. // g_ProcessedIdleTasksEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!g_ProcessedIdleTasksEvent) { ErrorCode = GetLastError(); printf("Failed to create g_ProcessedIdleTasksEvent=%x\n",ErrorCode); goto cleanup; } // // Set idle detection parameters for stress. // Parameters.IdleDetectionPeriod = 1000; Parameters.IdleVerificationPeriod = 500; Parameters.NumVerifications = 2; Parameters.IdleInputCheckPeriod = 100; Parameters.IdleTaskRunningCheckPeriod = 1000; Parameters.MinCpuIdlePercentage = 50; Parameters.MinDiskIdlePercentage = 50; Parameters.MaxNumRegisteredTasks = 500; RpcTryExcept { ErrorCode = ItSrvSetDetectionParameters(NULL, &Parameters); } RpcExcept(IT_RPC_EXCEPTION_HANDLER()) { ErrorCode = RpcExceptionCode(); } RpcEndExcept if (ErrorCode != ERROR_SUCCESS) { printf("Failed set idle detection params for stress.\n"); goto cleanup; } // // Register and start tasks. // for (TaskIdx = 0; TaskIdx < NUM_TEST_TASKS; TaskIdx++) { Task = &g_Tasks[TaskIdx]; Task->No = TaskIdx; Task->Id = ItOptimalDiskLayoutTaskId; Task->ThreadHandle = CreateThread(NULL, 0, TaskThreadProc, &g_Tasks[TaskIdx], 0, 0); if (!Task->ThreadHandle) { ErrorCode = GetLastError(); printf("Could not spawn task %d: %x\n", TaskIdx, ErrorCode); goto cleanup; } } // // Loop forever sending input messages once in a while to stop // idle tasks. // while (1) { SleepTime = MAX_WAIT_FOR_START * (rand() % 64) / 64; Sleep(SleepTime); // // Every so often, ask all idle tasks to be processed. // if ((rand() % 2) == 0) { if ((rand() % 2) == 0) { printf("MainThread: Sending user input before processing all tasks\n"); SendInput(1, &MouseInput, sizeof(MouseInput)); } printf("MainThread: ProcessIdleTasks()\n"); ResetEvent(g_ProcessedIdleTasksEvent); g_ProcessingIdleTasks = TRUE; ErrorCode = ProcessIdleTasks(); printf("MainThread: ProcessIdleTasks()=%x\n",ErrorCode); g_ProcessingIdleTasks = FALSE; SetEvent(g_ProcessedIdleTasksEvent); if (ErrorCode != ERROR_SUCCESS) { goto cleanup; } } if ((rand() % 2) == 0) { printf("MainThread: Sending user input\n"); SendInput(1, &MouseInput, sizeof(MouseInput)); } } cleanup: if (g_ProcessedIdleTasksEvent) { CloseHandle(g_ProcessedIdleTasksEvent); } return ErrorCode; } /*********************************************************************/ /* MIDL allocate and free */ /*********************************************************************/ void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len) { return(HeapAlloc(GetProcessHeap(),0,(len))); } void __RPC_USER midl_user_free(void __RPC_FAR * ptr) { HeapFree(GetProcessHeap(),0,(ptr)); }