From 53a26afe327acad76c5d837cafa9db25415bf3b9 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 23:15:52 +0300 Subject: [PATCH] cgen: fix live programs hang if a [live] function calls another (fixes #15412) --- vlib/v/gen/c/fn.v | 10 +++- vlib/v/gen/c/live.v | 51 +++++++++++++------ .../testdata/live_nested_call_nix.c.must_have | 10 ++++ vlib/v/gen/c/testdata/live_nested_call_nix.vv | 15 ++++++ 4 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 vlib/v/gen/c/testdata/live_nested_call_nix.c.must_have create mode 100644 vlib/v/gen/c/testdata/live_nested_call_nix.vv diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index ae22ff577..26fdf9335 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -501,6 +501,11 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { // Adding the mutex lock/unlock inside the body of the implementation // function is not reliable, because the implementation function can do // an early exit, which will leave the mutex locked. + live_mutex_ptr_type := if g.pref.os == .windows { + 'HANDLE*' + } else { + 'pthread_mutex_t*' + } mut fn_args_list := []string{} for ia, fa in fargs { fn_args_list << '${fargtypes[ia]} ${fa}' @@ -516,9 +521,10 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { } g.definitions.writeln('${type_name} ${name}(' + fn_args_list.join(', ') + ');') g.hotcode_definitions.writeln('${type_name} ${name}(' + fn_args_list.join(', ') + '){') - g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);') + g.hotcode_definitions.writeln(' ${live_mutex_ptr_type} live_fn_mutex_ptr = v_live_fn_mutex_ptr();') + g.hotcode_definitions.writeln(' pthread_mutex_lock(live_fn_mutex_ptr);') g.hotcode_definitions.writeln(' ${live_fncall}') - g.hotcode_definitions.writeln(' pthread_mutex_unlock(&live_fn_mutex);') + g.hotcode_definitions.writeln(' pthread_mutex_unlock(live_fn_mutex_ptr);') g.hotcode_definitions.writeln(' ${live_fnreturn}') g.hotcode_definitions.writeln('}') } diff --git a/vlib/v/gen/c/live.v b/vlib/v/gen/c/live.v index a1fb9dd2b..339a7f75a 100644 --- a/vlib/v/gen/c/live.v +++ b/vlib/v/gen/c/live.v @@ -6,13 +6,20 @@ import v.util fn (mut g Gen) generate_hotcode_reloading_declarations() { if g.pref.os == .windows { - if g.pref.is_livemain { - g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;') - } - if g.pref.is_liveshared { - g.hotcode_definitions.writeln('HANDLE live_fn_mutex;') - } + g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;') g.hotcode_definitions.writeln(' +HANDLE* v_live_fn_mutex_ptr(void) { + if (g_live_reload_info) { + v__live__LiveReloadInfo* live_info = (v__live__LiveReloadInfo*)g_live_reload_info; + if (live_info->live_fn_mutex) { + return (HANDLE*)live_info->live_fn_mutex; + } + } + if (!live_fn_mutex) { + live_fn_mutex = CreateMutexA(0, 0, 0); + } + return &live_fn_mutex; +} void pthread_mutex_lock(HANDLE *m) { WaitForSingleObject(*m, INFINITE); } @@ -21,13 +28,29 @@ void pthread_mutex_unlock(HANDLE *m) { } ') } else { - if g.pref.is_livemain { - g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;') - } - if g.pref.is_liveshared { - g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;') + g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;') + g.hotcode_definitions.writeln('pthread_once_t live_fn_mutex_once = PTHREAD_ONCE_INIT;') + g.hotcode_definitions.writeln(' +void v_init_live_mutex(void) { + pthread_mutexattr_t live_fn_mutex_attr; + pthread_mutexattr_init(&live_fn_mutex_attr); + pthread_mutexattr_settype(&live_fn_mutex_attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&live_fn_mutex, &live_fn_mutex_attr); + pthread_mutexattr_destroy(&live_fn_mutex_attr); +} + +pthread_mutex_t* v_live_fn_mutex_ptr(void) { + if (g_live_reload_info) { + v__live__LiveReloadInfo* live_info = (v__live__LiveReloadInfo*)g_live_reload_info; + if (live_info->live_fn_mutex) { + return (pthread_mutex_t*)live_info->live_fn_mutex; } } + pthread_once(&live_fn_mutex_once, v_init_live_mutex); + return &live_fn_mutex; +} +') + } } fn (mut g Gen) generate_hotcode_reloader_code() { @@ -101,14 +124,12 @@ fn (mut g Gen) generate_hotcode_reloading_main_caller() { vopts := '${ccompiler} ${so_debug_flag} -sharedlive -shared' g.writeln('\t\t// start background reloading thread') - if g.pref.os == .windows { - g.writeln('\t\tlive_fn_mutex = CreateMutexA(0, 0, 0);') - } + g.writeln('\t\tvoid* live_fn_mutex_addr = v_live_fn_mutex_ptr();') g.writeln('\t\tv__live__LiveReloadInfo* live_info = v__live__executable__new_live_reload_info(') g.writeln('\t\t\t\t\t builtin__tos2("${file}"),') g.writeln('\t\t\t\t\t builtin__tos2("${vexe}"),') g.writeln('\t\t\t\t\t builtin__tos2("${vopts}"),') - g.writeln('\t\t\t\t\t &live_fn_mutex,') + g.writeln('\t\t\t\t\t live_fn_mutex_addr,') g.writeln('\t\t\t\t\t v_bind_live_symbols') g.writeln('\t\t);') mut already_added := map[string]bool{} diff --git a/vlib/v/gen/c/testdata/live_nested_call_nix.c.must_have b/vlib/v/gen/c/testdata/live_nested_call_nix.c.must_have new file mode 100644 index 000000000..efffa1b96 --- /dev/null +++ b/vlib/v/gen/c/testdata/live_nested_call_nix.c.must_have @@ -0,0 +1,10 @@ +pthread_once_t live_fn_mutex_once = PTHREAD_ONCE_INIT; +pthread_mutexattr_settype(&live_fn_mutex_attr, PTHREAD_MUTEX_RECURSIVE); +pthread_mutex_t* v_live_fn_mutex_ptr(void) { +return (pthread_mutex_t*)live_info->live_fn_mutex; +pthread_once(&live_fn_mutex_once, v_init_live_mutex); +pthread_mutex_t* live_fn_mutex_ptr = v_live_fn_mutex_ptr(); +pthread_mutex_lock(live_fn_mutex_ptr); +pthread_mutex_unlock(live_fn_mutex_ptr); +void* live_fn_mutex_addr = v_live_fn_mutex_ptr(); +live_fn_mutex_addr, diff --git a/vlib/v/gen/c/testdata/live_nested_call_nix.vv b/vlib/v/gen/c/testdata/live_nested_call_nix.vv new file mode 100644 index 000000000..c6db21aaf --- /dev/null +++ b/vlib/v/gen/c/testdata/live_nested_call_nix.vv @@ -0,0 +1,15 @@ +// vtest vflags: -live +import time + +@[live] +fn main() { + for { + f() + time.sleep(20 * time.millisecond) + } +} + +@[live] +fn f() { + println('f()') +} -- 2.39.5