Merge branch 'auto-lock' into 'main'

Add G_*_AUTO_LOCK macros

Closes #3223

See merge request GNOME/glib!3822
This commit is contained in:
Philip Withnall 2024-01-21 21:02:35 +00:00
commit aee343fe5c
3 changed files with 251 additions and 3 deletions

View File

@ -142,6 +142,20 @@
* %G_LOCK_DEFINE. * %G_LOCK_DEFINE.
*/ */
/**
* G_AUTO_LOCK:
* @name: the name of the lock
*
* Works like [func@GLib.MUTEX_AUTO_LOCK], but for a lock defined with
* [func@GLib.LOCK_DEFINE].
*
* This feature is only supported on GCC and clang. This macro is not defined on
* other compilers and should not be used in programs that are intended to be
* portable to those compilers.
*
* Since: 2.80
*/
/* GMutex Documentation {{{1 ------------------------------------------ */ /* GMutex Documentation {{{1 ------------------------------------------ */
/** /**

View File

@ -139,6 +139,10 @@ struct _GOnce
# define G_TRYLOCK(name) g_mutex_trylock (&G_LOCK_NAME (name)) # define G_TRYLOCK(name) g_mutex_trylock (&G_LOCK_NAME (name))
#endif /* !G_DEBUG_LOCKS */ #endif /* !G_DEBUG_LOCKS */
#ifdef g_autoptr
#define G_AUTO_LOCK(name) G_MUTEX_AUTO_LOCK (&G_LOCK_NAME (name), g__##name##_locker)
#endif /* g_autoptr */
GLIB_AVAILABLE_IN_2_32 GLIB_AVAILABLE_IN_2_32
GThread * g_thread_ref (GThread *thread); GThread * g_thread_ref (GThread *thread);
GLIB_AVAILABLE_IN_2_32 GLIB_AVAILABLE_IN_2_32
@ -339,7 +343,7 @@ typedef void GMutexLocker;
* *
* // Code with mutex locked here * // Code with mutex locked here
* *
* if (cond) * if (condition)
* // No need to unlock * // No need to unlock
* return; * return;
* *
@ -350,6 +354,10 @@ typedef void GMutexLocker;
* } * }
* ]| * ]|
* *
* Note that it is common for the declared variable to not be used in the scope,
* which causes some compilers to warn. That can be avoided by using
* `G_GNUC_UNUSED` or, since 2.80, [func@GLib.MUTEX_AUTO_LOCK].
*
* Returns: a #GMutexLocker * Returns: a #GMutexLocker
* Since: 2.44 * Since: 2.44
*/ */
@ -378,6 +386,49 @@ g_mutex_locker_free (GMutexLocker *locker)
g_mutex_unlock ((GMutex *) locker); g_mutex_unlock ((GMutex *) locker);
} }
/**
* G_MUTEX_AUTO_LOCK:
* @mutex: a [type@GLib.Mutex]
* @var: a variable name to be declared
*
* Declare a [type@GLib.MutexLocker] variable with `g_autoptr()` and lock the
* mutex. The mutex will be unlocked automatically when leaving the scope. The
* variable is declared with `G_GNUC_UNUSED` to avoid compiler warning if it is
* not used in the scope.
*
* This feature is only supported on GCC and clang. This macro is not defined on
* other compilers and should not be used in programs that are intended to be
* portable to those compilers.
*
* Note that this should be used in a place where it is allowed to declare a
* variable, which could be before any statement in the case
* `-Wdeclaration-after-statement` is used, or C standard prior to C99.
*
* ```c
* {
* G_MUTEX_AUTO_LOCK (&obj->mutex, locker);
*
* obj->stuff_with_lock ();
* if (condition)
* {
* // No need to unlock
* return;
* }
*
* // Unlock before end of scope
* g_clear_pointer (&locker, g_mutex_locker_free);
* obj->stuff_without_lock ();
* }
* ```
*
* Since: 2.80.0
*/
#ifdef g_autoptr
#define G_MUTEX_AUTO_LOCK(mutex, var) \
GLIB_AVAILABLE_MACRO_IN_2_80 g_autoptr (GMutexLocker) \
G_GNUC_UNUSED var = g_mutex_locker_new (mutex)
#endif /* g_autoptr */
/** /**
* GRecMutexLocker: * GRecMutexLocker:
* *
@ -414,7 +465,7 @@ typedef void GRecMutexLocker;
* *
* // Code with rec_mutex locked here * // Code with rec_mutex locked here
* *
* if (cond) * if (condition)
* // No need to unlock * // No need to unlock
* return; * return;
* *
@ -425,6 +476,10 @@ typedef void GRecMutexLocker;
* } * }
* ]| * ]|
* *
* Note that it is common for the declared variable to not be used in the scope,
* which causes some compilers to warn. That can be avoided by using
* `G_GNUC_UNUSED` or, since 2.80, [func@GLib.REC_MUTEX_AUTO_LOCK].
*
* Returns: a #GRecMutexLocker * Returns: a #GRecMutexLocker
* Since: 2.60 * Since: 2.60
*/ */
@ -457,6 +512,49 @@ g_rec_mutex_locker_free (GRecMutexLocker *locker)
} }
G_GNUC_END_IGNORE_DEPRECATIONS G_GNUC_END_IGNORE_DEPRECATIONS
/**
* G_REC_MUTEX_AUTO_LOCK:
* @mutex: a [type@GLib.RecMutex]
* @var: a variable name to be declared
*
* Declare a [type@GLib.RecMutexLocker] variable with `g_autoptr()` and lock the
* mutex. The mutex will be unlocked automatically when leaving the scope. The
* variable is declared with `G_GNUC_UNUSED` to avoid compiler warning if it is
* not used in the scope.
*
* This feature is only supported on GCC and clang. This macro is not defined on
* other compilers and should not be used in programs that are intended to be
* portable to those compilers.
*
* Note that this should be used in a place where it is allowed to declare a
* variable, which could be before any statement in the case
* `-Wdeclaration-after-statement` is used, or C standard prior to C99.
*
* ```c
* {
* G_REC_MUTEX_AUTO_LOCK (&obj->rec_mutex, locker);
*
* obj->stuff_with_lock ();
* if (condition)
* {
* // No need to unlock
* return;
* }
*
* // Unlock before end of scope
* g_clear_pointer (&locker, g_rec_mutex_locker_free);
* obj->stuff_without_lock ();
* }
* ```
*
* Since: 2.80.0
*/
#ifdef g_autoptr
#define G_REC_MUTEX_AUTO_LOCK(mutex, var) \
GLIB_AVAILABLE_MACRO_IN_2_80 g_autoptr (GRecMutexLocker) \
G_GNUC_UNUSED var = g_rec_mutex_locker_new (mutex)
#endif /* g_autoptr */
/** /**
* GRWLockWriterLocker: * GRWLockWriterLocker:
* *
@ -520,7 +618,7 @@ typedef void GRWLockWriterLocker;
* if (self->array == NULL) * if (self->array == NULL)
* self->array = g_ptr_array_new (); * self->array = g_ptr_array_new ();
* *
* if (cond) * if (condition)
* // No need to unlock * // No need to unlock
* return; * return;
* *
@ -535,6 +633,10 @@ typedef void GRWLockWriterLocker;
* } * }
* ]| * ]|
* *
* Note that it is common for the declared variable to not be used in the scope,
* which causes some compilers to warn. That can be avoided by using
* `G_GNUC_UNUSED` or, since 2.80, [func@GLib.RW_LOCK_WRITER_AUTO_LOCK].
*
* Returns: a #GRWLockWriterLocker * Returns: a #GRWLockWriterLocker
* Since: 2.62 * Since: 2.62
*/ */
@ -568,6 +670,49 @@ g_rw_lock_writer_locker_free (GRWLockWriterLocker *locker)
} }
G_GNUC_END_IGNORE_DEPRECATIONS G_GNUC_END_IGNORE_DEPRECATIONS
/**
* G_RW_LOCK_WRITER_AUTO_LOCK:
* @mutex: a [type@GLib.RWLock]
* @var: a variable name to be declared
*
* Declare a [type@GLib.RWLockWriterLocker] variable with `g_autoptr()` and lock
* for writing. The mutex will be unlocked automatically when leaving the scope.
* The variable is declared with `G_GNUC_UNUSED` to avoid compiler warning if it
* is not used in the scope.
*
* This feature is only supported on GCC and clang. This macro is not defined on
* other compilers and should not be used in programs that are intended to be
* portable to those compilers.
*
* Note that this should be used in a place where it is allowed to declare a
* variable, which could be before any statement in the case
* `-Wdeclaration-after-statement` is used, or C standard prior to C99.
*
* ```c
* {
* G_RW_LOCK_WRITER_AUTO_LOCK (&obj->rw_lock, locker);
*
* obj->stuff_with_lock ();
* if (condition)
* {
* // No need to unlock
* return;
* }
*
* // Unlock before end of scope
* g_clear_pointer (&locker, g_rw_lock_writer_locker_free);
* obj->stuff_without_lock ();
* }
* ```
*
* Since: 2.80.0
*/
#ifdef g_autoptr
#define G_RW_LOCK_WRITER_AUTO_LOCK(mutex, var) \
GLIB_AVAILABLE_MACRO_IN_2_80 g_autoptr (GRWLockWriterLocker) \
G_GNUC_UNUSED var = g_rw_lock_writer_locker_new (mutex)
#endif /* g_autoptr */
/** /**
* GRWLockReaderLocker: * GRWLockReaderLocker:
* *
@ -623,6 +768,49 @@ g_rw_lock_reader_locker_free (GRWLockReaderLocker *locker)
} }
G_GNUC_END_IGNORE_DEPRECATIONS G_GNUC_END_IGNORE_DEPRECATIONS
/**
* G_RW_LOCK_READER_AUTO_LOCK:
* @mutex: a [type@GLib.RWLock]
* @var: a variable name to be declared
*
* Declare a [type@GLib.RWLockReaderLocker] variable with `g_autoptr()` and lock
* for reading. The mutex will be unlocked automatically when leaving the scope.
* The variable is declared with `G_GNUC_UNUSED` to avoid compiler warning if it
* is not used in the scope.
*
* This feature is only supported on GCC and clang. This macro is not defined on
* other compilers and should not be used in programs that are intended to be
* portable to those compilers.
*
* Note that this should be used in a place where it is allowed to declare a
* variable, which could be before any statement in the case
* `-Wdeclaration-after-statement` is used, or C standard prior to C99.
*
* ```c
* {
* G_RW_LOCK_READER_AUTO_LOCK (&obj->rw_lock, locker);
*
* obj->stuff_with_lock ();
* if (condition)
* {
* // No need to unlock
* return;
* }
*
* // Unlock before end of scope
* g_clear_pointer (&locker, g_rw_lock_reader_locker_free);
* obj->stuff_without_lock ();
* }
* ```
*
* Since: 2.80.0
*/
#ifdef g_autoptr
#define G_RW_LOCK_READER_AUTO_LOCK(mutex, var) \
GLIB_AVAILABLE_MACRO_IN_2_80 g_autoptr (GRWLockReaderLocker) \
G_GNUC_UNUSED var = g_rw_lock_reader_locker_new (mutex)
#endif /* g_autoptr */
G_END_DECLS G_END_DECLS
#endif /* __G_THREAD_H__ */ #endif /* __G_THREAD_H__ */

