[glib/wip/kalev/GRWLock-autoptr] Add autoptr support for GRWLock



commit 981df680b981b90a111a310430bd0435ffc9e0a3
Author: Kalev Lember <klember redhat com>
Date:   Thu May 9 16:27:47 2019 +0200

    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                  |  92 +++++++++++++++++++++
 4 files changed, 250 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
 
+<SUBSECTION>
+GRWLockWriterLocker
+g_rw_lock_writer_locker_new
+g_rw_lock_writer_locker_free
+
+<SUBSECTION>
+GRWLockReaderLocker
+g_rw_lock_reader_locker_new
+g_rw_lock_reader_locker_free
+
 <SUBSECTION>
 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..e4b6c3fec 100644
--- a/glib/tests/autoptr.c
+++ b/glib/tests/autoptr.c
@@ -414,6 +414,97 @@ test_g_rec_mutex_locker (void)
     g_thread_join (thread);
 }
 
+/* Thread function to check that an rw lock given in @data is writer locked */
+static gpointer
+rw_lock_writer_locked_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 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 cannot be reader locked */
+static gpointer
+rw_lock_cannot_take_reader_lock_thread (gpointer data)
+{
+  GRWLock *lock = (GRWLock *) data;
+  g_assert_false (g_rw_lock_reader_trylock (lock));
+  return NULL;
+}
+
+/* Thread function to check that an rw lock given in @data can be writer locked */
+static gpointer
+rw_lock_try_writer_lock_thread (gpointer data)
+{
+  GRWLock *lock = (GRWLock *) data;
+  g_assert_true (g_rw_lock_writer_trylock (lock));
+  g_rw_lock_writer_unlock (lock);
+  return NULL;
+}
+
+/* Thread function to check that an rw lock given in @data can be reader locked */
+static gpointer
+rw_lock_try_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 the lock is actually writer locked */
+      thread = g_thread_new ("rw lock writer locked", rw_lock_writer_locked_thread, &lock);
+      g_thread_join (thread);
+
+      /* Verify that we cannot take a reader lock as a writer lock is currently held */
+      thread = g_thread_new ("rw lock cannot reader lock", rw_lock_cannot_take_reader_lock_thread, &lock);
+      g_thread_join (thread);
+    }
+
+  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 */
+      thread = g_thread_new ("rw lock try reader lock", rw_lock_try_reader_lock_thread, &lock);
+      g_thread_join (thread);
+
+      /* Verify that we cannot take a writer lock as a reader lock is currently held */
+      thread = g_thread_new ("rw lock cannot 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. */
+  thread = g_thread_new ("rw lock try writer lock", rw_lock_try_writer_lock_thread, &lock);
+  g_thread_join (thread);
+}
+
 static void
 test_g_cond (void)
 {
@@ -636,6 +727,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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]