Skip to content

Commit c4279bf

Browse files
authored
Merge pull request #815 from bytecodealliance/main
Implement async termination of blocking thread (bytecodealliance#2516)
2 parents 474d42d + 444b159 commit c4279bf

File tree

21 files changed

+1029
-302
lines changed

21 files changed

+1029
-302
lines changed

build-scripts/config_common.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,13 @@ else ()
273273
add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=0)
274274
endif ()
275275
endif ()
276+
if (WAMR_DISABLE_WAKEUP_BLOCKING_OP EQUAL 1)
277+
add_definitions (-DWASM_DISABLE_WAKEUP_BLOCKING_OP=1)
278+
message (" Wakeup of blocking operations disabled")
279+
else ()
280+
add_definitions (-DWASM_DISABLE_WAKEUP_BLOCKING_OP=0)
281+
message (" Wakeup of blocking operations enabled")
282+
endif ()
276283
if (WAMR_BUILD_SIMD EQUAL 1)
277284
if (NOT WAMR_BUILD_TARGET MATCHES "RISCV64.*")
278285
add_definitions (-DWASM_ENABLE_SIMD=1)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (C) 2023 Midokura Japan KK. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
*/
5+
6+
#include "wasm_runtime_common.h"
7+
8+
#include "bh_platform.h"
9+
#include "bh_common.h"
10+
#include "bh_assert.h"
11+
12+
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
13+
14+
#define LOCK(env) WASM_SUSPEND_FLAGS_LOCK((env)->wait_lock)
15+
#define UNLOCK(env) WASM_SUSPEND_FLAGS_UNLOCK((env)->wait_lock)
16+
17+
#define ISSET(env, bit) \
18+
((WASM_SUSPEND_FLAGS_GET((env)->suspend_flags) & WASM_SUSPEND_FLAG_##bit) \
19+
!= 0)
20+
#define SET(env, bit) \
21+
WASM_SUSPEND_FLAGS_FETCH_OR((env)->suspend_flags, WASM_SUSPEND_FLAG_##bit)
22+
#define CLR(env, bit) \
23+
WASM_SUSPEND_FLAGS_FETCH_AND((env)->suspend_flags, ~WASM_SUSPEND_FLAG_##bit)
24+
25+
bool
26+
wasm_runtime_begin_blocking_op(wasm_exec_env_t env)
27+
{
28+
LOCK(env);
29+
bh_assert(!ISSET(env, BLOCKING));
30+
SET(env, BLOCKING);
31+
if (ISSET(env, TERMINATE)) {
32+
CLR(env, BLOCKING);
33+
UNLOCK(env);
34+
return false;
35+
}
36+
UNLOCK(env);
37+
os_begin_blocking_op();
38+
return true;
39+
}
40+
41+
void
42+
wasm_runtime_end_blocking_op(wasm_exec_env_t env)
43+
{
44+
int saved_errno = errno;
45+
LOCK(env);
46+
bh_assert(ISSET(env, BLOCKING));
47+
CLR(env, BLOCKING);
48+
UNLOCK(env);
49+
os_end_blocking_op();
50+
errno = saved_errno;
51+
}
52+
53+
void
54+
wasm_runtime_interrupt_blocking_op(wasm_exec_env_t env)
55+
{
56+
/*
57+
* ISSET(BLOCKING) here means that the target thread
58+
* is in somewhere between wasm_begin_blocking_op and
59+
* wasm_end_blocking_op.
60+
* keep waking it up until it reaches wasm_end_blocking_op,
61+
* which clears the BLOCKING bit.
62+
*
63+
* this dumb loop is necessary because posix doesn't provide
64+
* a way to unmask signal and block atomically.
65+
*/
66+
67+
LOCK(env);
68+
SET(env, TERMINATE);
69+
while (ISSET(env, BLOCKING)) {
70+
UNLOCK(env);
71+
os_wakeup_blocking_op(env->handle);
72+
73+
/* relax a bit */
74+
os_usleep(50 * 1000);
75+
LOCK(env);
76+
}
77+
UNLOCK(env);
78+
}
79+
80+
#else /* WASM_ENABLE_THREAD_MGR && OS_ENABLE_WAKEUP_BLOCKING_OP */
81+
82+
bool
83+
wasm_runtime_begin_blocking_op(wasm_exec_env_t env)
84+
{
85+
return true;
86+
}
87+
88+
void
89+
wasm_runtime_end_blocking_op(wasm_exec_env_t env)
90+
{}
91+
92+
#endif /* WASM_ENABLE_THREAD_MGR && OS_ENABLE_WAKEUP_BLOCKING_OP */

core/iwasm/common/wasm_runtime_common.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,21 @@ wasm_runtime_env_init()
457457
}
458458
#endif
459459

460+
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
461+
if (os_blocking_op_init() != BHT_OK) {
462+
goto fail11;
463+
}
464+
os_end_blocking_op();
465+
#endif
466+
460467
return true;
461468

469+
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
470+
fail11:
471+
#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0
472+
aot_compiler_destroy();
473+
#endif
474+
#endif
462475
#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0
463476
fail10:
464477
#if WASM_ENABLE_FAST_JIT != 0
@@ -1392,6 +1405,10 @@ wasm_runtime_init_thread_env(void)
13921405
}
13931406
#endif
13941407

1408+
#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP)
1409+
os_end_blocking_op();
1410+
#endif
1411+
13951412
return true;
13961413
}
13971414

