Implement async termination of blocking thread (#2516)
Send a signal whose handler is no-op to a blocking thread to wake up the blocking syscall with either EINTR equivalent or partial success. Unlike the approach taken in the `dev/interrupt_block_insn` branch (that is, signal + longjmp similarly to `OS_ENABLE_HW_BOUND_CHECK`), this PR does not use longjmp because: * longjmp from signal handler doesn't work on nuttx refer to https://github.com/apache/nuttx/issues/10326 * the singal+longjmp approach may be too difficult for average programmers who might implement host functions to deal with See also https://github.com/bytecodealliance/wasm-micro-runtime/issues/1910
This commit is contained in:
92
core/iwasm/common/wasm_blocking_op.c
Normal file
92
core/iwasm/common/wasm_blocking_op.c
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Midokura Japan KK. All rights reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#include "wasm_runtime_common.h"
|
||||
|
||||
#include "bh_platform.h"
|
||||
#include "bh_common.h"
|
||||
#include "bh_assert.h"
|
||||
|
||||
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
|
||||
|
||||
#define LOCK(env) WASM_SUSPEND_FLAGS_LOCK((env)->wait_lock)
|
||||
#define UNLOCK(env) WASM_SUSPEND_FLAGS_UNLOCK((env)->wait_lock)
|
||||
|
||||
#define ISSET(env, bit) \
|
||||
((WASM_SUSPEND_FLAGS_GET((env)->suspend_flags) & WASM_SUSPEND_FLAG_##bit) \
|
||||
!= 0)
|
||||
#define SET(env, bit) \
|
||||
WASM_SUSPEND_FLAGS_FETCH_OR((env)->suspend_flags, WASM_SUSPEND_FLAG_##bit)
|
||||
#define CLR(env, bit) \
|
||||
WASM_SUSPEND_FLAGS_FETCH_AND((env)->suspend_flags, ~WASM_SUSPEND_FLAG_##bit)
|
||||
|
||||
bool
|
||||
wasm_runtime_begin_blocking_op(wasm_exec_env_t env)
|
||||
{
|
||||
LOCK(env);
|
||||
bh_assert(!ISSET(env, BLOCKING));
|
||||
SET(env, BLOCKING);
|
||||
if (ISSET(env, TERMINATE)) {
|
||||
CLR(env, BLOCKING);
|
||||
UNLOCK(env);
|
||||
return false;
|
||||
}
|
||||
UNLOCK(env);
|
||||
os_begin_blocking_op();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
wasm_runtime_end_blocking_op(wasm_exec_env_t env)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
LOCK(env);
|
||||
bh_assert(ISSET(env, BLOCKING));
|
||||
CLR(env, BLOCKING);
|
||||
UNLOCK(env);
|
||||
os_end_blocking_op();
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void
|
||||
wasm_runtime_interrupt_blocking_op(wasm_exec_env_t env)
|
||||
{
|
||||
/*
|
||||
* ISSET(BLOCKING) here means that the target thread
|
||||
* is in somewhere between wasm_begin_blocking_op and
|
||||
* wasm_end_blocking_op.
|
||||
* keep waking it up until it reaches wasm_end_blocking_op,
|
||||
* which clears the BLOCKING bit.
|
||||
*
|
||||
* this dumb loop is necessary because posix doesn't provide
|
||||
* a way to unmask signal and block atomically.
|
||||
*/
|
||||
|
||||
LOCK(env);
|
||||
SET(env, TERMINATE);
|
||||
while (ISSET(env, BLOCKING)) {
|
||||
UNLOCK(env);
|
||||
os_wakeup_blocking_op(env->handle);
|
||||
|
||||
/* relax a bit */
|
||||
os_usleep(50 * 1000);
|
||||
LOCK(env);
|
||||
}
|
||||
UNLOCK(env);
|
||||
}
|
||||
|
||||
#else /* WASM_ENABLE_THREAD_MGR && OS_ENABLE_WAKEUP_BLOCKING_OP */
|
||||
|
||||
bool
|
||||
wasm_runtime_begin_blocking_op(wasm_exec_env_t env)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
wasm_runtime_end_blocking_op(wasm_exec_env_t env)
|
||||
{}
|
||||
|
||||
#endif /* WASM_ENABLE_THREAD_MGR && OS_ENABLE_WAKEUP_BLOCKING_OP */
|
||||
@ -457,8 +457,21 @@ wasm_runtime_env_init()
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
|
||||
if (os_blocking_op_init() != BHT_OK) {
|
||||
goto fail11;
|
||||
}
|
||||
os_end_blocking_op();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
|
||||
fail11:
|
||||
#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0
|
||||
aot_compiler_destroy();
|
||||
#endif
|
||||
#endif
|
||||
#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0
|
||||
fail10:
|
||||
#if WASM_ENABLE_FAST_JIT != 0
|
||||
@ -1392,6 +1405,10 @@ wasm_runtime_init_thread_env(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
|
||||
os_end_blocking_op();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -1040,6 +1040,15 @@ WASM_RUNTIME_API_EXTERN bool
|
||||
wasm_runtime_is_import_global_linked(const char *module_name,
|
||||
const char *global_name);
|
||||
|
||||
WASM_RUNTIME_API_EXTERN bool
|
||||
wasm_runtime_begin_blocking_op(WASMExecEnv *exec_env);
|
||||
|
||||
WASM_RUNTIME_API_EXTERN void
|
||||
wasm_runtime_end_blocking_op(WASMExecEnv *exec_env);
|
||||
|
||||
void
|
||||
wasm_runtime_interrupt_blocking_op(WASMExecEnv *exec_env);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -20,6 +20,8 @@ extern "C" {
|
||||
#define WASM_SUSPEND_FLAG_BREAKPOINT 0x4
|
||||
/* Return from pthread_exit */
|
||||
#define WASM_SUSPEND_FLAG_EXIT 0x8
|
||||
/* The thread might be blocking */
|
||||
#define WASM_SUSPEND_FLAG_BLOCKING 0x10
|
||||
|
||||
typedef union WASMSuspendFlags {
|
||||
bh_atomic_32_t flags;
|
||||
|
||||
Reference in New Issue
Block a user