log: fix panic on mutex destroy, when exiting a program, while a thread is still logging

This commit is contained in:
Delyan Angelov 2025-02-24 18:28:27 +02:00
parent 1cb76c92f4
commit b9d057118c
No known key found for this signature in database
GPG key ID: 66886C0F12D595ED
2 changed files with 30 additions and 10 deletions

View file

@ -6,11 +6,6 @@ pub type FnExitCb = fn ()
fn C.atexit(f FnExitCb) int fn C.atexit(f FnExitCb) int
fn C.strerror(int) &char fn C.strerror(int) &char
@[noreturn]
fn vhalt() {
for {}
}
@[markused] @[markused]
fn v_segmentation_fault_handler(signal_number i32) { fn v_segmentation_fault_handler(signal_number i32) {
$if freestanding { $if freestanding {
@ -99,7 +94,7 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
C.exit(1) 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. // 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) C.exit(1)
} }
} }
vhalt() C.exit(1)
} }
// return a C-API error message matching to `errnum` // return a C-API error message matching to `errnum`

View file

@ -23,15 +23,22 @@ pub fn new_thread_safe_log() &ThreadSafeLog {
@[unsafe] @[unsafe]
pub fn (mut x ThreadSafeLog) free() { pub fn (mut x ThreadSafeLog) free() {
unsafe { 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.Log.free()
x.mu.destroy()
free(x.mu)
// C.printf(c'ThreadSafeLog free(x), x: %p\n', x)
} }
} }
// set_level changes the log level // set_level changes the log level
pub fn (mut x ThreadSafeLog) set_level(level Level) { pub fn (mut x ThreadSafeLog) set_level(level Level) {
if unsafe { x.mu == 0 } {
return
}
x.mu.lock() x.mu.lock()
x.Log.set_level(level) x.Log.set_level(level)
x.mu.unlock() 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. // 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. // 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) { pub fn (mut x ThreadSafeLog) set_always_flush(should_flush bool) {
if unsafe { x.mu == 0 } {
return
}
x.mu.lock() x.mu.lock()
x.Log.set_always_flush(should_flush) x.Log.set_always_flush(should_flush)
x.mu.unlock() x.mu.unlock()
@ -47,6 +57,9 @@ pub fn (mut x ThreadSafeLog) set_always_flush(should_flush bool) {
// debug logs a debug message // debug logs a debug message
pub fn (mut x ThreadSafeLog) debug(s string) { pub fn (mut x ThreadSafeLog) debug(s string) {
if unsafe { x.mu == 0 } {
return
}
x.mu.lock() x.mu.lock()
x.Log.debug(s) x.Log.debug(s)
x.mu.unlock() x.mu.unlock()
@ -54,6 +67,9 @@ pub fn (mut x ThreadSafeLog) debug(s string) {
// info logs an info messagep // info logs an info messagep
pub fn (mut x ThreadSafeLog) info(s string) { pub fn (mut x ThreadSafeLog) info(s string) {
if unsafe { x.mu == 0 } {
return
}
x.mu.lock() x.mu.lock()
x.Log.info(s) x.Log.info(s)
x.mu.unlock() x.mu.unlock()
@ -61,6 +77,9 @@ pub fn (mut x ThreadSafeLog) info(s string) {
// warn logs a warning message // warn logs a warning message
pub fn (mut x ThreadSafeLog) warn(s string) { pub fn (mut x ThreadSafeLog) warn(s string) {
if unsafe { x.mu == 0 } {
return
}
x.mu.lock() x.mu.lock()
x.Log.warn(s) x.Log.warn(s)
x.mu.unlock() x.mu.unlock()
@ -68,6 +87,9 @@ pub fn (mut x ThreadSafeLog) warn(s string) {
// error logs an error message // error logs an error message
pub fn (mut x ThreadSafeLog) error(s string) { pub fn (mut x ThreadSafeLog) error(s string) {
if unsafe { x.mu == 0 } {
return
}
x.mu.lock() x.mu.lock()
x.Log.error(s) x.Log.error(s)
x.mu.unlock() x.mu.unlock()
@ -76,6 +98,9 @@ pub fn (mut x ThreadSafeLog) error(s string) {
// fatal logs a fatal message, and panics // fatal logs a fatal message, and panics
@[noreturn] @[noreturn]
pub fn (mut x ThreadSafeLog) fatal(s string) { pub fn (mut x ThreadSafeLog) fatal(s string) {
if unsafe { x.mu == 0 } {
panic(s)
}
x.mu.lock() x.mu.lock()
defer { defer {
// TODO: Log.fatal() is marked as noreturn, but this defer is allowed. // TODO: Log.fatal() is marked as noreturn, but this defer is allowed.