Skip to content

Commit 2de2458

Browse files
authored
Fix wait_info data race for deletion and fix atomic_wait logic (#2016)
Fix a data race for test main_proc_exit_wait.c from #1963. And fix atomic_wait logic that was wrong before: - a thread 1 started executing wasm instruction wasm_atomic_wait but hasn't reached waiting on condition variable - a main thread calls proc_exit and notifies all the threads that reached waiting on condition variable Which leads to thread 1 hang on waiting on condition variable after that Now it's atomically checked whether proc_exit was already called.
1 parent 578fbc5 commit 2de2458

3 files changed

Lines changed: 79 additions & 52 deletions

File tree

core/iwasm/common/wasm_shared_memory.c

Lines changed: 73 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
#include "bh_log.h"
77
#include "wasm_shared_memory.h"
8+
#if WASM_ENABLE_THREAD_MGR != 0
9+
#include "../libraries/thread-mgr/thread_manager.h"
10+
#endif
811

912
static bh_list shared_memory_list_head;
1013
static bh_list *const shared_memory_list = &shared_memory_list_head;
@@ -21,6 +24,8 @@ typedef struct AtomicWaitInfo {
2124
korp_mutex wait_list_lock;
2225
bh_list wait_list_head;
2326
bh_list *wait_list;
27+
/* WARNING: insert to the list allowed only in acquire_wait_info
28+
otherwise there will be data race as described in PR #2016 */
2429
} AtomicWaitInfo;
2530

2631
typedef struct AtomicWaitNode {
@@ -298,7 +303,7 @@ notify_wait_list(bh_list *wait_list, uint32 count)
298303
}
299304

300305
static AtomicWaitInfo *
301-
acquire_wait_info(void *address, bool create)
306+
acquire_wait_info(void *address, AtomicWaitNode *wait_node)
302307
{
303308
AtomicWaitInfo *wait_info = NULL;
304309
bh_list_status ret;
@@ -308,7 +313,7 @@ acquire_wait_info(void *address, bool create)
308313
if (address)
309314
wait_info = (AtomicWaitInfo *)bh_hash_map_find(wait_map, address);
310315

311-
if (!create) {
316+
if (!wait_node) {
312317
os_mutex_unlock(&wait_map_lock);
313318
return wait_info;
314319
}
@@ -336,6 +341,12 @@ acquire_wait_info(void *address, bool create)
336341
}
337342
}
338343

344+
os_mutex_lock(&wait_info->wait_list_lock);
345+
ret = bh_list_insert(wait_info->wait_list, wait_node);
346+
os_mutex_unlock(&wait_info->wait_list_lock);
347+
bh_assert(ret == BH_LIST_SUCCESS);
348+
(void)ret;
349+
339350
os_mutex_unlock(&wait_map_lock);
340351

341352
bh_assert(wait_info);
@@ -376,16 +387,22 @@ destroy_wait_info(void *wait_info)
376387
}
377388
}
378389

379-
static bool
380-
map_remove_wait_info(HashMap *wait_map_, AtomicWaitInfo *wait_info,
381-
void *address)
390+
static void
391+
map_try_release_wait_info(HashMap *wait_map_, AtomicWaitInfo *wait_info,
392+
void *address)
382393
{
394+
os_mutex_lock(&wait_map_lock);
395+
os_mutex_lock(&wait_info->wait_list_lock);
383396
if (wait_info->wait_list->len > 0) {
384-
return false;
397+
os_mutex_unlock(&wait_info->wait_list_lock);
398+
os_mutex_unlock(&wait_map_lock);
399+
return;
385400
}
401+
os_mutex_unlock(&wait_info->wait_list_lock);
386402

387403
bh_hash_map_remove(wait_map_, address, NULL, NULL);
388-
return true;
404+
os_mutex_unlock(&wait_map_lock);
405+
destroy_wait_info(wait_info);
389406
}
390407

391408
uint32
@@ -396,7 +413,8 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
396413
AtomicWaitInfo *wait_info;
397414
AtomicWaitNode *wait_node;
398415
WASMSharedMemNode *node;
399-
bool check_ret, is_timeout, no_wait, removed_from_map;
416+
WASMExecEnv *exec_env;
417+
bool check_ret, is_timeout, no_wait;
400418

401419
bh_assert(module->module_type == Wasm_Module_Bytecode
402420
|| module->module_type == Wasm_Module_AoT);
@@ -418,14 +436,6 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
418436
return -1;
419437
}
420438

421-
/* acquire the wait info, create new one if not exists */
422-
wait_info = acquire_wait_info(address, true);
423-
424-
if (!wait_info) {
425-
wasm_runtime_set_exception(module, "failed to acquire wait_info");
426-
return -1;
427-
}
428-
429439
node = search_module((WASMModuleCommon *)module_inst->module);
430440
os_mutex_lock(&node->shared_mem_lock);
431441
no_wait = (!wait64 && *(uint32 *)address != (uint32)expect)
@@ -435,40 +445,59 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
435445
if (no_wait) {
436446
return 1;
437447
}
438-
else {
439-
bh_list_status ret;
440448

441-
if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) {
442-
wasm_runtime_set_exception(module, "failed to create wait node");
443-
return -1;
444-
}
445-
memset(wait_node, 0, sizeof(AtomicWaitNode));
449+
if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) {
450+
wasm_runtime_set_exception(module, "failed to create wait node");
451+
return -1;
452+
}
453+
memset(wait_node, 0, sizeof(AtomicWaitNode));
446454

