Created
June 7, 2011 22:08
-
-
Save Rolias/1013288 to your computer and use it in GitHub Desktop.
TaskBase - Parent class for all tasks started in NetBurner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* TaskBase.cpp | |
* | |
* Created on: Mar 2, 2010 | |
* Author: Tod Gentille | |
* Syncor Systems, Inc. | |
*/ | |
#include "TaskBase.h" | |
#include <iostream> | |
#include <ucos.h> | |
#include "../Utilities/DebugUtil.h" | |
using namespace std; | |
namespace SyncorLibrary | |
{ | |
TaskBase::TaskBase(uint8_t taskPriority, uint32_t taskSize) : | |
_taskSize(0), _taskIsInitialized(false), _taskPriority(TCP_PRIO), _pTaskStackBottom(0), _pTaskStackTop(0), | |
_maximumAllowedPriority(TCP_PRIO + 15), _minimumAllowedPriority(TCP_PRIO), _destructorForceQuit(false) | |
{ | |
Construct(taskPriority, taskSize); | |
} | |
TaskBase::TaskBase(const TaskBase& rhs) : | |
_taskSize(0), _taskIsInitialized(false), _taskPriority(TCP_PRIO), _pTaskStackBottom(0), _pTaskStackTop(0), | |
_maximumAllowedPriority(TCP_PRIO + 15), _minimumAllowedPriority(TCP_PRIO), _destructorForceQuit(false) | |
{ | |
Construct(rhs._taskPriority, rhs._taskSize); | |
} | |
TaskBase::~TaskBase() | |
{ | |
try | |
{ | |
if (!TaskInitialized()) | |
{ | |
delete[] _pTaskStackBottom; | |
_pTaskStackBottom = NULL; | |
_pTaskStackTop = NULL; | |
} | |
else | |
{ | |
//This is not good. The main loop is running but the owning object has gone out of scope | |
//We have to force quit the loop if we don't want to trap. | |
cout | |
<< "TaskBase destructor was called but TaskCompleted() was not called. The task may still be running" | |
<< " so the stack memory can't be cleaned up. This is a possible memory leak." << endl; | |
} | |
} | |
catch (...) //just preventing throw from leaving destructor | |
{ | |
} | |
} | |
bool TaskBase::WaitForTaskToExit() | |
{ | |
_destructorForceQuit = true; | |
int delay_counter = 0; | |
while (++delay_counter < 30) | |
{ | |
OSTimeDly(TICKS_PER_SECOND); | |
if (!TaskInitialized()) | |
{ | |
break; | |
} | |
} | |
return TaskInitialized(); | |
} | |
//Common code for constructors | |
void TaskBase::Construct(const uint8_t taskPriority, const uint32_t taskSize) | |
{ | |
_taskSize = taskSize; | |
_taskIsInitialized = false; | |
// _debuggingTask =true; | |
_pTaskStackBottom = new DWORD[taskSize]; | |
_pTaskStackTop = _pTaskStackBottom + _taskSize; | |
_maximumAllowedPriority = TCP_PRIO + 15; | |
_minimumAllowedPriority = TCP_PRIO; | |
SetTaskPriority(taskPriority); | |
_destructorForceQuit = false; | |
} | |
bool TaskBase::SetTaskPriority(const uint8_t value) | |
{ | |
const uint8_t orig_priority = _taskPriority; | |
if (value < GetMinTaskPriority()) | |
{ | |
_taskPriority = GetMinTaskPriority(); | |
} | |
else if (value > GetMaxTaskPriority()) | |
{ | |
_taskPriority = GetMaxTaskPriority(); | |
} | |
else | |
{ | |
_taskPriority = value; | |
} | |
if (_taskIsInitialized) | |
{ | |
const BYTE os_err = OSChangePrio(_taskPriority); | |
if (os_err == OS_PRIO_EXIST) | |
{ | |
_taskPriority = orig_priority; | |
return false; | |
} | |
} | |
return true; | |
} | |
bool TaskBase::TryLowerPriority() | |
{ | |
SetTaskPriority(_taskPriority + 1); //Bigger number is lower priority | |
if (_taskPriority == GetMaxTaskPriority()) | |
{ | |
return false; | |
} | |
return true; | |
} | |
void TaskBase::Startup() | |
{ | |
DebugUtil::Write("In TaskBase Startup()",DebugLevel::Verbose); | |
if (_taskIsInitialized) | |
{ | |
DebugUtil::Write("Task is already initialized."); | |
return; | |
} | |
bool keep_trying = true; | |
while (keep_trying) | |
{ | |
const BYTE os_err = OSTaskCreate(InitializeWrapper, this, _pTaskStackTop, _pTaskStackBottom, _taskPriority); | |
if (os_err == OS_NO_ERR) | |
{ | |
keep_trying = false; | |
_taskIsInitialized = true; | |
} | |
else if (os_err == OS_PRIO_EXIST) | |
{ | |
keep_trying = TryLowerPriority(); | |
DebugUtil::Write("trying lower priority."); | |
} | |
} | |
} | |
void TaskBase::Shutdown() | |
{ | |
TaskCompleted(); | |
DebugUtil::Write("TaskBase::Shutdown has been invoked."); | |
} | |
void TaskBase::InitializeWrapper(void * const pTaskBase) | |
{ | |
TaskBase* const pTask_base = static_cast<TaskBase*> (pTaskBase); | |
pTask_base->Initialize(0); | |
//In a lot of cases Initialize will be a forever loop and won't return here. | |
//If it does return it means the task is done so go ahead and shut it down | |
pTask_base->Shutdown(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* TaskBase.h | |
* | |
* Created on: Mar 2, 2010 | |
* Author: Tod | |
*/ | |
#ifndef TASKBASE_H_ | |
#define TASKBASE_H_ | |
#include <constants.h> | |
#include <basictypes.h> | |
namespace SyncorLibrary | |
{ | |
const uint8_t DEFAULT_PRIORITY = MAIN_PRIO - 1; | |
class TaskBase | |
{ | |
public: | |
explicit TaskBase(uint8_t taskPriority = DEFAULT_PRIORITY, uint32_t taskStackSize = USER_TASK_STK_SIZE); | |
virtual ~TaskBase(); | |
TaskBase(const TaskBase& rhs); //copy constructor | |
void Startup(); | |
virtual void Shutdown(); | |
bool SetTaskPriority(const uint8_t value); | |
int GetTaskPriority() const | |
{ | |
return _taskPriority; | |
} | |
uint8_t GetMaxTaskPriority() const | |
{ | |
return _maximumAllowedPriority; | |
} | |
bool SetMaxTaskPriority(const uint8_t value) | |
{ | |
if ((value < MAX_ALLOWED_PRIORITY) && (value > GetMinTaskPriority() )) | |
{ | |
_maximumAllowedPriority = value; | |
return true; | |
} | |
return false; | |
} | |
uint8_t GetMinTaskPriority() const | |
{ | |
return _minimumAllowedPriority; | |
} | |
bool SetMinTaskPriority(const uint8_t value) | |
{ | |
if ((value > MIN_ALLOWED_PRIORITY) && (value < GetMaxTaskPriority() )) | |
{ | |
_minimumAllowedPriority = value; | |
return true; | |
} | |
return false; | |
} | |
bool TaskInitialized() const | |
{ | |
return _taskIsInitialized; | |
} | |
void TaskCompleted() | |
{ | |
_taskIsInitialized = false; | |
} | |
bool IsForceQuitRequested() const | |
{ | |
return _destructorForceQuit; | |
} | |
protected: | |
virtual void Initialize(const void * const pd)=0; | |
private: | |
uint32_t _taskSize; | |
bool _taskIsInitialized; | |
uint8_t _taskPriority; | |
DWORD* _pTaskStackBottom; | |
DWORD* _pTaskStackTop; | |
uint8_t _maximumAllowedPriority; | |
uint8_t _minimumAllowedPriority; | |
bool _destructorForceQuit; | |
void Construct(const uint8_t taskPriority, const uint32_t taskSize); | |
bool TryLowerPriority(); | |
bool WaitForTaskToExit(); | |
static const uint8_t MAX_ALLOWED_PRIORITY = 62;//63 is used by idle task | |
static const uint8_t MIN_ALLOWED_PRIORITY = 1; | |
static void InitializeWrapper(void* const pTaskBase); | |
//Hide operator= | |
TaskBase& operator= (const TaskBase& rhs); | |
}; | |
} | |
#endif /* TASKBASE_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* TestTaskBase.cpp | |
* | |
* Created on: Mar 2, 2010 | |
* Author: Tod Gentille | |
* Syncor Systems, Inc. | |
*/ | |
#include <UnitTest++.h> | |
#include <Startup/TaskBase.h> | |
#include <iostream> | |
#include <ucos.h> | |
#include <utils.h> | |
#include <Bsp.h> | |
using namespace std; | |
using namespace SyncorLibrary; | |
class SomeConcreteTask: public TaskBase | |
{ | |
public: | |
SomeConcreteTask() : | |
_stayAlive(true) | |
{ | |
} | |
bool _stayAlive; | |
void Initialize(const void * const pd) | |
{ | |
while (_stayAlive) | |
{ | |
OSTimeDly(1);; | |
} | |
} | |
}; | |
void ShowRamAtStart() | |
{ | |
DWORD ram_at_start = spaceleft(); | |
cout << "start:" << ram_at_start << endl; | |
} | |
struct MemoryChecker | |
{ | |
DWORD _ramAtStart; | |
DWORD _ramAtEnd; | |
} _memoryChecker; | |
struct TaskBaseFixture | |
{ | |
SomeConcreteTask _myTask; | |
SomeConcreteTask _mySecondTask; | |
}; | |
SUITE(TaskBaseClass) | |
{ | |
TEST_FIXTURE(TaskBaseFixture, CanSetPriorityToMax) | |
{ | |
//Get the memory after the constructor for _myTask has run | |
//This has to be done inside a test. | |
_memoryChecker._ramAtStart = spaceleft(); | |
int expected = _myTask.GetMaxTaskPriority(); | |
_myTask.SetTaskPriority(expected); | |
CHECK_EQUAL(expected, _myTask.GetTaskPriority()); | |
} | |
TEST_FIXTURE(TaskBaseFixture, CanSetPriorityToMin) | |
{ | |
int expected = _myTask.GetMinTaskPriority(); | |
_myTask.SetTaskPriority(expected); | |
CHECK_EQUAL(expected, _myTask.GetTaskPriority()); | |
} | |
TEST_FIXTURE(TaskBaseFixture, CantSetPriorityBelowMin) | |
{ | |
int expected = _myTask.GetMinTaskPriority(); | |
_myTask.SetTaskPriority(expected - 1); | |
CHECK_EQUAL(expected, _myTask.GetTaskPriority()); | |
} | |
TEST_FIXTURE(TaskBaseFixture, TaskCanBeSafelyStartedTwiceWithNoEffect) | |
{ | |
_myTask.SetTaskPriority(HTTP_PRIO - 1); | |
_myTask.Startup(); | |
_myTask.Startup(); | |
CHECK_EQUAL(true, _myTask.TaskInitialized() ); | |
_myTask._stayAlive = false; | |
OSTimeDly(2); | |
} | |
TEST_FIXTURE(TaskBaseFixture, TaskCanBeStartedAndStopped) | |
{ | |
_myTask.SetTaskPriority(HTTP_PRIO - 1); | |
CHECK_EQUAL(false, _myTask.TaskInitialized() ); | |
_myTask.Startup(); | |
CHECK_EQUAL(true,_myTask.TaskInitialized()); | |
_myTask._stayAlive = false; | |
OSTimeDly(2); //wait for it to die. | |
CHECK_EQUAL(false, _myTask.TaskInitialized() ); | |
} | |
TEST_FIXTURE(TaskBaseFixture, TaskPriorityCanChangeAfterTaskStarts) | |
{ | |
_myTask.SetTaskPriority(MAIN_PRIO - 1); | |
_myTask.Startup(); | |
OSTimeDly(1); | |
CHECK_EQUAL(true,_myTask.TaskInitialized()); | |
int initial_priority = _myTask.GetTaskPriority(); | |
_myTask.SetTaskPriority(initial_priority - 1); | |
int new_priority = _myTask.GetTaskPriority(); | |
CHECK (new_priority < initial_priority); | |
_myTask._stayAlive = false; | |
OSTimeDly(2); | |
CHECK_EQUAL(false, _myTask.TaskInitialized() ); | |
} | |
TEST_FIXTURE(TaskBaseFixture, TwoTasksStartedAtSamePriorityUseDifferentPriorities) | |
{ | |
_myTask.SetTaskPriority(MAIN_PRIO - 1); | |
_mySecondTask.SetTaskPriority(MAIN_PRIO - 1); | |
_myTask.Startup(); | |
_mySecondTask.Startup(); | |
OSTimeDly(1); | |
CHECK_EQUAL(true,_myTask.TaskInitialized()); | |
CHECK_EQUAL(true,_mySecondTask.TaskInitialized()); | |
CHECK(_myTask.GetTaskPriority() != _mySecondTask.GetTaskPriority()); | |
_myTask._stayAlive = false; | |
_mySecondTask._stayAlive = false; | |
OSTimeDly(2); //wait for it to die. | |
CHECK_EQUAL(false, _myTask.TaskInitialized() ); | |
CHECK_EQUAL(false, _mySecondTask.TaskInitialized() ); | |
} | |
TEST_FIXTURE(TaskBaseFixture, TwoTasksWontStartIfAHigherPriorityIsNotAvailable) | |
{ | |
_myTask.SetTaskPriority(_myTask.GetMaxTaskPriority()); | |
//NOTE: using max priority from _myTask not _mySecondTask on purpose | |
_mySecondTask.SetTaskPriority(_myTask.GetMaxTaskPriority()); | |
_myTask.Startup(); | |
_mySecondTask.Startup(); | |
CHECK_EQUAL(true,_myTask.TaskInitialized()); | |
CHECK_EQUAL(false,_mySecondTask.TaskInitialized()); | |
CHECK(_myTask.GetTaskPriority() == _mySecondTask.GetTaskPriority()); | |
_myTask._stayAlive = false; | |
_mySecondTask._stayAlive = false; | |
OSTimeDly(2); //wait for it to die. | |
CHECK_EQUAL(false, _myTask.TaskInitialized() ); | |
CHECK_EQUAL(false, _mySecondTask.TaskInitialized() ); | |
} | |
TEST( VerifyNoMemoryLeaksForTaskStacksWhenTaskIsNotRunning) | |
{ | |
OSTimeDly(2); | |
DWORD ram_at_start = spaceleft(); | |
SomeConcreteTask* local_task = new SomeConcreteTask(); | |
DWORD ram_at_middle = spaceleft(); | |
local_task->SetTaskPriority(HTTP_PRIO - 1); | |
local_task->Startup(); | |
OSTimeDly(2); | |
local_task->_stayAlive = false; | |
OSTimeDly(2); | |
delete local_task; //destructor will run and since task is done it will clean up stack memory | |
OSTimeDly(2); | |
DWORD ram_at_end = spaceleft(); | |
cout << "start:" << ram_at_start << " middle:" << ram_at_middle << " end:" << ram_at_end << endl; | |
CHECK(ram_at_start == ram_at_end); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment