From b9d057118cd414922f6b94ab55d4e0f70598b978 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Mon, 24 Feb 2025 18:28:27 +0200 Subject: [PATCH] log: fix panic on mutex destroy, when exiting a program, while a thread is still logging --- vlib/builtin/builtin.c.v | 9 ++------- vlib/log/safe_log.v | 31 ++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index 1762172048..17087eb66b 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -6,11 +6,6 @@ pub type FnExitCb = fn () fn C.atexit(f FnExitCb) int fn C.strerror(int) &char -@[noreturn] -fn vhalt() { - for {} -} - @[markused] fn v_segmentation_fault_handler(signal_number i32) { $if freestanding { @@ -99,7 +94,7 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { C.exit(1) } } - vhalt() + C.exit(1) } // panic_option_not_set is called by V, when you use option error propagation in your main function. @@ -154,7 +149,7 @@ pub fn panic(s string) { C.exit(1) } } - vhalt() + C.exit(1) } // return a C-API error message matching to `errnum` diff --git a/vlib/log/safe_log.v b/vlib/log/safe_log.v index 7b171d8540..2ae35210e1 100644 --- a/vlib/log/safe_log.v +++ b/vlib/log/safe_log.v @@ -23,15 +23,22 @@ pub fn new_thread_safe_log() &ThreadSafeLog { @[unsafe] pub fn (mut x ThreadSafeLog) free() { unsafe { + // make sure other threads are not in the blocks protected by the mutex: + mut p := x.mu + p.try_lock() + x.mu = nil + p.unlock() + p.destroy() + free(p) x.Log.free() - x.mu.destroy() - free(x.mu) - // C.printf(c'ThreadSafeLog free(x), x: %p\n', x) } } // set_level changes the log level pub fn (mut x ThreadSafeLog) set_level(level Level) { + if unsafe { x.mu == 0 } { + return + } x.mu.lock() x.Log.set_level(level) x.mu.unlock() @@ -40,6 +47,9 @@ pub fn (mut x ThreadSafeLog) set_level(level Level) { // set_always_flush called with true, will make the log flush after every single .fatal(), .error(), .warn(), .info(), .debug() call. // That can be much slower, if you plan to do lots of frequent calls, but if your program exits early or crashes, your logs will be more complete. pub fn (mut x ThreadSafeLog) set_always_flush(should_flush bool) { + if unsafe { x.mu == 0 } { + return + } x.mu.lock() x.Log.set_always_flush(should_flush) x.mu.unlock() @@ -47,6 +57,9 @@ pub fn (mut x ThreadSafeLog) set_always_flush(should_flush bool) { // debug logs a debug message pub fn (mut x ThreadSafeLog) debug(s string) { + if unsafe { x.mu == 0 } { + return + } x.mu.lock() x.Log.debug(s) x.mu.unlock() @@ -54,6 +67,9 @@ pub fn (mut x ThreadSafeLog) debug(s string) { // info logs an info messagep pub fn (mut x ThreadSafeLog) info(s string) { + if unsafe { x.mu == 0 } { + return + } x.mu.lock() x.Log.info(s) x.mu.unlock() @@ -61,6 +77,9 @@ pub fn (mut x ThreadSafeLog) info(s string) { // warn logs a warning message pub fn (mut x ThreadSafeLog) warn(s string) { + if unsafe { x.mu == 0 } { + return + } x.mu.lock() x.Log.warn(s) x.mu.unlock() @@ -68,6 +87,9 @@ pub fn (mut x ThreadSafeLog) warn(s string) { // error logs an error message pub fn (mut x ThreadSafeLog) error(s string) { + if unsafe { x.mu == 0 } { + return + } x.mu.lock() x.Log.error(s) x.mu.unlock() @@ -76,6 +98,9 @@ pub fn (mut x ThreadSafeLog) error(s string) { // fatal logs a fatal message, and panics @[noreturn] pub fn (mut x ThreadSafeLog) fatal(s string) { + if unsafe { x.mu == 0 } { + panic(s) + } x.mu.lock() defer { // TODO: Log.fatal() is marked as noreturn, but this defer is allowed.