Skip to content

Commit 0c85cb1

Browse files
authored
feat(wasi-threads): Improve thread id allocator to reuse identifiers (#1809)
This PR allows reusing thread ids once they are released. That is done by using a stack data structure to keep track of the used ids. When a thread is created, it takes an available identifier from the stack. When the thread exits, it returns the id to the stack of available identifiers.
1 parent 0f637cf commit 0c85cb1

1 file changed

Lines changed: 78 additions & 8 deletions

File tree

core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ static const char *THREAD_START_FUNCTION = "wasi_thread_start";
1818

1919
static korp_mutex thread_id_lock;
2020

21+
// Stack data structure to track available thread identifiers
22+
#define AVAIL_TIDS_INIT_SIZE CLUSTER_MAX_THREAD_NUM
23+
typedef struct {
24+
int32 *ids;
25+
uint32 pos, size;
26+
} AvailableThreadIds;
27+
static AvailableThreadIds avail_tids;
28+
2129
typedef struct {
2230
/* app's entry function */
2331
wasm_function_inst_t start_func;
@@ -30,16 +38,56 @@ typedef struct {
3038
static int32
3139
allocate_thread_id()
3240
{
33-
static int32 thread_id = 0;
34-
35-
int32 id;
41+
int32 id = -1;
3642

3743
os_mutex_lock(&thread_id_lock);
38-
id = thread_id++;
44+
if (avail_tids.pos == 0) { // Resize stack and push new thread ids
45+
uint32 old_size = avail_tids.size;
46+
uint32 new_size = avail_tids.size * 2;
47+
if (new_size / 2 != avail_tids.size) {
48+
LOG_ERROR("Overflow detected during new size calculation");
49+
goto return_id;
50+
}
51+
52+
size_t realloc_size = new_size * sizeof(int32);
53+
if (realloc_size / sizeof(int32) != new_size) {
54+
LOG_ERROR("Overflow detected during realloc");
55+
goto return_id;
56+
}
57+
int32 *tmp =
58+
(int32 *)wasm_runtime_realloc(avail_tids.ids, realloc_size);
59+
if (tmp == NULL) {
60+
LOG_ERROR("Thread ID allocator realloc failed");
61+
goto return_id;
62+
}
63+
64+
avail_tids.size = new_size;
65+
avail_tids.pos = old_size;
66+
avail_tids.ids = tmp;
67+
for (uint32 i = 0; i < old_size; i++)
68+
avail_tids.ids[i] = new_size - i;
69+
}
70+
71+
// Pop available thread identifier from `avail_tids` stack
72+
id = avail_tids.ids[--avail_tids.pos];
73+
74+
return_id:
3975
os_mutex_unlock(&thread_id_lock);
4076
return id;
4177
}
4278

79+
void
80+
deallocate_thread_id(int32 thread_id)
81+
{
82+
os_mutex_lock(&thread_id_lock);
83+
84+
// Release thread identifier by pushing it into `avail_tids` stack
85+
bh_assert(avail_tids.pos < avail_tids.size);
86+
avail_tids.ids[avail_tids.pos++] = thread_id;
87+
88+
os_mutex_unlock(&thread_id_lock);
89+
}
90+
4391
static void *
4492
thread_start(void *arg)
4593
{
@@ -57,6 +105,8 @@ thread_start(void *arg)
57105
wasm_cluster_spread_exception(exec_env);
58106
}
59107

108+
// Routine exit
109+
deallocate_thread_id(thread_arg->thread_id);
60110
wasm_runtime_free(thread_arg);
61111
exec_env->thread_arg = NULL;
62112

@@ -101,23 +151,26 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg)
101151
if (!start_func) {
102152
LOG_ERROR("Failed to find thread start function %s",
103153
THREAD_START_FUNCTION);
104-
goto thread_spawn_fail;
154+
goto thread_preparation_fail;
105155
}
106156

107157
if (!(thread_start_arg = wasm_runtime_malloc(sizeof(ThreadStartArg)))) {
108158
LOG_ERROR("Runtime args allocation failed");
109-
goto thread_spawn_fail;
159+
goto thread_preparation_fail;
110160
}
111161

112162
thread_start_arg->thread_id = thread_id = allocate_thread_id();
163+
if (thread_id < 0) {
164+
LOG_ERROR("Failed to get thread identifier");
165+
goto thread_preparation_fail;
166+
}
113167
thread_start_arg->arg = start_arg;
114168
thread_start_arg->start_func = start_func;
115169

116170
os_mutex_lock(&exec_env->wait_lock);
117171
ret = wasm_cluster_create_thread(exec_env, new_module_inst, thread_start,
118172
thread_start_arg);
119173
if (ret != 0) {
120-
os_mutex_unlock(&exec_env->wait_lock);
121174
LOG_ERROR("Failed to spawn a new thread");
122175
goto thread_spawn_fail;
123176
}
@@ -126,9 +179,12 @@ thread_spawn_wrapper(wasm_exec_env_t exec_env, uint32 start_arg)
126179
return thread_id;
127180

128181
thread_spawn_fail:
182+
os_mutex_unlock(&exec_env->wait_lock);
183+
deallocate_thread_id(thread_id);
184+
185+
thread_preparation_fail:
129186
if (new_module_inst)
130187
wasm_runtime_deinstantiate_internal(new_module_inst, true);
131-
132188
if (thread_start_arg)
133189
wasm_runtime_free(thread_start_arg);
134190

@@ -156,11 +212,25 @@ lib_wasi_threads_init(void)
156212
if (0 != os_mutex_init(&thread_id_lock))
157213
return false;
158214

215+
// Initialize stack to store thread identifiers
216+
avail_tids.size = AVAIL_TIDS_INIT_SIZE;
217+
avail_tids.pos = avail_tids.size;
218+
avail_tids.ids =
219+
(int32 *)wasm_runtime_malloc(avail_tids.size * sizeof(int32));
220+
if (avail_tids.ids == NULL) {
221+
os_mutex_destroy(&thread_id_lock);
222+
return false;
223+
}
224+
for (uint32 i = 0; i < avail_tids.size; i++)
225+
avail_tids.ids[i] = avail_tids.size - i;
226+
159227
return true;
160228
}
161229

162230
void
163231
lib_wasi_threads_destroy(void)
164232
{
233+
wasm_runtime_free(avail_tids.ids);
234+
avail_tids.ids = NULL;
165235
os_mutex_destroy(&thread_id_lock);
166236
}

0 commit comments

Comments
 (0)