#include "thread.h" // Thread callback... #pragma LOCKEDCODE VOID CThread::ThreadFunction(CThread* Thread) { if(Thread) Thread->ThreadRoutine(NULL); } #pragma PAGEDCODE VOID CThread::ThreadRoutine(PVOID context) { NTSTATUS status; TRACE("================= STARTING THREAD %8.8lX ===============\n", thread); // Wait for a request to Start pooling or for // someone to kill this thread. PVOID mainevents[] = {(PVOID) &evKill,(PVOID) &evStart}; PVOID pollevents[] = {(PVOID) &evKill,(PVOID) timer->getHandle(),(PVOID) &smOnDemandStart}; ASSERT(arraysize(mainevents) <= THREAD_WAIT_OBJECTS); ASSERT(arraysize(pollevents) <= THREAD_WAIT_OBJECTS); BOOLEAN kill = FALSE; while (!kill && thread) { // until told to start or to quit ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL); // Before going to thread routine thread considered to be Idle if(event) event->set(&evIdle, IO_NO_INCREMENT, FALSE); status = event->waitForMultipleObjects(arraysize(mainevents), mainevents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL); if(!NT_SUCCESS(status)) { // error in wait TRACE("Thread: waitForMultipleObjects failed - %X\n", status); break; } if (status == STATUS_WAIT_0) { DEBUG_START(); TRACE("Request to kill thread arrived...\n"); TRACE("================= KILLING THREAD! ===============\n"); break; // kill event was set } // Starting the timer with a zero due time will cause us to perform the // first poll immediately. Thereafter, polls occur at the POLLING_INTERVAL // interval (measured in milliseconds). // Now thread is busy... if(event) event->clear(&evIdle); LARGE_INTEGER duetime = {0};// Signal timer right away! timer->set(duetime, PoolingTimeout, NULL); while (TRUE) { // Block until time to poll again ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL); status = event->waitForMultipleObjects(arraysize(pollevents), pollevents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL); if (!NT_SUCCESS(status)) { // error in wait DEBUG_START(); TRACE("CTread - waitForMultipleObjects failed - %X\n", status); TRACE("================= KILLING THREAD! ===============\n"); timer->cancel(); kill = TRUE; break; } if (status == STATUS_WAIT_0) { // told to quit DEBUG_START(); TRACE("Loop: Request to kill thread arrived...\n"); TRACE("================= KILLING THREAD! ===============\n"); timer->cancel(); status = STATUS_DELETE_PENDING; kill = TRUE; break; } //if(device) if(pfClientThreadFunction) { if(StopRequested) break; // Do device specific thread processing... //TRACE("Calling thread %8.8lX function...\n",thread); if(status = pfClientThreadFunction(ClientContext)) { TRACE("Device reported error %8.8lX\n",status); timer->cancel(); break; } } else { DEBUG_START(); TRACE("================= THREAD FUNCTION POINTER IS NOT SET!! FINISHED... ===============\n"); TRACE("================= KILLING THREAD! ===============\n"); status = STATUS_DELETE_PENDING; kill = TRUE; break; } } }// until told to quit TRACE(" Leaving thread %8.8lX...\n", thread); if(event) event->set(&evIdle, IO_NO_INCREMENT, FALSE); if(event) event->set(&evStopped, IO_NO_INCREMENT, FALSE); if(semaphore) semaphore->initialize(&smOnDemandStart, 0, MAXLONG); if(system) system->terminateSystemThread(STATUS_SUCCESS); } #pragma PAGEDCODE CThread::CThread(PCLIENT_THREAD_ROUTINE ClientThreadFunction,PVOID ClientContext, ULONG delay) { // StartPollingThread for the device NTSTATUS status; HANDLE hthread; m_Status = STATUS_INSUFFICIENT_RESOURCES; //this->device = device; // Create objects.. event = kernel->createEvent(); system = kernel->createSystem(); timer = kernel->createTimer(SynchronizationTimer); semaphore = kernel->createSemaphore(); debug = kernel->createDebug(); StopRequested = FALSE; ThreadActive = FALSE; if(ALLOCATED_OK(event)) { event->initialize(&evKill, NotificationEvent, FALSE); event->initialize(&evStart, SynchronizationEvent, FALSE); event->initialize(&evStopped, NotificationEvent, FALSE); event->initialize(&evIdle, NotificationEvent, TRUE); } // At the begining there is no request to start, // so semaphore is not at signal state. if(ALLOCATED_OK(semaphore)) semaphore->initialize(&smOnDemandStart, 0, MAXLONG); pfClientThreadFunction = ClientThreadFunction; this->ClientContext = ClientContext; PoolingTimeout = delay; // Default thread pooling interval... // Create system thread object... status = system->createSystemThread(&hthread, THREAD_ALL_ACCESS, NULL, NULL, NULL, (PKSTART_ROUTINE) ThreadFunction, this); if(NT_SUCCESS(status)) // Get thread pointer... { thread = NULL; status = system->referenceObjectByHandle(hthread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*) &thread, NULL); if(!NT_SUCCESS(status)) { TRACE("FAILED TO REFERENCE OBJECT! Error %8.8lX\n", status); } } else TRACE("FAILED TO CREATE SYSTEM THREAD! Error %8.8lX\n", status); system->ZwClose(hthread); if(NT_SUCCESS(status) && ALLOCATED_OK(event)&& ALLOCATED_OK(system)&& ALLOCATED_OK(timer)&& ALLOCATED_OK(semaphore) && thread) m_Status = STATUS_SUCCESS; } // StartPollingThread #pragma PAGEDCODE CThread::~CThread() { // StopPollingThread DEBUG_START(); TRACE("Terminating thread %8.8lX...\n", thread); if(event) event->set(&evKill, IO_NO_INCREMENT, FALSE); StopRequested = TRUE; //device = NULL; if (thread) { // wait for the thread to die if(system && event) { ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL); event->waitForSingleObject(&evStopped, Executive, KernelMode, FALSE, NULL); if(!isWin98()) event->waitForSingleObject(thread, Executive, KernelMode, FALSE, NULL); system->dereferenceObject(thread); thread = NULL; } } TRACE("Thread terminated...\n"); if(event) event->dispose(); if(system) system->dispose(); if(timer) timer->dispose(); if(semaphore) semaphore->dispose(); if(debug) debug->dispose(); } #pragma PAGEDCODE VOID CThread::kill() { DEBUG_START(); TRACE("Killing thread %8.8lX...\n", thread); StopRequested = TRUE; if(system) { ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL); } if(event) event->set(&evKill, IO_NO_INCREMENT, FALSE); if(event) event->waitForSingleObject(&evStopped, Executive, KernelMode, FALSE, NULL); } #pragma PAGEDCODE VOID CThread::start() { DEBUG_START(); TRACE("Starting thread %8.8lX...\n", thread); if(system) { ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL); } StopRequested = FALSE; ThreadActive = TRUE; // Start Card pooling... if(event) event->set(&evStart, IO_NO_INCREMENT, FALSE); } #pragma PAGEDCODE VOID CThread::stop() { DEBUG_START(); TRACE("Stop thread %8.8lX...\n", thread); StopRequested = TRUE; ThreadActive = FALSE; if(system) { ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL); } if(event) event->clear(&evStart); // Unblock thread if it is blocked... if(semaphore) semaphore->release(&smOnDemandStart,0,1,FALSE); // Wait for for the thread to go to the idle state... if(event) event->waitForSingleObject(&evIdle, Executive, KernelMode, FALSE, NULL); // Stop thread ... if(semaphore) semaphore->initialize(&smOnDemandStart, 0, MAXLONG); } #pragma PAGEDCODE BOOL CThread::isThreadActive() { return ThreadActive; } #pragma PAGEDCODE VOID CThread::setPoolingInterval(ULONG delay) { PoolingTimeout = delay; }; #pragma PAGEDCODE VOID CThread::callThreadFunction() { // This will force thread function to be called right away. // Useful if we want to update some information or // start some processing without waiting for the pooling // timeout to occure. if(semaphore) semaphore->release(&smOnDemandStart,0,1,FALSE); };