#include "stdafx.h" #include "OSAL/Task.h" #include #include //const UINT32 NTTask::kDefaultPriority; //const INT32 NTTask::kInvalidTaskID; /** * Create but don't launch a task. * @param name The name of the task. "FRC_" will be prepended to the task name. * @param function The address of the function to run as the new task. * @param priority The priority for the task. * @param stackSize The size of the stack for the task */ NTTask::NTTask(const char* name, FUNCPTR function, INT32 priority, UINT32 stackSize) { //m_taskID = kInvalidTaskID; m_Handle=NULL; m_function = function; m_priority = priority; m_stackSize = stackSize; m_taskName = new char[strlen(name) + 5]; strcpy(m_taskName, "FRC_"); strcpy(m_taskName+4, name); //TODO see if we want to debug out this... it may be interesting info #if 0 static INT32 instances = 0; instances++; #endif } NTTask::~NTTask() { //if (m_taskID != kInvalidTaskID) Stop(); if (m_Handle) Stop(); delete [] m_taskName; m_taskName = NULL; } // The thread callback DWORD thread_proc( void *p_ptr ) { // Get the pointer to myself NTTask *p_this = (NTTask*)p_ptr; assert( p_this ); (*p_this->m_function)( p_this->m_Arg ); return 0; } //This sets the name of the thread, which can help to identify threads in win32 #define MS_VC_EXCEPTION 0x406D1388 static void set_thread_name( const char *p_thread_name, DWORD ID ) { #pragma pack(push,8) typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) // Set the information THREADNAME_INFO info = { 0x1000, p_thread_name, ID, 0 }; // Raise the exception __try { ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); } __except( EXCEPTION_EXECUTE_HANDLER ) {} } bool NTTask::StartInternal() { if (m_Handle) { assert(false); // This may be lifted... just want to see if it happens Stop(); } m_Handle = ::CreateThread( NULL, m_stackSize, (LPTHREAD_START_ROUTINE)thread_proc, (void*)this, NULL, &m_ID ); if (m_ID!=NULL) set_thread_name(m_taskName,m_ID); return m_Handle!=NULL; } /** * Starts this task. * If it is already running or unable to start, it fails and returns false. */ bool NTTask::Start(void *arg0) { m_Arg=arg0; return StartInternal(); } /** * Restarts a running task. * If the task isn't started, it starts it. * @return false if the task is running and we are unable to kill the previous instance */ bool NTTask::Restart() { //return HandleError(taskRestart(m_taskID)); Stop(); return StartInternal(); } /** * Kills the running task. * @returns true on success false if the task doesn't exist or we are unable to kill it. */ bool NTTask::Stop() { if (!m_Handle) return false; bool ok = true; // Wait for the thread to finish #ifdef _DEBUG try_again: #endif _DEBUG //const int TimeOut=2000; const int TimeOut=INFINITE; if ( ::WaitForSingleObject( m_Handle , TimeOut ) == WAIT_TIMEOUT ) { // Signal the thread as having been terminated //if ( m_p_error ) *m_p_error = true; // If this gets triggered we have a bug in the code. #ifdef _DEBUG switch( ::MessageBoxW( NULL, L"A thread being used by the application\n" L"has taken to long to exit and so is about\n" L"to be terminated to avoid locking-up\n" L"the application.\n\n" L"Click ABORT to debug.\n" L"Click RETRY to wait for a bit longer.\n" L"Click IGNORE to terminate the thread.\n\n" L"This message is NOT displayed in release mode.", L"Thread exit has timed out.", MB_ABORTRETRYIGNORE ) ) { case IDRETRY: goto try_again; case IDABORT: ::DebugBreak(); break; case IDIGNORE: break; } #endif _DEBUG // Free thread memory CONTEXT c_ = {0}; c_.ContextFlags = CONTEXT_FULL; ::GetThreadContext( m_Handle, &c_ ); MEMORY_BASIC_INFORMATION Info_ = {0}; #ifdef _M_X64 ::VirtualQuery( (PVOID) c_.Rsp, &Info_, sizeof(Info_) ); #else ::VirtualQuery( (PVOID) c_.Esp, &Info_, sizeof(Info_) ); #endif // Terminate the thread ::TerminateThread( m_Handle, 0 ); // Free the memory ::VirtualFree( Info_.AllocationBase, 0, MEM_RELEASE ); } //if (Verify()) //{ // ok = HandleError(taskDelete(m_taskID)); //} //m_taskID = kInvalidTaskID; // The thread has finished CloseHandle( m_Handle ); m_Handle = NULL; return ok; } /** * Returns true if the task is ready to execute (i.e. not suspended, delayed, or blocked). * @return true if ready, false if not ready. */ bool NTTask::IsReady() { //return taskIsReady(m_taskID); return m_Handle!=NULL; } /** * Returns true if the task was explicitly suspended by calling Suspend() * @return true if suspended, false if not suspended. */ bool NTTask::IsSuspended() { //return taskIsSuspended(m_taskID); return false; } /** * Pauses a running task. * Returns true on success, false if unable to pause or the task isn't running. */ bool NTTask::Suspend() { //return HandleError(taskSuspend(m_taskID)); assert(false); return false; } /** * Resumes a paused task. * Returns true on success, false if unable to resume or if the task isn't running/paused. */ bool NTTask::Resume() { //return HandleError(taskResume(m_taskID)); assert(false); return false; } /** * Verifies a task still exists. * @returns true on success. */ bool NTTask::Verify() { //return taskIdVerify(m_taskID) == OK; return true; } /** * Gets the priority of a task. * @returns task priority or 0 if an error occured */ INT32 NTTask::GetPriority() { //if (HandleError(taskPriorityGet(m_taskID, &m_priority))) // return m_priority; //else // return 0; return m_priority; } /** * This routine changes a task's priority to a specified priority. * Priorities range from 0, the highest priority, to 255, the lowest priority. * Default task priority is 100. * @param priority The priority the task should run at. * @returns true on success. */ bool NTTask::SetPriority(INT32 priority) { m_priority = priority; //return HandleError(taskPrioritySet(m_taskID, m_priority)); return true; } /** * Returns the name of the task. * @returns Pointer to the name of the task or NULL if not allocated */ const char* NTTask::GetName() { return m_taskName; } /** * Get the ID of a task * @returns Task ID of this task. NTTask::kInvalidTaskID (-1) if the task has not been started or has already exited. */ INT32 NTTask::GetID() { //if (Verify()) // return m_taskID; if (m_Handle) return m_ID; return kInvalidTaskID; } /** * Handles errors generated by task related code. */ bool NTTask::HandleError(STATUS results) { if (results != ERROR) return true; //switch(errnoGet()) //{ //case S_objLib_OBJ_ID_ERROR: // wpi_setWPIErrorWithContext(TaskIDError, m_taskName); // break; // //case S_objLib_OBJ_DELETED: // wpi_setWPIErrorWithContext(TaskDeletedError, m_taskName); // break; // //case S_taskLib_ILLEGAL_OPTIONS: // wpi_setWPIErrorWithContext(TaskOptionsError, m_taskName); // break; // //case S_memLib_NOT_ENOUGH_MEMORY: // wpi_setWPIErrorWithContext(TaskMemoryError, m_taskName); // break; // //case S_taskLib_ILLEGAL_PRIORITY: // wpi_setWPIErrorWithContext(TaskPriorityError, m_taskName); // break; //default: // printErrno(errnoGet()); // wpi_setWPIErrorWithContext(TaskError, m_taskName); //} return false; }