Orocos 每一个TaskContext 类表示一个完整的模块,该模块有configure,start,stop,clean,setPeriod等等方法来控制这个模块的运行。如果使用了该模块的 setActivity 方法,该模块将置于一个单独的线程中运行,根据自己的周期频率执行各个函数,读写dataport等。
setActivity 方法如下:
bool TaskContext::setActivity(ActivityInterface* new_act) // Activity 继承于 ActivityInterface
{
if (this->isRunning())
return false;
if ( new_act == 0) {
#if defined(ORO_ACT_DEFAULT_SEQUENTIAL)
new_act = new SequentialActivity();
#elseif defined(ORO_ACT_DEFAULT_ACTIVITY)
new_act = new Activity(); // 在这new了一个新的Activity,即创建了一个新的线程用于执行该模块。
#endif
}
new_act->stop();
if(our_act){
our_act->stop();
}
new_act->run( this->engine() ); // 这个模块所有需要运行的资源都是通过这个 ExecutionEngine 来执行的。
our_act = ActivityInterface::shared_ptr( new_act );
our_act->start();
return true;
}
所以说,ExecutionEngine 是通过 Activity 来执行的,而 TaskContext 中的资源又是通过 ExecutionEngine 来执行的。归根到底是通过 Activity 来执行的 :)
其中每一个 Activity 类都表示一个线程:
class Activity : public base::ActivityInterface, public os::Thread
其中 os::Thread 类根据编译时所选择的平台可以是 gnulinux, xenomai, win32等线程的实现:
#ifdef OROPKG_OS_GNULINUX
#include "gnulinux/fosi.h"
#endif
#ifdef OROPKG_OS_LXRT
#include "lxrt/fosi.h"
#endif
#ifdef OROPKG_OS_XENOMAI
#include "xenomai/fosi.h"
#endif
#ifdef OROPKG_OS_ECOS
#include "ecos/fosi.h"
#endif
#ifdef OROPKG_OS_MACOSX
#include "macosx/fosi.h"
#endif
#ifdef OROPKG_OS_WIN32
#include "win32/fosi.h"
#endif
Thread 类结构如下:
class Thread: public ThreadInterface
{
friend void* thread_function(void* t); // 传入Activity的this指针(其中又保存了一个ExecutionEngine的指针),thread_function中实现一个while(1){...}来运行各种Hook,从而驱动顶层的 TaskContext 的运转。
public:
Thread(int scheduler, int priority, double period, unsigned cpu_affinity,
const std::string & name); // 根据条件构造相应的线程
virtual ~Thread();
bool setPeriod(Seconds s); // 执行周期,也可以为非周期性的
...
private:
Thread(const Thread&); // noncopiable
...
}
最关键的就是 thread_function 这个函数了,实现了线程的周期性执行,step执行等:
void *thread_function(void* t)
{
/**
* This is one time initialisation
*/
Thread* task = static_cast<os::Thread*> (t);
Logger::In in(task->getName());
task->configure();
// signal to setup() that we're created.
rtos_sem_signal(&(task->sem));
// This lock forces setup(), which holds the lock, to continue.
{ MutexLock lock(task->breaker); }
#ifdef OROPKG_OS_THREAD_SCOPE
// order thread scope toggle bit on thread number
unsigned int bit = task->threadNumber();
#endif
SCOPE_OFF
int overruns = 0, cur_sched = task->msched_type;
NANO_TIME cur_period = task->period;
while (!task->prepareForExit)
{
TRY(
/**
* The real task starts here.
*/
while (1)
{
if (!task->active || (task->active && task->period == 0) || !task->running )
{
// consider this the 'configuration or waiting for command state'
if (task->period != 0) {
overruns = 0;
// drop out of periodic mode:
rtos_task_set_period(task->getTask(), 0);
}
rtos_sem_wait(&(task->sem)); // wait for command.
task->configure(); // check for reconfigure
if (task->prepareForExit) // check for exit
{
break; // break while(1) {}
}
// end of configuration
}
// This is the running state. Only enter if a task is running
if ( task->running )
{
if (task->period != 0) // periodic
{
MutexLock lock(task->breaker);
while(task->running && !task->prepareForExit )
{
TRY
(
SCOPE_ON
task->step(); // one cycle
SCOPE_OFF
)
CATCH_ALL
(
SCOPE_OFF
throw;
)
// Check changes in period
if ( cur_period != task->period) {
// reconfigure period before going to sleep
rtos_task_set_period(task->getTask(), task->period);
cur_period = task->period;
if (cur_period == 0)
break; // break while(task->running) if no longer periodic
}
// Check changes in scheduler
if ( cur_sched != task->msched_type) {
rtos_task_set_scheduler(task->getTask(), task->msched_type);
cur_sched = task->msched_type;
}
// rtos_task_wait_period will return immediately if
// the task is not periodic (ie period == 0)
// return non-zero to indicate overrun.
if (rtos_task_wait_period(task->getTask()) != 0)
{
++overruns;
if (overruns == task->maxOverRun)
break; // break while(task->running)
}
else if (overruns != 0)
--overruns;
} // while(task->running)
if (overruns == task->maxOverRun || task->prepareForExit)
break; // break while(1) {}
}
else // non periodic:
TRY
(
// this mutex guarantees that stop() waits
// until loop() returns.
MutexLock lock(task->breaker);
task->inloop = true;
SCOPE_ON
task->loop();
SCOPE_OFF
task->inloop = false;
) CATCH_ALL
(
SCOPE_OFF
task->inloop = false;
throw;
)
}
} // while(1)
if (overruns == task->maxOverRun)
{
task->emergencyStop();
Logger::In in(rtos_task_get_name(task->getTask()));
log(Critical) << rtos_task_get_name(task->getTask())
<< " got too many periodic overruns in step() ("
<< overruns << " times), stopped Thread !"
<< endlog();
log() << " See Thread::setMaxOverrun() for info."
<< endlog();
}
)CATCH(std::exception const& e,
SCOPE_OFF
task->emergencyStop();
Logger::In in(rtos_task_get_name(task->getTask()));
log(Critical) << rtos_task_get_name(task->getTask())
<< " caught a C++ exception, stopped thread !"
<< endlog();
log(Critical) << "exception was: "
<< e.what() << endlog();
) CATCH_ALL
(
SCOPE_OFF
task->emergencyStop();
Logger::In in(rtos_task_get_name(task->getTask()));
log(Critical) << rtos_task_get_name(task->getTask())
<< " caught an unknown C++ exception, stopped thread !"
<< endlog();
)
} // while (!prepareForExit)
return 0;
}
void Thread::emergencyStop()
{
// set state to not running
this->running = false;
this->inloop = false;
this->active = false;
// execute finalize in current mode, even if hard.
this->finalize();
}