From e4bd6dd5151312c654704a2492c56451429f8d31 Mon Sep 17 00:00:00 2001 From: Kalev Lember Date: Thu, 9 May 2019 16:27:47 +0200 Subject: [PATCH] Add autoptr support for GRWLock --- docs/reference/glib/glib-sections.txt | 10 ++ glib/glib-autocleanups.h | 2 + glib/gthread.h | 146 ++++++++++++++++++++++++++ glib/tests/autoptr.c | 67 ++++++++++++ 4 files changed, 225 insertions(+) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 4bb4b2dfb..736b1d856 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -743,6 +743,16 @@ GRecMutexLocker g_rec_mutex_locker_new g_rec_mutex_locker_free + +GRWLockWriterLocker +g_rw_lock_writer_locker_new +g_rw_lock_writer_locker_free + + +GRWLockReaderLocker +g_rw_lock_reader_locker_new +g_rw_lock_reader_locker_free + GRWLock g_rw_lock_init diff --git a/glib/glib-autocleanups.h b/glib/glib-autocleanups.h index d89c8d2fe..efa4a99ab 100644 --- a/glib/glib-autocleanups.h +++ b/glib/glib-autocleanups.h @@ -76,6 +76,8 @@ 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_AUTOPTR_CLEANUP_FUNC(GRWLockWriterLocker, g_rw_lock_writer_locker_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRWLockReaderLocker, g_rw_lock_reader_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 80b0ef99b..c8761c5e6 100644 --- a/glib/gthread.h +++ b/glib/gthread.h @@ -405,6 +405,152 @@ g_rec_mutex_locker_free (GRecMutexLocker *locker) g_rec_mutex_unlock ((GRecMutex *) locker); } +/** + * GRWLockWriterLocker: + * + * Opaque type. See g_rw_lock_writer_locker_new() for details. + * Since: 2.62 + */ +typedef void GRWLockWriterLocker; + +/** + * g_rw_lock_writer_locker_new: + * @rw_lock: a #GRWLock + * + * Obtain a write lock on @rw_lock and return a new #GRWLockWriterLocker. + * Unlock with g_rw_lock_writer_locker_free(). Using g_rw_lock_writer_unlock() + * on @rw_lock while a #GRWLockWriterLocker 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 + * { + * ... + * GRWLock rw_lock; + * GPtrArray *array; + * ... + * } MyObject; + * + * static gchar * + * my_object_get_data (MyObject *self, guint index) + * { + * g_autoptr(GRWLockReaderLocker) locker = g_rw_lock_reader_locker_new (&self->rw_lock); + * + * // Code with a read lock obtained on rw_lock here + * + * if (self->array == NULL) + * // No need to unlock + * return NULL; + * + * if (index < self->array->len) + * // No need to unlock + * return g_ptr_array_index (self->array, index); + * + * // Optionally early unlock + * g_clear_pointer (&locker, g_rw_lock_reader_locker_free); + * + * // Code with rw_lock unlocked here + * return NULL; + * } + * + * static void + * my_object_set_data (MyObject *self, guint index, gpointer data) + * { + * g_autoptr(GRWLockWriterLocker) locker = g_rw_lock_writer_locker_new (&self->rw_lock); + * + * // Code with a write lock obtained on rw_lock here + * + * if (self->array == NULL) + * self->array = g_ptr_array_new (); + * + * if (cond) + * // No need to unlock + * return; + * + * if (index >= self->array->len) + * g_ptr_array_set_size (self->array, index+1); + * g_ptr_array_index (self->array, index) = data; + * + * // Optionally early unlock + * g_clear_pointer (&locker, g_rw_lock_writer_locker_free); + * + * // Code with rw_lock unlocked here + * } + * ]| + * + * Returns: a #GRWLockWriterLocker + * Since: 2.62 + */ +static inline GRWLockWriterLocker * +g_rw_lock_writer_locker_new (GRWLock *rw_lock) +{ + g_rw_lock_writer_lock (rw_lock); + return (GRWLockWriterLocker *) rw_lock; +} + +/** + * g_rw_lock_writer_locker_free: + * @locker: a GRWLockWriterLocker + * + * Release a write lock on @locker's read-write lock. See + * g_rw_lock_writer_locker_new() for details. + * + * Since: 2.62 + */ +static inline void +g_rw_lock_writer_locker_free (GRWLockWriterLocker *locker) +{ + g_rw_lock_writer_unlock ((GRWLock *) locker); +} + +/** + * GRWLockReaderLocker: + * + * Opaque type. See g_rw_lock_reader_locker_new() for details. + * Since: 2.62 + */ +typedef void GRWLockReaderLocker; + +/** + * g_rw_lock_reader_locker_new: + * @rw_lock: a #GRWLock + * + * Obtain a read lock on @rw_lock and return a new #GRWLockReaderLocker. + * Unlock with g_rw_lock_reader_locker_free(). Using g_rw_lock_reader_unlock() + * on @rw_lock while a #GRWLockReaderLocker exists can lead to undefined + * behaviour. + * + * This is intended to be used with g_autoptr(). For a code sample, see + * g_rw_lock_writer_locker_new(). + * + * Returns: a #GRWLockReaderLocker + * Since: 2.62 + */ +static inline GRWLockReaderLocker * +g_rw_lock_reader_locker_new (GRWLock *rw_lock) +{ + g_rw_lock_reader_lock (rw_lock); + return (GRWLockReaderLocker *) rw_lock; +} + +/** + * g_rw_lock_reader_locker_free: + * @locker: a GRWLockReaderLocker + * + * Release a read lock on @locker's read-write lock. See + * g_rw_lock_reader_locker_new() for details. + * + * Since: 2.62 + */ +static inline void +g_rw_lock_reader_locker_free (GRWLockReaderLocker *locker) +{ + g_rw_lock_reader_unlock ((GRWLock *) locker); +} + G_END_DECLS #endif /* __G_THREAD_H__ */ diff --git a/glib/tests/autoptr.c b/glib/tests/autoptr.c index c9d1f07e5..d24ad1ed8 100644 --- a/glib/tests/autoptr.c +++ b/glib/tests/autoptr.c @@ -414,6 +414,72 @@ test_g_rec_mutex_locker (void) g_thread_join (thread); } +/* Thread function to check that an rw lock given in @data cannot be writer locked */ +static gpointer +rw_lock_cannot_take_writer_lock_thread (gpointer data) +{ + GRWLock *lock = (GRWLock *) data; + g_assert_false (g_rw_lock_writer_trylock (lock)); + return NULL; +} + +/* Thread function to check that an rw lock given in @data can be reader locked */ +static gpointer +rw_lock_can_take_reader_lock_thread (gpointer data) +{ + GRWLock *lock = (GRWLock *) data; + g_assert_true (g_rw_lock_reader_trylock (lock)); + g_rw_lock_reader_unlock (lock); + return NULL; +} + +static void +test_g_rw_lock_lockers (void) +{ + GRWLock lock; + GThread *thread; + + g_rw_lock_init (&lock); + + if (TRUE) + { + g_autoptr(GRWLockWriterLocker) val = g_rw_lock_writer_locker_new (&lock); + + g_assert_nonnull (val); + + /* Verify that we cannot take another writer lock as a writer lock is currently held */ + thread = g_thread_new ("rw lock cannot take writer lock", rw_lock_cannot_take_writer_lock_thread, &lock); + g_thread_join (thread); + + /* Verify that we cannot take a reader lock as a writer lock is currently held */ + g_assert_false (g_rw_lock_reader_trylock (&lock)); + } + + if (TRUE) + { + g_autoptr(GRWLockReaderLocker) val = g_rw_lock_reader_locker_new (&lock); + + g_assert_nonnull (val); + + /* Verify that we can take another reader lock from another thread */ + thread = g_thread_new ("rw lock can take reader lock", rw_lock_can_take_reader_lock_thread, &lock); + g_thread_join (thread); + + /* ... and also that recursive reader locking from the same thread works */ + g_assert_true (g_rw_lock_reader_trylock (&lock)); + g_rw_lock_reader_unlock (&lock); + + /* Verify that we cannot take a writer lock as a reader lock is currently held */ + thread = g_thread_new ("rw lock cannot take writer lock", rw_lock_cannot_take_writer_lock_thread, &lock); + g_thread_join (thread); + } + + /* Verify that we can take a writer lock again: this can only work if all of + * the locks taken above have been correctly released. */ + g_assert_true (g_rw_lock_writer_trylock (&lock)); + g_rw_lock_writer_unlock (&lock); +} + static void test_g_cond (void) { @@ -636,6 +702,7 @@ main (int argc, gchar *argv[]) 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_rw_lock_lockers", test_g_rw_lock_lockers); 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);