在 Orocos 中每一个 ExecutionEngine 可以属于一个线程,其拥有一个成员变量,指向 Activity 类(表示一个线程)。 该 ExecutionEngine 负责函数的调用,数据的传输等等(如下图所示)
其中每一个 ExecutionEngine 中都有一个无锁的队列 MWSRQueue
类 see link
internal::MWSRQueue<base::DisposableInterface*>* mqueue;
MWSRQueue 代表的是 Multi-Writer, Single-Reader queue.
而 DisposableInterface 则表示一个可以调用的对象(LocalOperationCallerImpl,详见 link, 其实就是一个 boost::function 的 wrapper), 但都是以指针形式保存的。
如果该函数不是在调用端执行的 (详见Operation&OperationCaller), 则需要将函数指针 插入到 执行该函数的线程类的队列 中,等到该线程被执行的时候再来执行该函数。
ExecutionEngine 在 Activity(线程) 中运行, Activity 在一个 while 循环中会调用 ExecutionEngine 的 step 函数, 而这个 step 函数的功能就是调用 mqueue 中 每一个指针 的 executeAndDispose 函数,如下:
void ExecutionEngine::step()
{
// execute all commands from the AtomicQueue.
// msg_lock may not be held when entering this function !
DisposableInterface* com(0);
{
while ( mqueue->dequeue(com) ) { // 遍历 dequeue 中的每一个函数指针
assert( com );
com->executeAndDispose(); // 调用 LocalOperationCallerImpl 中 BindStorage 的 exec 函数,最终实现 boost::function 的调用
}
// there's no need to hold the lock during
// emptying the queue. But we must hold the
// lock once between excuteAndDispose and the
// broadcast to avoid the race condition in
// waitForMessages().
// This allows us to recurse into step.
MutexLock locker( msg_lock ); // 互斥锁
}
if ( com )
msg_cond.broadcast(); // 条件变量 required for waitForMessages() (3rd party thread),用于通知该线程中所有的函数已经被调用。
}
调用的过程很清晰了,但是这个 mqueue 是何时创建的呢?
每次调用ExecutionEngine::process这个函数,就会向 mqueue 中插入一个指针。
bool ExecutionEngine::process( DisposableInterface* c )
{
// forward message to master ExecutionEngine if available
if (mmaster) {
return mmaster->process(c);
}
if ( c && this->getActivity() ) {
// We only reject running functions when we're in the FatalError state.
if (taskc && taskc->mTaskState == TaskCore::FatalError )
return false;
bool result = mqueue->enqueue( c ); // 向 mqueue 插入新的函数指针
this->getActivity()->trigger();
msg_cond.broadcast(); // required for waitAndProcessMessages() (EE thread)
return result;
}
return false;
}
到此,Operation 是如何选择在哪个线程中运行的也清晰了。只需要选择对应的 ExecutionEngine,然后调用其 procees 函数,这样被调用对象的指针就插入到了该 ExecutionEngine 中,在 ExecutionEngine的 step 函数中就会调用该函数。