The g_once() function exists to call a callback function exactly once,
and to block multiple contending threads on its completion, then to
return its return value to all of them (so they all see the same value).
The full implementation of g_once() (in g_once_impl()) uses a mutex and
condition variable to achieve this, and is needed in the contended case,
where multiple threads need to be blocked on completion of the callback.
However, most of the times that g_once() is called, the callback will
already have been called, and it just needs to establish that it has
been called and to return the stored return value.
Previously, a fast path was used if we knew that memory barriers were
not needed on the current architecture to safely access two dependent
global variables in the presence of multi-threaded access. This is true
of all sequentially consistent architectures.
Checking whether we could use this fast path (if
`G_ATOMIC_OP_MEMORY_BARRIER_NEEDED` was *not* defined) was a bit of a
pain, though, as it required GLib to know the memory consistency model
of every architecture. This kind of knowledge is traditionally a
compiler’s domain.
So, simplify the fast path by using the compiler-provided atomic
intrinsics, and acquire-release memory consistency semantics, if they
are available. If they’re not available, fall back to always locking as
before.
We definitely need to use `__ATOMIC_ACQUIRE` in the macro implementation
of g_once(). We don’t actually need to make the `__ATOMIC_RELEASE`
changes in `gthread.c` though, since locking and unlocking a mutex
guarantees to insert a full compiler and hardware memory barrier
(enforcing sequential consistency). So the `__ATOMIC_RELEASE` changes
are only in there to make it obvious what stores are logically meant to
match up with the `__ATOMIC_ACQUIRE` loads in `gthread.h`.
Notably, only the second store (and the first load) has to be atomic.
i.e. When storing `once->retval` and `once->status`, the first store is
normal and the second is atomic. This is because the writes have a
happens-before relationship, and all (atomic or non-atomic) writes
which happen-before an atomic store/release are visible in the thread
doing an atomic load/acquire on the same atomic variable, once that load
is complete.
References:
* https://preshing.com/20120913/acquire-and-release-semantics/
* https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/_005f_005fatomic-Builtins.html
* https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
* https://en.cppreference.com/w/cpp/atomic/memory_order#Release-Acquire_ordering
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #1323
There were multi-threaded tests for g_once_init_{enter,leave}(), but not
for g_once(). Add one which tests multi-threaded contention for entering
and retrieving the value of the `GOnce`.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Helps: #1323
Unify the creation of GPtrArray.
Maybe we will add yet another constructor for creating %NULL terminated
arrays. Unify the constructors by adding an internal helper method.
The alternative instead of adding a ptr_array_new() helper, would be to
let everybody call g_ptr_array_full(). For no strong reasons, choose
this approach because the compiler is more eager to inline the static
helper as it would inlining g_ptr_array_full().
This reverts commit c0146be3a4.
The revert was originally added because the original change broke
gnome-build-meta. Now that the problem has been diagnosed, the original
commit can be fixed — see the commit which follows this one.
See: !1487
The glib-mkenums program allows generating code to handle enums/flags
with very different purposes. One of its purposes could be generating
per-enum/flag methods to be exposed in a library API, and while doing
that, it would be nice to have a way to specify in which API version
the enum/flag was introduced, so that the same version could be shown
in the generated API methods.
E.g. From the following code:
/**
* QmiWmsMessageProtocol:
* @QMI_WMS_MESSAGE_PROTOCOL_CDMA: CDMA.
* @QMI_WMS_MESSAGE_PROTOCOL_WCDMA: WCDMA.
*
* Type of message protocol.
*
* Since: 1.0
*/
typedef enum { /*< since=1.0 >*/
QMI_WMS_MESSAGE_PROTOCOL_CDMA = 0x00,
QMI_WMS_MESSAGE_PROTOCOL_WCDMA = 0x01
} QmiWmsMessageProtocol;
The template would allow us to generate a method documented like this,
including the Since tag with the value given in the mkenums 'since' tag.
/**
* qmi_wms_message_protocol_get_string:
* @val: a QmiWmsMessageProtocol.
*
* Gets the nickname string for the #QmiWmsMessageProtocol specified at @val.
*
* Returns: (transfer none): a string with the nickname, or %NULL if not found. Do not free the returned value.
* Since: 1.0
*/
const gchar *qmi_wms_message_protocol_get_string (QmiWmsMessageProtocol val);
Signed-off-by: Aleksander Morgado <aleksander@aleksander.es>
The public functions exposed as static inlines currently don't have
annotations to describe when they were introduced. This means that
compiling this file:
#include <glib.h>
void foo (void)
{
g_rec_mutex_locker_new (NULL);
}
with:
gcc -c test.c \
-I/tmp/glib/include/glib-2.0 \
-I/tmp/glib/lib/x86_64-linux-gnu/glib-2.0/include \
-Werror \
-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28 \
-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28
will not produce any error message, despite using
`g_rec_mutex_locker_new`, a function that was introduced after 2.28.
This patch adds some annotations to all the publicly exposed static
inline functions I could find.
I could not use the existing G_AVAILABLE* macros, because they may
expand to `extern`. This would then clash with the `static` keyword and
produce:
../glib/gthread.h:397:1: error: multiple storage classes in declaration specifiers
397 | static inline GRecMutexLocker *
| ^~~~~~
So I opted for adding a new set of macros,
GLIB_AVAILABLE_STATIC_INLINE_IN_2_XY.
With this patch applied, the example from above produces the expected
warning:
test.c: In function ‘foo’:
test.c:5:3: error: ‘g_rec_mutex_locker_new’ is deprecated: Not available before 2.60 [-Werror=deprecated-declarations]
5 | g_rec_mutex_locker_new (NULL);
| ^~~~~~~~~~~~~~~~~~~~~~
In file included from /tmp/glib/include/glib-2.0/glib/gasyncqueue.h:32,
from /tmp/glib/include/glib-2.0/glib.h:32,
from test.c:1:
/tmp/glib/include/glib-2.0/glib/gthread.h:398:1: note: declared here
398 | g_rec_mutex_locker_new (GRecMutex *rec_mutex)
| ^~~~~~~~~~~~~~~~~~~~~~
It is critical to mention how the identity parameter is expected to be
handled. In particular, if identity is not passed, then the identity of
the server certificate will not be checked at all. This is in contrast
to the connection-level APIs, which are supposed to be fail-safe. The
database and certificate-level APIs are more manual.