Implementation:Alibaba MNN ExecutorScope
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
- Alibaba_MNN_Thread_Local_Execution_Scope -- The principle behind thread-local execution scoping via RAII