Skip to content

Instantly share code, notes, and snippets.

@Rolias
Created June 7, 2011 22:08
Show Gist options
  • Save Rolias/1013288 to your computer and use it in GitHub Desktop.
Save Rolias/1013288 to your computer and use it in GitHub Desktop.
TaskBase - Parent class for all tasks started in NetBurner
/*
* 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();
}
}
/*
* 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_ */
/*
* 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