core/iwasm/common/wasm_runtime_common.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,15 @@ WASM_RUNTIME_API_EXTERN bool
10401040
wasm_runtime_is_import_global_linked(const char *module_name,
10411041
const char *global_name);
10421042

1043+
WASM_RUNTIME_API_EXTERN bool
1044+
wasm_runtime_begin_blocking_op(WASMExecEnv *exec_env);
1045+
1046+
WASM_RUNTIME_API_EXTERN void
1047+
wasm_runtime_end_blocking_op(WASMExecEnv *exec_env);
1048+
1049+
void
1050+
wasm_runtime_interrupt_blocking_op(WASMExecEnv *exec_env);
1051+
10431052
#ifdef __cplusplus
10441053
}
10451054
#endif

core/iwasm/common/wasm_suspend_flags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ extern "C" {
2020
#define WASM_SUSPEND_FLAG_BREAKPOINT 0x4
2121
/* Return from pthread_exit */
2222
#define WASM_SUSPEND_FLAG_EXIT 0x8
23+
/* The thread might be blocking */
24+
#define WASM_SUSPEND_FLAG_BLOCKING 0x10
2325

2426
typedef union WASMSuspendFlags {
2527
bh_atomic_32_t flags;

core/iwasm/include/wasm_export.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,50 @@ wasm_runtime_set_context_spread(wasm_module_inst_t inst, void *key,
15461546
WASM_RUNTIME_API_EXTERN void *
15471547
wasm_runtime_get_context(wasm_module_inst_t inst, void *key);
15481548

1549+
/*
1550+
* wasm_runtime_begin_blocking_op/wasm_runtime_end_blocking_op
1551+
*
1552+
* These APIs are intended to be used by the implementations of
1553+
* host functions. It wraps an operation which possibly blocks for long
1554+
* to prepare for async termination.
1555+
*
1556+
* eg.
1557+
*
1558+
* if (!wasm_runtime_begin_blocking_op(exec_env)) {
1559+
* return EINTR;
1560+
* }
1561+
* ret = possibly_blocking_op();
1562+
* wasm_runtime_end_blocking_op(exec_env);
1563+
* return ret;
1564+
*
1565+
* If threading support (WASM_ENABLE_THREAD_MGR) is not enabled,
1566+
* these functions are no-op.
1567+
*
1568+
* If the underlying platform support (OS_ENABLE_WAKEUP_BLOCKING_OP) is
1569+
* not available, these functions are no-op. In that case, the runtime
1570+
* might not terminate a blocking thread in a timely manner.
1571+
*
1572+
* If the underlying platform support is available, it's used to wake up
1573+
* the thread for async termination. The expectation here is that a
1574+
* `os_wakeup_blocking_op` call makes the blocking operation
1575+
* (`possibly_blocking_op` in the above example) return in a timely manner.
1576+
*
1577+
* The actual wake up mechanism used by `os_wakeup_blocking_op` is
1578+
* platform-dependent. It might impose some platform-dependent restrictions
1579+
* on the implementation of the blocking opearation.
1580+
*
1581+
* For example, on POSIX-like platforms, a signal (by default SIGUSR1) is
1582+
* used. The signal delivery configurations (eg. signal handler, signal mask,
1583+
* etc) for the signal are set up by the runtime. You can change the signal
1584+
* to use for this purpose by calling os_set_signal_number_for_blocking_op
1585+
* before the runtime initialization.
1586+
*/
1587+
WASM_RUNTIME_API_EXTERN bool
1588+
wasm_runtime_begin_blocking_op(wasm_exec_env_t exec_env);
1589+
1590+
WASM_RUNTIME_API_EXTERN void
1591+
wasm_runtime_end_blocking_op(wasm_exec_env_t exec_env);
1592+
15491593
/* clang-format on */
15501594

15511595
#ifdef __cplusplus

0 commit comments

Comments
 (0)