@@ -166,18 +166,33 @@ std::error_condition RsEventsService::registerEventsHandler(
166166std::error_condition RsEventsService::unregisterEventsHandler (
167167 RsEventsHandlerId_t hId )
168168{
169- RS_STACK_MUTEX ( mHandlerMapMtx ) ;
169+ std::error_condition retval = RsEventsErrorNum:: INVALID_HANDLER_ID ;
170170
171- for (uint32_t i=0 ; i<mHandlerMaps .size (); ++i)
172171 {
173- auto it = mHandlerMaps [i].find (hId);
174- if (it != mHandlerMaps [i].end ())
172+ RS_STACK_MUTEX (mHandlerMapMtx );
173+
174+ for (uint32_t i=0 ; i<mHandlerMaps .size (); ++i)
175175 {
176- mHandlerMaps [i].erase (it);
177- return std::error_condition ();
176+ auto it = mHandlerMaps [i].find (hId);
177+ if (it != mHandlerMaps [i].end ())
178+ {
179+ mHandlerMaps [i].erase (it);
180+ retval = std::error_condition ();
181+ break ;
182+ }
178183 }
179184 }
180- return RsEventsErrorNum::INVALID_HANDLER_ID ;
185+
186+ /* At this point no *future* dispatch can pick up this handler. But an
187+ * ongoing handleEvent() may still hold a copy of it in flight (callbacks are
188+ * run outside mHandlerMapMtx). Fence on mDispatchMtx so that, once we return,
189+ * the handler is guaranteed not to be executing either: callers that
190+ * unregister from their destructor (most GUI widgets) can then be destroyed
191+ * safely. Recursive mutex => when called from within a callback on the
192+ * dispatching thread this is a cheap no-op instead of a self-deadlock. */
193+ { std::lock_guard<std::recursive_mutex> dispatchFence (mDispatchMtx ); }
194+
195+ return retval;
181196}
182197
183198void RsEventsService::threadTick ()
@@ -223,12 +238,21 @@ void RsEventsService::handleEvent(std::shared_ptr<const RsEvent> event)
223238 return ;
224239 }
225240
241+ /* Hold mDispatchMtx across the whole dispatch so unregisterEventsHandler()
242+ * can fence on it and guarantee a handler is not running once it returns
243+ * (see mDispatchMtx doc). Recursive: a callback re-entering on this same
244+ * thread (self-unregister or synchronous sendEvent) does not deadlock.
245+ * Safe against GUI teardown because handlers only post asynchronously (Qt
246+ * QueuedConnection) and never block waiting on the thread that unregisters,
247+ * so there is no lock-order cycle. */
248+ std::lock_guard<std::recursive_mutex> dispatchLock (mDispatchMtx );
249+
226250 std::list<std::function<void (std::shared_ptr<const RsEvent>)> > callbacks;
227251 {
228252 RS_STACK_MUTEX (mHandlerMapMtx );
229- /* It is important to NOT call the callback under mutex protection to
230- * allow callbacks to send other events or unregister themselves,
231- * which would otherwise deadlock. */
253+ /* It is important to NOT call the callback under mHandlerMapMtx
254+ * protection to allow callbacks to send other events or unregister
255+ * themselves, which would otherwise deadlock. */
232256
233257 // Call all clients that registered a callback for this event type
234258 for (auto & cbit: mHandlerMaps [static_cast <uint32_t >(event->mType )])
0 commit comments