View File

@ -399,6 +399,12 @@ test_g_mutex_locker (void)
g_mutex_init (&mutex); g_mutex_init (&mutex);
if (TRUE)
{
/* val is unused in this scope but compiler should not warn. */
G_MUTEX_AUTO_LOCK (&mutex, val);
}
if (TRUE) if (TRUE)
{ {
g_autoptr(GMutexLocker) val = g_mutex_locker_new (&mutex); g_autoptr(GMutexLocker) val = g_mutex_locker_new (&mutex);
@ -442,6 +448,12 @@ test_g_rec_mutex_locker (void)
g_rec_mutex_init (&rec_mutex); g_rec_mutex_init (&rec_mutex);
if (TRUE)
{
/* val is unused in this scope but compiler should not warn. */
G_REC_MUTEX_AUTO_LOCK (&rec_mutex, val);
}
if (TRUE) if (TRUE)
{ {
g_autoptr(GRecMutexLocker) val = g_rec_mutex_locker_new (&rec_mutex); g_autoptr(GRecMutexLocker) val = g_rec_mutex_locker_new (&rec_mutex);
@ -492,6 +504,18 @@ test_g_rw_lock_lockers (void)
g_rw_lock_init (&lock); g_rw_lock_init (&lock);
if (TRUE)
{
/* val is unused in this scope but compiler should not warn. */
G_RW_LOCK_WRITER_AUTO_LOCK (&lock, val);
}
if (TRUE)
{
/* val is unused in this scope but compiler should not warn. */
G_RW_LOCK_READER_AUTO_LOCK (&lock, val);
}
if (TRUE) if (TRUE)
{ {
g_autoptr(GRWLockWriterLocker) val = g_rw_lock_writer_locker_new (&lock); g_autoptr(GRWLockWriterLocker) val = g_rw_lock_writer_locker_new (&lock);
@ -533,6 +557,27 @@ test_g_rw_lock_lockers (void)
g_rw_lock_clear (&lock); g_rw_lock_clear (&lock);
} }
G_LOCK_DEFINE (test_g_auto_lock);
static void
test_g_auto_lock (void)
{
GThread *thread;
if (TRUE)
{
G_AUTO_LOCK (test_g_auto_lock);
/* Verify that the mutex is actually locked */
thread = g_thread_new ("mutex locked", mutex_locked_thread, &G_LOCK_NAME (test_g_auto_lock));
g_thread_join (thread);
}
/* Verify that the mutex is unlocked again */
thread = g_thread_new ("mutex unlocked", mutex_unlocked_thread, &G_LOCK_NAME (test_g_auto_lock));
g_thread_join (thread);
}
static void static void
test_g_cond (void) test_g_cond (void)
{ {
@ -785,6 +830,7 @@ main (int argc, gchar *argv[])
g_test_add_func ("/autoptr/g_mutex_locker", test_g_mutex_locker); 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_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_rw_lock_lockers", test_g_rw_lock_lockers);
g_test_add_func ("/autoptr/g_auto_lock", test_g_auto_lock);
g_test_add_func ("/autoptr/g_cond", test_g_cond); 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_timer", test_g_timer);
g_test_add_func ("/autoptr/g_time_zone", test_g_time_zone); g_test_add_func ("/autoptr/g_time_zone", test_g_time_zone);