Skip to content

Commit acba14f

Browse files
authored
fix(hub): avoid deadlocks when emitting events (#633)
In a very specific combination of events, any event emission can deadlock if it tries to add stuff to the Hub, like breadcrumbs. This can be easily reproduced with the following setup: - `sentry-log` (or `sentry-tracing` if `tracing-log` is set up) - event filtering configured to generate breadcrumbs for debug logs - any cause for events to not be sent to the server Should the event fail to be sent for too long, the channel to the sender thread fills up and events start to be dropped. This will generate a debug log line, logging that an event has been dropped and why. With `sentry-log` (or `tracing-log` + `sentry-tracing`), this would generate a breadcrumb. However, the whole call to `Transport::send_envelope` is done in the context of `HubImpl::with`, that holds a read lock on the `HubImpl`'s stack. When generating the breadcrumb for the debug line, we end up calling `Hub::add_breadcrumb`, which calls `HubImpl::with_mut` to get a mutable reference to the top of the stack. However, since we already have a read lock on the stack, we deadlock on the write lock. The fix is to move the call to `Transport::send_envelope` outside of the lock zone, and we use `HubImpl::with` only to clone the top `StackLayer`. Since this structure is only `Arc`s, the only performance hit is two `Arc` clones and not the whole stack cloning.
1 parent 02b708a commit acba14f

File tree

1 file changed

+5
-10
lines changed

1 file changed

+5
-10
lines changed

sentry-core/src/hub.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,11 @@ impl Hub {
9999
/// for more documentation.
100100
pub fn capture_event(&self, event: Event<'static>) -> Uuid {
101101
with_client_impl! {{
102-
self.inner.with(|stack| {
103-
let top = stack.top();
104-
if let Some(ref client) = top.client {
105-
let event_id = client.capture_event(event, Some(&top.scope));
106-
*self.last_event_id.write().unwrap() = Some(event_id);
107-
event_id
108-
} else {
109-
Default::default()
110-
}
111-
})
102+
let top = self.inner.with(|stack| stack.top().clone());
103+
let Some(ref client) = top.client else { return Default::default() };
104+
let event_id = client.capture_event(event, Some(&top.scope));
105+
*self.last_event_id.write().unwrap() = Some(event_id);
106+
event_id
112107
}}
113108
}
114109

0 commit comments

Comments
 (0)