Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 55 additions & 38 deletions core/iwasm/common/wasm_shared_memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

#include "bh_log.h"
#include "wasm_shared_memory.h"
#if WASM_ENABLE_THREAD_MGR != 0
#include "../libraries/thread-mgr/thread_manager.h"
#endif

static bh_list shared_memory_list_head;
static bh_list *const shared_memory_list = &shared_memory_list_head;
Expand Down Expand Up @@ -298,7 +301,7 @@ notify_wait_list(bh_list *wait_list, uint32 count)
}

static AtomicWaitInfo *
acquire_wait_info(void *address, bool create)
acquire_wait_info(void *address, bool create, AtomicWaitNode *wait_node)
Comment thread
wenyongh marked this conversation as resolved.
Outdated
{
AtomicWaitInfo *wait_info = NULL;
bh_list_status ret;
Expand All @@ -308,7 +311,7 @@ acquire_wait_info(void *address, bool create)
if (address)
wait_info = (AtomicWaitInfo *)bh_hash_map_find(wait_map, address);

if (!create) {
if (!create && !wait_info) {
os_mutex_unlock(&wait_map_lock);
return wait_info;
}
Expand All @@ -335,6 +338,13 @@ acquire_wait_info(void *address, bool create)
goto fail3;
}
}
if (wait_node) {
os_mutex_lock(&wait_info->wait_list_lock);
ret = bh_list_insert(wait_info->wait_list, wait_node);
os_mutex_unlock(&wait_info->wait_list_lock);
bh_assert(ret == BH_LIST_SUCCESS);
(void)ret;
}

os_mutex_unlock(&wait_map_lock);

Expand Down Expand Up @@ -380,17 +390,24 @@ static bool
map_remove_wait_info(HashMap *wait_map_, AtomicWaitInfo *wait_info,
void *address)
{
os_mutex_lock(&wait_map_lock);
Comment thread
g0djan marked this conversation as resolved.
os_mutex_lock(&wait_info->wait_list_lock);
if (wait_info->wait_list->len > 0) {
Comment thread
g0djan marked this conversation as resolved.
os_mutex_unlock(&wait_info->wait_list_lock);
os_mutex_unlock(&wait_map_lock);
return false;
}
os_mutex_unlock(&wait_info->wait_list_lock);
Comment thread
wenyongh marked this conversation as resolved.

bh_hash_map_remove(wait_map_, address, NULL, NULL);
os_mutex_unlock(&wait_map_lock);
return true;
}

uint32
wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
uint64 expect, int64 timeout, bool wait64)
uint64 expect, int64 timeout, bool wait64,
WASMExecEnv *exec_env)
Comment thread
wenyongh marked this conversation as resolved.
Outdated
{
WASMModuleInstance *module_inst = (WASMModuleInstance *)module;
AtomicWaitInfo *wait_info;
Expand Down Expand Up @@ -418,14 +435,6 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
return -1;
}

/* acquire the wait info, create new one if not exists */
wait_info = acquire_wait_info(address, true);

if (!wait_info) {
wasm_runtime_set_exception(module, "failed to acquire wait_info");
return -1;
}

node = search_module((WASMModuleCommon *)module_inst->module);
os_mutex_lock(&node->shared_mem_lock);
no_wait = (!wait64 && *(uint32 *)address != (uint32)expect)
Expand All @@ -435,40 +444,48 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
if (no_wait) {
return 1;
}
else {
bh_list_status ret;

if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) {
wasm_runtime_set_exception(module, "failed to create wait node");
return -1;
}
memset(wait_node, 0, sizeof(AtomicWaitNode));
if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) {
wasm_runtime_set_exception(module, "failed to create wait node");
return -1;
}
memset(wait_node, 0, sizeof(AtomicWaitNode));

if (0 != os_mutex_init(&wait_node->wait_lock)) {
wasm_runtime_free(wait_node);
return -1;
}
if (0 != os_mutex_init(&wait_node->wait_lock)) {
wasm_runtime_free(wait_node);
return -1;
}

if (0 != os_cond_init(&wait_node->wait_cond)) {
os_mutex_destroy(&wait_node->wait_lock);
wasm_runtime_free(wait_node);
return -1;
}
if (0 != os_cond_init(&wait_node->wait_cond)) {
os_mutex_destroy(&wait_node->wait_lock);
wasm_runtime_free(wait_node);
return -1;
}

wait_node->status = S_WAITING;
os_mutex_lock(&wait_info->wait_list_lock);
ret = bh_list_insert(wait_info->wait_list, wait_node);
os_mutex_unlock(&wait_info->wait_list_lock);
bh_assert(ret == BH_LIST_SUCCESS);
(void)ret;
wait_node->status = S_WAITING;

/* acquire the wait info, create new one if not exists */
wait_info = acquire_wait_info(address, true, wait_node);
Comment thread
wenyongh marked this conversation as resolved.
Outdated

if (!wait_info) {
os_mutex_destroy(&wait_node->wait_lock);
wasm_runtime_free(wait_node);
wasm_runtime_set_exception(module, "failed to acquire wait_info");
return -1;
}

/* condition wait start */
os_mutex_lock(&wait_node->wait_lock);