447-
if (0 != os_mutex_init(&wait_node->wait_lock)) {
448-
wasm_runtime_free(wait_node);
449-
return -1;
450-
}
455+
if (0 != os_mutex_init(&wait_node->wait_lock)) {
456+
wasm_runtime_free(wait_node);
457+
return -1;
458+
}
451459

452-
if (0 != os_cond_init(&wait_node->wait_cond)) {
453-
os_mutex_destroy(&wait_node->wait_lock);
454-
wasm_runtime_free(wait_node);
455-
return -1;
456-
}
460+
if (0 != os_cond_init(&wait_node->wait_cond)) {
461+
os_mutex_destroy(&wait_node->wait_lock);
462+
wasm_runtime_free(wait_node);
463+
return -1;
464+
}
457465

458-
wait_node->status = S_WAITING;
459-
os_mutex_lock(&wait_info->wait_list_lock);
460-
ret = bh_list_insert(wait_info->wait_list, wait_node);
461-
os_mutex_unlock(&wait_info->wait_list_lock);
462-
bh_assert(ret == BH_LIST_SUCCESS);
463-
(void)ret;
466+
wait_node->status = S_WAITING;
467+
468+
/* acquire the wait info, create new one if not exists */
469+
wait_info = acquire_wait_info(address, wait_node);
470+
471+
if (!wait_info) {
472+
os_mutex_destroy(&wait_node->wait_lock);
473+
wasm_runtime_free(wait_node);
474+
wasm_runtime_set_exception(module, "failed to acquire wait_info");
475+
return -1;
464476
}
465477

478+
#if WASM_ENABLE_THREAD_MGR != 0
479+
exec_env =
480+
wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst);
481+
bh_assert(exec_env);
482+
#endif
483+
484+
os_mutex_lock(&node->shared_mem_lock);
485+
no_wait = (!wait64 && *(uint32 *)address != (uint32)expect)
486+
|| (wait64 && *(uint64 *)address != expect);
487+
os_mutex_unlock(&node->shared_mem_lock);
488+
466489
/* condition wait start */
467490
os_mutex_lock(&wait_node->wait_lock);
468491

469-
os_cond_reltimedwait(&wait_node->wait_cond, &wait_node->wait_lock,
470-
timeout < 0 ? BHT_WAIT_FOREVER
471-
: (uint64)timeout / 1000);
492+
if (!no_wait
493+
#if WASM_ENABLE_THREAD_MGR != 0
494+
&& !wasm_cluster_is_thread_terminated(exec_env)
495+
#endif
496+
) {
497+
os_cond_reltimedwait(&wait_node->wait_cond, &wait_node->wait_lock,
498+
timeout < 0 ? BHT_WAIT_FOREVER
499+
: (uint64)timeout / 1000);
500+
}
472501

473502
is_timeout = wait_node->status == S_WAITING ? true : false;
474503
os_mutex_unlock(&wait_node->wait_lock);
@@ -486,14 +515,12 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
486515
wasm_runtime_free(wait_node);
487516

488517
/* Release wait info if no wait nodes attached */
489-
removed_from_map = map_remove_wait_info(wait_map, wait_info, address);
490518
os_mutex_unlock(&wait_info->wait_list_lock);
491-
if (removed_from_map)
492-
destroy_wait_info(wait_info);
519+
map_try_release_wait_info(wait_map, wait_info, address);
493520
os_mutex_unlock(&node->shared_mem_lock);
494521

495522
(void)check_ret;
496-
return is_timeout ? 2 : 0;
523+
return no_wait ? 1 : is_timeout ? 2 : 0;
497524
}
498525

499526
uint32
@@ -523,7 +550,7 @@ wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address,
523550
return -1;
524551
}
525552

526-
wait_info = acquire_wait_info(address, false);
553+
wait_info = acquire_wait_info(address, NULL);
527554

528555
/* Nobody wait on this address */
529556
if (!wait_info) {

core/iwasm/common/wasm_shared_memory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define _WASM_SHARED_MEMORY_H
88

99
#include "bh_common.h"
10+
#include "wasm_exec_env.h"
1011
#if WASM_ENABLE_INTERP != 0
1112
#include "wasm_runtime.h"
1213
#endif

core/iwasm/libraries/lib-wasi-threads/test/common.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <pthread.h>
1010
#include <stdbool.h>
1111
#include <unistd.h>
12+
#include <limits.h>
1213

1314
#include "wasi_thread_start.h"
1415

@@ -23,7 +24,6 @@ static bool termination_by_trap;
2324
static bool termination_in_main_thread;
2425
static blocking_task_type_t blocking_task_type;
2526

26-
#define TIMEOUT_SECONDS 10ll
2727
#define NUM_THREADS 3
2828
static pthread_barrier_t barrier;
2929

@@ -36,15 +36,14 @@ void
3636
run_long_task()
3737
{
3838
if (blocking_task_type == BLOCKING_TASK_BUSY_WAIT) {
39-
for (int i = 0; i < TIMEOUT_SECONDS; i++)
40-
sleep(1);
39+
for (;;) {
40+
}
4141
}
4242
else if (blocking_task_type == BLOCKING_TASK_ATOMIC_WAIT) {
43-
__builtin_wasm_memory_atomic_wait32(
44-
0, 0, TIMEOUT_SECONDS * 1000 * 1000 * 1000);
43+
__builtin_wasm_memory_atomic_wait32(0, 0, -1);
4544
}
4645
else {
47-
sleep(TIMEOUT_SECONDS);
46+
sleep(UINT_MAX);
4847
}
4948
}
5049

0 commit comments

Comments
 (0)