glib: add internal g_datalist_id_update_atomic() function

This commit is contained in:
Thomas Haller 2024-01-12 20:46:27 +01:00
parent dcd6c000ed
commit d101913026
4 changed files with 123 additions and 0 deletions

View File

@ -827,6 +827,107 @@ g_datalist_id_remove_no_notify (GData **datalist,
return ret_data; return ret_data;
} }
/**
* g_datalist_id_update_atomic:
* @datalist: the data list
* @key_id: they key to add.
* @callback: callback to update (set, remove, steal, update) the
* data.
* @user_data: the user data for @callback.
*
* Will call @callback while holding the lock on @datalist. Be careful, to not
* end up calling into another data-list function, because the lock is not reentrant
* and deadlock will happen.
*
* The callback receives the current data and destroy function. If the key
* is currently not in @datalist, they will be %NULL.
* The callback can update those pointers, and @datalist will be updated.
* Note that if callback modifies a received data, then it MUST steal it
* and take ownership on it. Possibly freeing it with the provided destroy function.
*
* The point is to atomically update the entry, while holding a lock.
*
* Returns: the data that @callback returns.
*/
gpointer
g_datalist_id_update_atomic (GData **datalist,
GQuark key_id,
GDataListUpdateAtomicFunc callback,
gpointer user_data)
{
GData *d;
GDataElt *data;
gpointer new_data;
GDestroyNotify new_destroy;
guint32 idx;
gboolean to_unlock = TRUE;
d = g_datalist_lock_and_get (datalist);
data = datalist_find (d, key_id, &idx);
if (data)
{
new_data = data->data;
new_destroy = data->destroy;
}
else
{
new_data = NULL;
new_destroy = NULL;
}
callback (key_id, &new_data, &new_destroy, user_data);
if (data && !new_data)
{
/* Remove.
*
* The old data->data was already stolen by callback(). */
if (idx != d->len - 1u)
*data = d->data[d->len - 1u];
d->len--;
/* We don't bother to shrink, but if all data are now gone
* we at least free the memory
*/
if (d->len == 0)
{
/* datalist may be situated in dataset, so must not be
* unlocked when we free it
*/
g_datalist_unlock_and_set (datalist, NULL);
g_free (d);
to_unlock = FALSE;
}
}
else if (data)
{
/* Update.
*
* The old data was stolen by callback(). We only update the pointers and are done. */
data->data = new_data;
data->destroy = new_destroy;
}
else if (!data && !new_data)
{
/* Absent, no change. We had no data, and nothing to add. */
}
else
{
/* Add */
if (datalist_append (&d, key_id, new_data, new_destroy))
{
g_datalist_unlock_and_set (datalist, d);
to_unlock = FALSE;
}
}
if (to_unlock)
g_datalist_unlock (datalist);
return new_data;
}
/** /**
* g_dataset_id_get_data: * g_dataset_id_get_data:
* @dataset_location: (not nullable): the location identifying the dataset. * @dataset_location: (not nullable): the location identifying the dataset.

View File

@ -38,6 +38,15 @@ G_BEGIN_DECLS
#define G_DATALIST_GET_FLAGS(datalist) \ #define G_DATALIST_GET_FLAGS(datalist) \
((gsize) g_atomic_pointer_get (datalist) & G_DATALIST_FLAGS_MASK) ((gsize) g_atomic_pointer_get (datalist) & G_DATALIST_FLAGS_MASK)
typedef gpointer (*GDataListUpdateAtomicFunc) (GQuark key_id,
gpointer *data,
GDestroyNotify *destroy_notify,
gpointer user_data);
gpointer g_datalist_id_update_atomic (GData **datalist,
GQuark key_id,
GDataListUpdateAtomicFunc callback,
gpointer user_data);
G_END_DECLS G_END_DECLS

View File

@ -24,6 +24,7 @@
#include "glib-private.h" #include "glib-private.h"
#include "glib-init.h" #include "glib-init.h"
#include "gutilsprivate.h" #include "gutilsprivate.h"
#include "gdatasetprivate.h"
#ifdef USE_INVALID_PARAMETER_HANDLER #ifdef USE_INVALID_PARAMETER_HANDLER
#include <crtdbg.h> #include <crtdbg.h>
@ -74,6 +75,8 @@ glib__private__ (void)
g_uri_get_default_scheme_port, g_uri_get_default_scheme_port,
g_set_prgname_once, g_set_prgname_once,
g_datalist_id_update_atomic,
}; };
return &table; return &table;

View File

@ -75,6 +75,11 @@ void __lsan_ignore_object (const void *p) __attribute__ ((weak));
*/ */
#define G_CONTAINER_OF(ptr, type, field) ((type *) G_STRUCT_MEMBER_P (ptr, -G_STRUCT_OFFSET (type, field))) #define G_CONTAINER_OF(ptr, type, field) ((type *) G_STRUCT_MEMBER_P (ptr, -G_STRUCT_OFFSET (type, field)))
typedef gpointer (*GDataListUpdateAtomicFunc) (GQuark key_id,
gpointer *data,
GDestroyNotify *destroy_notify,
gpointer user_data);
/* /*
* g_leak_sanitizer_is_supported: * g_leak_sanitizer_is_supported:
* *
@ -291,6 +296,11 @@ typedef struct {
/* See gutils.c */ /* See gutils.c */
gboolean (* g_set_prgname_once) (const gchar *prgname); gboolean (* g_set_prgname_once) (const gchar *prgname);
gpointer (*g_datalist_id_update_atomic) (GData **datalist,
GQuark key_id,
GDataListUpdateAtomicFunc callback,
gpointer user_data);
/* Add other private functions here, initialize them in glib-private.c */ /* Add other private functions here, initialize them in glib-private.c */
} GLibPrivateVTable; } GLibPrivateVTable;