os_cond_reltimedwait(&wait_node->wait_cond, &wait_node->wait_lock,
timeout < 0 ? BHT_WAIT_FOREVER
: (uint64)timeout / 1000);
#if WASM_ENABLE_THREAD_MGR != 0
if (!wasm_cluster_is_thread_terminated(exec_env)) {
Comment thread
wenyongh marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerned about if and not while here: spurious wakeup
@wenyongh @eloparco any thought on what ret should be considered as appropriate one to exit loop or what codes should be considered a false wakeup?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the flag being set between the call to wasm_cluster_is_thread_terminated and os_cond_reltimedwait, right? That would cause the termination to be missed and the os_cond_reltimedwait to wait forever in case of BHT_WAIT_FOREVER

@g0djan g0djan Mar 11, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the problem you talking about I solved with your idea with condition variable(I just found a way to do it with the existing one):
thread 1

  • lock wait_node->wait_lock
  • check whether it's been already terminated
  • call wait on the condition variable that releases the lock

thread 2

  • lock wait_node->wait_lock
  • call notify on condition variable
  • unlock wait_node->wait_lock

To check I ran the tests and it never hangs any more, no tsan warnings as well(besides unrelated LOAD-STORE one)

@g0djan g0djan Mar 11, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm concerned about spurious wakeup, on some systems thread can actually wake up from wait on condition variable even there was no other thread in this program that notified it. Usually it's prevented by checking exit condition rather in a loop than just with an if.
https://en.wikipedia.org/wiki/Spurious_wakeup

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I presume we want to add a while with a check on the wait_node->status

#endif
os_cond_reltimedwait(&wait_node->wait_cond, &wait_node->wait_lock,
timeout < 0 ? BHT_WAIT_FOREVER
: (uint64)timeout / 1000);
#if WASM_ENABLE_THREAD_MGR != 0
}
#endif

is_timeout = wait_node->status == S_WAITING ? true : false;
os_mutex_unlock(&wait_node->wait_lock);
Expand All @@ -486,8 +503,8 @@ wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
wasm_runtime_free(wait_node);

/* Release wait info if no wait nodes attached */
removed_from_map = map_remove_wait_info(wait_map, wait_info, address);
os_mutex_unlock(&wait_info->wait_list_lock);
removed_from_map = map_remove_wait_info(wait_map, wait_info, address);
if (removed_from_map)
destroy_wait_info(wait_info);
Comment thread
wenyongh marked this conversation as resolved.
Outdated
os_mutex_unlock(&node->shared_mem_lock);
Expand Down Expand Up @@ -523,7 +540,7 @@ wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address,
return -1;
}

wait_info = acquire_wait_info(address, false);
wait_info = acquire_wait_info(address, false, NULL);

/* Nobody wait on this address */
if (!wait_info) {
Expand Down
7 changes: 6 additions & 1 deletion core/iwasm/common/wasm_shared_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#define _WASM_SHARED_MEMORY_H

#include "bh_common.h"
#include "wasm_exec_env.h"
#if WASM_ENABLE_THREAD_MGR != 0
#include "../libraries/thread-mgr/thread_manager.h"
#endif
#if WASM_ENABLE_INTERP != 0
#include "wasm_runtime.h"
#endif
Expand Down Expand Up @@ -60,7 +64,8 @@ shared_memory_set_memory_inst(WASMModuleCommon *module,

uint32
wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address,
uint64 expect, int64 timeout, bool wait64);
uint64 expect, int64 timeout, bool wait64,
WASMExecEnv *exec_env);

uint32
wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address,
Expand Down
4 changes: 2 additions & 2 deletions core/iwasm/interpreter/wasm_interp_classic.c
Original file line number Diff line number Diff line change
Expand Up @@ -3428,7 +3428,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,

ret = wasm_runtime_atomic_wait(
(WASMModuleInstanceCommon *)module, maddr,
(uint64)expect, timeout, false);
(uint64)expect, timeout, false, exec_env);
if (ret == (uint32)-1)
goto got_exception;

Expand All @@ -3452,7 +3452,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,

ret = wasm_runtime_atomic_wait(
(WASMModuleInstanceCommon *)module, maddr, expect,
timeout, true);
timeout, true, exec_env);
if (ret == (uint32)-1)
goto got_exception;

Expand Down
5 changes: 2 additions & 3 deletions core/iwasm/libraries/lib-wasi-threads/test/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,11 @@ void
run_long_task()
{
if (blocking_task_type == BLOCKING_TASK_BUSY_WAIT) {
for (int i = 0; i < TIMEOUT_SECONDS; i++)
for (;;)
sleep(1);
Comment thread
wenyongh marked this conversation as resolved.
Outdated
}
else if (blocking_task_type == BLOCKING_TASK_ATOMIC_WAIT) {
__builtin_wasm_memory_atomic_wait32(
0, 0, TIMEOUT_SECONDS * 1000 * 1000 * 1000);
__builtin_wasm_memory_atomic_wait32(0, 0, -1);
Comment thread
wenyongh marked this conversation as resolved.
}
else {
sleep(TIMEOUT_SECONDS);
Comment thread
wenyongh marked this conversation as resolved.
Outdated
Expand Down