@@ -194,10 +194,28 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack()
194
194
mGLibMainLoopThread = g_thread_new (" gmain-matter" , GLibMainLoopThread, mGLibMainLoop );
195
195
196
196
{
197
+ // Wait for the GLib main loop to start. It is required that the context used
198
+ // by the main loop is acquired before any other GLib functions are called. Otherwise,
199
+ // the GLibMatterContextInvokeSync() might run functions on the wrong thread.
200
+
197
201
std::unique_lock<std::mutex> lock (mGLibMainLoopCallbackIndirectionMutex );
198
- CallbackIndirection startedInd ([](void *) { return G_SOURCE_REMOVE; }, nullptr );
199
- g_idle_add (G_SOURCE_FUNC (&CallbackIndirection::Callback), &startedInd);
200
- startedInd.Wait (lock);
202
+ GLibMatterContextInvokeData invokeData{};
203
+
204
+ auto * idleSource = g_idle_source_new ();
205
+ g_source_set_callback (
206
+ idleSource,
207
+ [](void * userData_) {
208
+ auto * data = reinterpret_cast <GLibMatterContextInvokeData *>(userData_);
209
+ std::unique_lock<std::mutex> lock_ (PlatformMgrImpl ().mGLibMainLoopCallbackIndirectionMutex );
210
+ data->mDone = true ;
211
+ data->mDoneCond .notify_one ();
212
+ return G_SOURCE_REMOVE;
213
+ },
214
+ &invokeData, nullptr );
215
+ g_source_attach (idleSource, g_main_loop_get_context (mGLibMainLoop ));
216
+ g_source_unref (idleSource);
217
+
218
+ invokeData.mDoneCond .wait (lock, [&invokeData]() { return invokeData.mDone ; });
201
219
}
202
220
203
221
#endif
@@ -248,68 +266,52 @@ void PlatformManagerImpl::_Shutdown()
248
266
249
267
#if CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP
250
268
g_main_loop_quit (mGLibMainLoop );
251
- g_main_loop_unref (mGLibMainLoop );
252
269
g_thread_join (mGLibMainLoopThread );
270
+ g_main_loop_unref (mGLibMainLoop );
253
271
#endif
254
272
}
255
273
256
274
#if CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP
257
-
258
- void PlatformManagerImpl::CallbackIndirection::Wait (std::unique_lock<std::mutex> & lock)
259
- {
260
- mDoneCond .wait (lock, [this ]() { return mDone ; });
261
- }
262
-
263
- gboolean PlatformManagerImpl::CallbackIndirection::Callback (CallbackIndirection * self)
275
+ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync (CHIP_ERROR (*func)(void *), void * userData)
264
276
{
265
- // We can not access "self" before acquiring the lock, because TSAN will complain that
266
- // there is a race condition between the thread that created the object and the thread
267
- // that is executing the callback .
268
- std::unique_lock<std::mutex> lock (PlatformMgrImpl (). mGLibMainLoopCallbackIndirectionMutex );
277
+ // Because of TSAN false positives, we need to use a mutex to synchronize access to all members of
278
+ // the GLibMatterContextInvokeData object (including constructor and destructor). This is a temporary
279
+ // workaround until TSAN-enabled GLib will be used in our CI .
280
+ std::unique_lock<std::mutex> lock (mGLibMainLoopCallbackIndirectionMutex );
269
281
270
- auto callback = self->mCallback ;
271
- auto userData = self->mUserData ;
282
+ GLibMatterContextInvokeData invokeData{ func, userData };
272
283
273
284
lock.unlock ();
274
- auto result = callback (userData);
275
- lock.lock ();
276
285
277
- self->mDone = true ;
278
- self->mDoneCond .notify_all ();
286
+ g_main_context_invoke_full (
287
+ g_main_loop_get_context (mGLibMainLoop ), G_PRIORITY_HIGH_IDLE,
288
+ [](void * userData_) {
289
+ auto * data = reinterpret_cast <GLibMatterContextInvokeData *>(userData_);
279
290
280
- return result;
281
- }
291
+ // XXX: Temporary workaround for TSAN false positives.
292
+ std::unique_lock<std::mutex> lock_ ( PlatformMgrImpl (). mGLibMainLoopCallbackIndirectionMutex );
282
293
283
- #if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
284
- CHIP_ERROR PlatformManagerImpl::RunOnGLibMainLoopThread (GSourceFunc callback, void * userData, bool wait)
285
- {
294
+ auto mFunc = data->mFunc ;
295
+ auto mUserData = data->mFuncUserData ;
286
296
287
- GMainContext * context = g_main_loop_get_context ( mGLibMainLoop );
288
- VerifyOrReturnError (context != nullptr , CHIP_ERROR_INTERNAL,
289
- ChipLogDetail (DeviceLayer, " Failed to get GLib main loop context " ) );
297
+ lock_. unlock ( );
298
+ auto result = mFunc ( mUserData );
299
+ lock_. lock ( );
290
300
291
- // If we've been called from the GLib main loop thread itself, there is no reason to wait
292
- // for the callback, as it will be executed immediately by the g_main_context_invoke() call
293
- // below. Using a callback indirection in this case would cause a deadlock.
294
- if (g_main_context_is_owner (context))
295
- {
296
- wait = false ;
297
- }
301
+ data->mDone = true ;
302
+ data->mFuncResult = result;
303
+ data->mDoneCond .notify_one ();
298
304
299
- if (wait )
300
- {
301
- std::unique_lock<std::mutex> lock (mGLibMainLoopCallbackIndirectionMutex );
302
- CallbackIndirection indirection (callback, userData);
303
- g_main_context_invoke (context, G_SOURCE_FUNC (&CallbackIndirection::Callback), &indirection);
304
- indirection.Wait (lock);
305
- return CHIP_NO_ERROR;
306
- }
305
+ return G_SOURCE_REMOVE;
306
+ },
307
+ &invokeData, nullptr );
307
308
308
- g_main_context_invoke (context, callback, userData);
309
- return CHIP_NO_ERROR;
310
- }
311
- #endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
309
+ lock.lock ();
312
310
311
+ invokeData.mDoneCond .wait (lock, [&invokeData]() { return invokeData.mDone ; });
312
+
313
+ return invokeData.mFuncResult ;
314
+ }
313
315
#endif // CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP
314
316
315
317
} // namespace DeviceLayer
0 commit comments