Skip to content

Commit 0fba7a2

Browse files
Adds global panic hook (#24)
* Add rust thread panic hook --------- Co-authored-by: Justin Funston <[email protected]> Co-authored-by: Justin Funston <[email protected]>
1 parent 3200475 commit 0fba7a2

File tree

4 files changed

+62
-3
lines changed

4 files changed

+62
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "RustyObjectStore"
22
uuid = "1b5eed3d-1f46-4baa-87f3-a4a892b23610"
3-
version = "0.2.0"
3+
version = "0.3.0"
44

55
[deps]
66
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ In this way, the requests are asynchronous all the way up to Julia and the netwo
9595
For a GET request, Julia provides a buffer for the native library to write into.
9696
This requires Julia to know a suitable size before-hand and requires the native library to do an extra memory copy, but the upside is that Julia controls the lifetime of the memory.
9797

98+
The library provides a way for Julia code to be notifed about a panic on a Rust thread through the `on_rust_panic` argument of `init_object_store`.
99+
The default behavior is to log the stack trace (if enabled through RUST_BACKTRACE) and exit the process.
100+
The general recommendation is to treat Rust panics as fatal because Julia tasks may hang due to not being notified.
101+
98102
#### Threading Model
99103

100104
Rust object_store uses the [tokio](https://docs.rs/tokio) async runtime.

src/RustyObjectStore.jl

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,15 @@ struct InitException <: Exception
8585
return_code::Cint
8686
end
8787

88+
function default_panic_hook()
89+
println("Rust thread panicked, exiting the process")
90+
exit(1)
91+
end
92+
8893
"""
8994
init_object_store()
9095
init_object_store(config::StaticConfig)
96+
init_object_store(config::StaticConfig; on_rust_panic::Function)
9197
9298
Initialise object store.
9399
@@ -96,15 +102,33 @@ It must be called before sending a request e.g. with `get_object!` or `put_objec
96102
The runtime is only started once and cannot be re-initialised with a different config,
97103
subsequent `init_object_store` calls have no effect.
98104
105+
An optional panic hook may be provided to react to panics on Rust's native threads.
106+
The default behavior is to log and exit the process.
107+
99108
# Throws
100109
- `InitException`: if the runtime fails to start.
101110
"""
102-
function init_object_store(config::StaticConfig=DEFAULT_CONFIG)
111+
function init_object_store(
112+
config::StaticConfig=DEFAULT_CONFIG;
113+
on_rust_panic::Function=default_panic_hook
114+
)
103115
@lock _INIT_LOCK begin
104116
if _OBJECT_STORE_STARTED[]
105117
return nothing
106118
end
107-
res = @ccall rust_lib.start(config::StaticConfig)::Cint
119+
cond = Base.AsyncCondition()
120+
errormonitor(Threads.@spawn begin
121+
while true
122+
wait(cond)
123+
try
124+
on_rust_panic()
125+
catch e
126+
@error "Custom panic hook failed" exception=(e, catch_backtrace())
127+
end
128+
end
129+
end)
130+
panic_cond_handle = cond.handle
131+
res = @ccall rust_lib.start(config::StaticConfig, panic_cond_handle::Ptr{Cvoid})::Cint
108132
if res != 0
109133
throw(InitException("Failed to initialise object store runtime.", res))
110134
end

test/rust_panic_test.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@testitem "Handle object_store_ffi panic" begin
2+
# This needs to be run on a spawned process to ensure proper initialization
3+
julia_cmd_ffi_panic = Base.julia_cmd()
4+
code = """
5+
using Test
6+
using RustyObjectStore
7+
8+
triggered = false
9+
10+
function on_panic()
11+
global triggered
12+
triggered = true
13+
end
14+
15+
init_object_store(;on_rust_panic=on_panic)
16+
17+
@test !triggered
18+
19+
@ccall RustyObjectStore.rust_lib._trigger_panic()::Cint
20+
21+
@test timedwait(() -> triggered, 0.5) == :ok
22+
23+
triggered = false
24+
25+
@ccall RustyObjectStore.rust_lib._trigger_panic()::Cint
26+
27+
@test timedwait(() -> triggered, 0.5) == :ok
28+
"""
29+
cmd = `$(julia_cmd_ffi_panic) --startup-file=no --project=. -e $code`
30+
@test success(pipeline(cmd; stdout=stdout, stderr=stderr))
31+
end

0 commit comments

Comments
 (0)