Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Implementation:Alibaba MNN ExecutorScope

From Leeroopedia


Metadata

Source Repository https://github.com/alibaba/MNN
Source File express/ExecutorScope.cpp (72 lines)
Language C++
Namespace MNN::Express
Domains Execution, Runtime
Last Updated 2026-02-10

Summary

ExecutorScope implements an RAII-style thread-local scoping mechanism for MNN Express executors. It ensures that each thread in a multi-threaded application maintains its own isolated execution context by pushing and popping Executor instances onto a thread-local scope stack. The implementation provides platform-specific thread-local storage: thread_local on non-iOS platforms and pthread_key on iOS.

Import

#include <MNN/expr/ExecutorScope.hpp>

I/O Contract

Input Output
std::shared_ptr<Executor> Thread-local scope context (push on construction, pop on destruction)
ExecutorScope::Current() (static) Returns the current thread's active Executor, or the global executor if none is scoped

Key Class: ExecutorScope

Platform-Specific Thread-Local Storage

The implementation selects between two thread-local storage strategies at compile time:

typedef std::shared_ptr<Express::Executor> ExecutorRef;
#if TARGET_OS_IPHONE
#include <pthread.h>
static pthread_key_t gKey;
static std::once_flag gInitFlag;
#else
thread_local static std::once_flag gInitFlag;
thread_local static Scope<ExecutorRef>* g_executor_scope = nullptr;
#endif

On non-iOS platforms, a thread_local pointer to a Scope<ExecutorRef> is used. On iOS, a pthread_key_t is created via pthread_key_create to store the scope per-thread, as iOS historically lacked full thread_local support.

Internal Helper: _getGlobalScope()

static Scope<ExecutorRef>* _getGlobalScope() {
    std::call_once(gInitFlag,
                   [&]() {
#if TARGET_OS_IPHONE
        pthread_key_create(&gKey, NULL);
#else
        thread_local static Scope<ExecutorRef> initValue;
        g_executor_scope = &initValue;
#endif
    });
#if TARGET_OS_IPHONE
    Scope<ExecutorRef>* scope = static_cast<Scope<ExecutorRef>*>(pthread_getspecific(gKey));
    if (!scope) {
        scope = new Scope<ExecutorRef>;
        pthread_setspecific(gKey, static_cast<void*>(scope));
    }
    return scope;
#else
    return g_executor_scope;
#endif
}

This function lazily initializes the thread-local scope object using std::call_once. On iOS, it allocates a new Scope per thread if one does not yet exist.

Constructor (L50-52)

ExecutorScope::ExecutorScope(const std::shared_ptr<Executor>& current) {
    _getGlobalScope()->EnterScope(current);
}

Pushes the given executor onto the thread-local scope stack.

Named Constructor (L54-57)

ExecutorScope::ExecutorScope(const std::string& scope_name,
                             const std::shared_ptr<Executor>& current) {
    _getGlobalScope()->EnterScope(scope_name, current);
}

Pushes the executor with an associated name for debugging or identification purposes.

Destructor (L59-61)

ExecutorScope::~ExecutorScope() {
    _getGlobalScope()->ExitScope();
}

Pops the current executor from the thread-local scope stack, restoring the previous executor context. This is the RAII guarantee: scope is always cleaned up when the ExecutorScope object goes out of scope.

Static Method: Current() (L63-69)

const std::shared_ptr<Executor> ExecutorScope::Current() {
    auto exe = _getGlobalScope()->Content();
    if (exe) {
        return exe;
    }
    return Executor::getGlobalExecutor();
}

Returns the active executor for the current thread. If no scoped executor exists, falls back to the global executor via Executor::getGlobalExecutor().

Usage Example

// Create a custom executor with specific backend configuration
auto executor = Executor::newExecutor(MNN_FORWARD_OPENCL, MNNForwardType{}, 2);
{
    // Scope the executor to this block for the current thread
    ExecutorScope scope(executor);
    // All Express operations in this block use the OpenCL executor
    auto output = model->forward(input);
}
// After the block, the previous executor is restored

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment