Orocos ExecutionEngine 对函数Operation调用的实现

Orocos 中每一个 ExecutionEngine 可以属于一个线程,其拥有一个成员变量,指向 Activity 类(表示一个线程)。 该 ExecutionEngine 负责函数的调用,数据的传输等等(如下图所示)
Orocos ExecutionEngine 对函数Operation调用的实现_第1张图片

其中每一个 ExecutionEngine 中都有一个无锁的队列 MWSRQueue 类 see link

internal::MWSRQueue::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 函数中就会调用该函数。

你可能感兴趣的:(c/c++,orocos,设计模式,Robotics,机器人)