diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 3eeb2ab73..5ea5b9b8f 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -733,6 +733,11 @@ g_rec_mutex_lock g_rec_mutex_trylock g_rec_mutex_unlock + +GRecMutexLocker +g_rec_mutex_locker_new +g_rec_mutex_locker_free + GRWLock g_rw_lock_init diff --git a/glib/glib-autocleanups.h b/glib/glib-autocleanups.h index ce1690b78..d89c8d2fe 100644 --- a/glib/glib-autocleanups.h +++ b/glib/glib-autocleanups.h @@ -75,6 +75,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMutexLocker, g_mutex_locker_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRecMutexLocker, g_rec_mutex_locker_free) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimer, g_timer_destroy) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimeZone, g_time_zone_unref) diff --git a/glib/gthread.h b/glib/gthread.h index 3d765c2c2..80b0ef99b 100644 --- a/glib/gthread.h +++ b/glib/gthread.h @@ -336,6 +336,75 @@ g_mutex_locker_free (GMutexLocker *locker) g_mutex_unlock ((GMutex *) locker); } +/** + * GRecMutexLocker: + * + * Opaque type. See g_rec_mutex_locker_new() for details. + * Since: 2.60 + */ +typedef void GRecMutexLocker; + +/** + * g_rec_mutex_locker_new: + * @rec_mutex: a recursive mutex to lock + * + * Lock @rec_mutex and return a new #GRecMutexLocker. Unlock with + * g_rec_mutex_locker_free(). Using g_rec_mutex_unlock() on @rec_mutex + * while a #GRecMutexLocker exists can lead to undefined behaviour. + * + * This is intended to be used with g_autoptr(). Note that g_autoptr() + * is only available when using GCC or clang, so the following example + * will only work with those compilers: + * |[ + * typedef struct + * { + * ... + * GRecMutex rec_mutex; + * ... + * } MyObject; + * + * static void + * my_object_do_stuff (MyObject *self) + * { + * g_autoptr(GRecMutexLocker) locker = g_rec_mutex_locker_new (&self->rec_mutex); + * + * // Code with rec_mutex locked here + * + * if (cond) + * // No need to unlock + * return; + * + * // Optionally early unlock + * g_clear_pointer (&locker, g_rec_mutex_locker_free); + * + * // Code with rec_mutex unlocked here + * } + * ]| + * + * Returns: a #GRecMutexLocker + * Since: 2.60 + */ +static inline GRecMutexLocker * +g_rec_mutex_locker_new (GRecMutex *rec_mutex) +{ + g_rec_mutex_lock (rec_mutex); + return (GRecMutexLocker *) rec_mutex; +} + +/** + * g_rec_mutex_locker_free: + * @locker: a GRecMutexLocker + * + * Unlock @locker's recursive mutex. See g_rec_mutex_locker_new() for details. + * + * Since: 2.60 + */ +static inline void +g_rec_mutex_locker_free (GRecMutexLocker *locker) +{ + g_rec_mutex_unlock ((GRecMutex *) locker); +} + G_END_DECLS #endif /* __G_THREAD_H__ */ diff --git a/glib/tests/autoptr.c b/glib/tests/autoptr.c index 5b3bce71c..92d4bbaca 100644 --- a/glib/tests/autoptr.c +++ b/glib/tests/autoptr.c @@ -329,10 +329,30 @@ test_g_mutex (void) g_mutex_init (&val); } +/* Thread function to check that a mutex given in @data is locked */ +static gpointer +mutex_locked_thread (gpointer data) +{ + GMutex *mutex = (GMutex *) data; + g_assert_false (g_mutex_trylock (mutex)); + return NULL; +} + +/* Thread function to check that a mutex given in @data is unlocked */ +static gpointer +mutex_unlocked_thread (gpointer data) +{ + GMutex *mutex = (GMutex *) data; + g_assert_true (g_mutex_trylock (mutex)); + g_mutex_unlock (mutex); + return NULL; +} + static void test_g_mutex_locker (void) { GMutex mutex; + GThread *thread; g_mutex_init (&mutex); @@ -340,8 +360,58 @@ test_g_mutex_locker (void) { g_autoptr(GMutexLocker) val = g_mutex_locker_new (&mutex); - g_assert (val != NULL); + g_assert_nonnull (val); + + /* Verify that the mutex is actually locked */ + thread = g_thread_new ("mutex locked", mutex_locked_thread, &mutex); + g_thread_join (thread); } + + /* Verify that the mutex is unlocked again */ + thread = g_thread_new ("mutex unlocked", mutex_unlocked_thread, &mutex); + g_thread_join (thread); +} + +/* Thread function to check that a recursive mutex given in @data is locked */ +static gpointer +rec_mutex_locked_thread (gpointer data) +{ + GRecMutex *rec_mutex = (GRecMutex *) data; + g_assert_false (g_rec_mutex_trylock (rec_mutex)); + return NULL; +} + +/* Thread function to check that a recursive mutex given in @data is unlocked */ +static gpointer +rec_mutex_unlocked_thread (gpointer data) +{ + GRecMutex *rec_mutex = (GRecMutex *) data; + g_assert_true (g_rec_mutex_trylock (rec_mutex)); + return NULL; +} + +static void +test_g_rec_mutex_locker (void) +{ + GRecMutex rec_mutex; + GThread *thread; + + g_rec_mutex_init (&rec_mutex); + + if (TRUE) + { + g_autoptr(GRecMutexLocker) val = g_rec_mutex_locker_new (&rec_mutex); + + g_assert_nonnull (val); + + /* Verify that the mutex is actually locked */ + thread = g_thread_new ("rec mutex locked", rec_mutex_locked_thread, &rec_mutex); + g_thread_join (thread); + } + + /* Verify that the mutex is unlocked again */ + thread = g_thread_new ("rec mutex unlocked", rec_mutex_unlocked_thread, &rec_mutex); + g_thread_join (thread); } static void @@ -536,6 +606,7 @@ main (int argc, gchar *argv[]) g_test_add_func ("/autoptr/g_thread", test_g_thread); g_test_add_func ("/autoptr/g_mutex", test_g_mutex); g_test_add_func ("/autoptr/g_mutex_locker", test_g_mutex_locker); + g_test_add_func ("/autoptr/g_rec_mutex_locker", test_g_rec_mutex_locker); g_test_add_func ("/autoptr/g_cond", test_g_cond); g_test_add_func ("/autoptr/g_timer", test_g_timer); g_test_add_func ("/autoptr/g_time_zone", test_g_time_zone);