Commit c711cb7
RsEventsService: replace global dispatch barrier with a per-handler one (follow-up to #330)
#330 fixed the shutdown use-after-free (a widget's event callback firing against a
dangling `this` -> SIGSEGV in qobject_cast<QThread*> inside
RsQThreadUtils::postToObject) by holding a single recursive mutex (mDispatchMtx)
across the whole handleEvent() dispatch, so unregisterEventsHandler() could fence
on it. That barrier is correct for the UAF but couples unrelated handlers and can
deadlock.
Two event handlers are deliberately synchronous and blocking: the passphrase
request (rsserver/rsloginhandler.cc, "Call the RsEvent blocking API") and the
plugin-confirmation dialog (plugins/pluginmanager.cc, "needs to be synchroneous").
They are delivered via sendEvent() -- so handleEvent() runs on the caller's thread
-- and their GUI handler blocks that caller with Qt::BlockingQueuedConnection
(gui/RsGUIEventManager.cpp) until the GUI thread answers a modal. With one global
dispatch lock:
1. a background thread requests the passphrase -> holds mDispatchMtx -> blocks
waiting on the GUI thread (BlockingQueuedConnection);
2. the GUI thread destroys any unrelated widget -> its destructor calls
unregisterEventsHandler() -> blocks acquiring mDispatchMtx;
3. background waits for GUI, GUI waits for the lock background holds -> hard hang.
Replace the global barrier with a per-handler one. At snapshot time (still under
mHandlerMapMtx) handleEvent() records each handler id it is about to run, together
with the dispatching thread, in mHandlersInFlight, and clears each entry as its
callback returns. unregisterEventsHandler() erases the handler from the map (no
future dispatch can pick it up) and then waits on a condition variable until that
specific handler is no longer running on any OTHER thread. Recording under the
same lock as the snapshot makes it race-free: unregister either erases before the
snapshot (never run) or after (in-flight mark already visible, so it waits).
Callbacks now run with no events-service lock held again (as before #330), so a
slow or blocking handler only ever delays unregister of that same handler, never
of an unrelated widget being torn down on another thread -> the deadlock above
cannot occur. The dispatching thread is never waited on, so a handler that
unregisters itself (or re-enters via a synchronous sendEvent) does not deadlock on
itself. The per-callback in-flight cleanup is exception-safe: if a callback throws,
the marks it and the not-yet-run handlers still hold are cleared before the
exception propagates, so a later unregisterEventsHandler() cannot block forever.
Touches only src/services/rseventsservice.{cc,h}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent dc75a6c commit c711cb7
2 files changed
Lines changed: 120 additions & 33 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
194 | 210 | | |
195 | 211 | | |
196 | 212 | | |
| |||
238 | 254 | | |
239 | 255 | | |
240 | 256 | | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
| 257 | + | |
249 | 258 | | |
250 | | - | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
251 | 266 | | |
252 | 267 | | |
253 | 268 | | |
| |||
256 | 271 | | |
257 | 272 | | |
258 | 273 | | |
259 | | - | |
| 274 | + | |
260 | 275 | | |
261 | 276 | | |
262 | 277 | | |
263 | 278 | | |
264 | | - | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
265 | 286 | | |
266 | 287 | | |
267 | | - | |
268 | | - | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
269 | 326 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
29 | 33 | | |
30 | 34 | | |
31 | 35 | | |
| |||
71 | 75 | | |
72 | 76 | | |
73 | 77 | | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
86 | 116 | | |
87 | 117 | | |
88 | 118 | | |
| |||
0 commit comments