diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml
index a0716c172..26cdafb67 100644
--- a/docs/reference/glib/glib-docs.xml
+++ b/docs/reference/glib/glib-docs.xml
@@ -119,6 +119,7 @@
+
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 0183b0898..331d92c75 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -3449,3 +3449,18 @@ g_hostname_is_ip_address
g_uuid_string_is_valid
g_uuid_string_random
+
+
+refcount
+grefcount
+g_ref_count_init
+g_ref_count_inc
+g_ref_count_dec
+g_ref_count_compare
+
+gatomicrefcount
+g_atomic_ref_count_init
+g_atomic_ref_count_inc
+g_atomic_ref_count_dec
+g_atomic_ref_count_compare
+
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 049706126..4d04e09da 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -149,6 +149,7 @@ libglib_2_0_la_SOURCES = \
gquark.c \
gqueue.c \
grand.c \
+ grefcount.c \
gregex.c \
gscanner.c \
gscripttable.h \
@@ -284,6 +285,7 @@ glibsubinclude_HEADERS = \
gquark.h \
gqueue.h \
grand.h \
+ grefcount.h \
gregex.h \
gscanner.h \
gsequence.h \
diff --git a/glib/garray.c b/glib/garray.c
index 39c87ca88..8f908e443 100644
--- a/glib/garray.c
+++ b/glib/garray.c
@@ -41,7 +41,7 @@
#include "gthread.h"
#include "gmessages.h"
#include "gqsort.h"
-
+#include "grefcount.h"
/**
* SECTION:arrays
@@ -106,7 +106,7 @@ struct _GRealArray
guint elt_size;
guint zero_terminated : 1;
guint clear : 1;
- gint ref_count;
+ gatomicrefcount ref_count;
GDestroyNotify clear_func;
};
@@ -199,9 +199,10 @@ g_array_sized_new (gboolean zero_terminated,
array->zero_terminated = (zero_terminated ? 1 : 0);
array->clear = (clear ? 1 : 0);
array->elt_size = elt_size;
- array->ref_count = 1;
array->clear_func = NULL;
+ g_atomic_ref_count_init (&array->ref_count);
+
if (array->zero_terminated || reserved_size != 0)
{
g_array_maybe_expand (array, reserved_size);
@@ -257,7 +258,7 @@ g_array_ref (GArray *array)
GRealArray *rarray = (GRealArray*) array;
g_return_val_if_fail (array, NULL);
- g_atomic_int_inc (&rarray->ref_count);
+ g_atomic_ref_count_inc (&rarray->ref_count);
return array;
}
@@ -287,7 +288,7 @@ g_array_unref (GArray *array)
GRealArray *rarray = (GRealArray*) array;
g_return_if_fail (array);
- if (g_atomic_int_dec_and_test (&rarray->ref_count))
+ if (g_atomic_ref_count_dec (&rarray->ref_count))
array_free (rarray, FREE_SEGMENT);
}
@@ -346,7 +347,7 @@ g_array_free (GArray *farray,
flags = (free_segment ? FREE_SEGMENT : 0);
/* if others are holding a reference, preserve the wrapper but do free/return the data */
- if (!g_atomic_int_dec_and_test (&array->ref_count))
+ if (!g_atomic_ref_count_dec (&array->ref_count))
flags |= PRESERVE_WRAPPER;
return array_free (array, flags);
@@ -882,7 +883,7 @@ struct _GRealPtrArray
gpointer *pdata;
guint len;
guint alloc;
- gint ref_count;
+ gatomicrefcount ref_count;
GDestroyNotify element_free_func;
};
@@ -936,9 +937,10 @@ g_ptr_array_sized_new (guint reserved_size)
array->pdata = NULL;
array->len = 0;
array->alloc = 0;
- array->ref_count = 1;
array->element_free_func = NULL;
+ g_atomic_ref_count_init (&array->ref_count);
+
if (reserved_size != 0)
g_ptr_array_maybe_expand (array, reserved_size);
@@ -1041,7 +1043,7 @@ g_ptr_array_ref (GPtrArray *array)
g_return_val_if_fail (array, NULL);
- g_atomic_int_inc (&rarray->ref_count);
+ g_atomic_ref_count_inc (&rarray->ref_count);
return array;
}
@@ -1066,7 +1068,7 @@ g_ptr_array_unref (GPtrArray *array)
g_return_if_fail (array);
- if (g_atomic_int_dec_and_test (&rarray->ref_count))
+ if (g_atomic_ref_count_dec (&rarray->ref_count))
ptr_array_free (array, FREE_SEGMENT);
}
@@ -1107,7 +1109,7 @@ g_ptr_array_free (GPtrArray *array,
/* if others are holding a reference, preserve the wrapper but
* do free/return the data
*/
- if (!g_atomic_int_dec_and_test (&rarray->ref_count))
+ if (!g_atomic_ref_count_dec (&rarray->ref_count))
flags |= PRESERVE_WRAPPER;
return ptr_array_free (array, flags);
diff --git a/glib/gbytes.c b/glib/gbytes.c
index 3b14a51cd..74f8148f6 100644
--- a/glib/gbytes.c
+++ b/glib/gbytes.c
@@ -30,6 +30,7 @@
#include
#include
#include
+#include
#include
@@ -69,7 +70,7 @@ struct _GBytes
{
gconstpointer data; /* may be NULL iff (size == 0) */
gsize size; /* may be 0 */
- gint ref_count;
+ gatomicrefcount ref_count;
GDestroyNotify free_func;
gpointer user_data;
};
@@ -187,7 +188,7 @@ g_bytes_new_with_free_func (gconstpointer data,
bytes->size = size;
bytes->free_func = free_func;
bytes->user_data = user_data;
- bytes->ref_count = 1;
+ g_atomic_ref_count_init (&bytes->ref_count);
return (GBytes *)bytes;
}
@@ -310,7 +311,7 @@ g_bytes_ref (GBytes *bytes)
{
g_return_val_if_fail (bytes != NULL, NULL);
- g_atomic_int_inc (&bytes->ref_count);
+ g_atomic_ref_count_inc (&bytes->ref_count);
return bytes;
}
@@ -330,7 +331,7 @@ g_bytes_unref (GBytes *bytes)
if (bytes == NULL)
return;
- if (g_atomic_int_dec_and_test (&bytes->ref_count))
+ if (g_atomic_ref_count_dec (&bytes->ref_count))
{
if (bytes->free_func != NULL)
bytes->free_func (bytes->user_data);
@@ -438,7 +439,7 @@ try_steal_and_unref (GBytes *bytes,
return NULL;
/* Are we the only reference? */
- if (g_atomic_int_get (&bytes->ref_count) == 1)
+ if (g_atomic_ref_count_compare (&bytes->ref_count, 1))
{
*size = bytes->size;
result = (gpointer)bytes->data;
diff --git a/glib/ghash.c b/glib/ghash.c
index 87218114f..d93169066 100644
--- a/glib/ghash.c
+++ b/glib/ghash.c
@@ -37,7 +37,7 @@
#include "gatomic.h"
#include "gtestutils.h"
#include "gslice.h"
-
+#include "grefcount.h"
/**
* SECTION:hash_tables
@@ -227,7 +227,7 @@ struct _GHashTable
GHashFunc hash_func;
GEqualFunc key_equal_func;
- gint ref_count;
+ gatomicrefcount ref_count;
#ifndef G_DISABLE_ASSERT
/*
* Tracks the structure of the hash table, not its contents: is only
@@ -374,7 +374,7 @@ g_hash_table_lookup_node (GHashTable *hash_table,
* (as keys, etc. will be NULL).
* Applications need to either use g_hash_table_destroy, or ensure the hash
* table is empty prior to removing the last reference using g_hash_table_unref(). */
- g_assert (hash_table->ref_count > 0);
+ g_assert (!g_atomic_ref_count_compare (&hash_table->ref_count, 0));
hash_value = hash_table->hash_func (key);
if (G_UNLIKELY (!HASH_IS_REAL (hash_value)))
@@ -716,11 +716,11 @@ g_hash_table_new_full (GHashFunc hash_func,
hash_table = g_slice_new (GHashTable);
g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT);
+ g_atomic_ref_count_init (&hash_table->ref_count);
hash_table->nnodes = 0;
hash_table->noccupied = 0;
hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
hash_table->key_equal_func = key_equal_func;
- hash_table->ref_count = 1;
#ifndef G_DISABLE_ASSERT
hash_table->version = 0;
#endif
@@ -1077,7 +1077,7 @@ g_hash_table_ref (GHashTable *hash_table)
{
g_return_val_if_fail (hash_table != NULL, NULL);
- g_atomic_int_inc (&hash_table->ref_count);
+ g_atomic_ref_count_inc (&hash_table->ref_count);
return hash_table;
}
@@ -1098,7 +1098,7 @@ g_hash_table_unref (GHashTable *hash_table)
{
g_return_if_fail (hash_table != NULL);
- if (g_atomic_int_dec_and_test (&hash_table->ref_count))
+ if (g_atomic_ref_count_dec (&hash_table->ref_count))
{
g_hash_table_remove_all_nodes (hash_table, TRUE, TRUE);
if (hash_table->keys != hash_table->values)
diff --git a/glib/glib.h b/glib/glib.h
index 4f5a7f702..84299c4f9 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -69,6 +69,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/glib/grefcount.c b/glib/grefcount.c
new file mode 100644
index 000000000..37085316b
--- /dev/null
+++ b/glib/grefcount.c
@@ -0,0 +1,285 @@
+/* grefcount.c: Reference counting
+ *
+ * Copyright 2018 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+/**
+ * SECTION:refcount
+ * @Title: Reference counting
+ * @Short_description: Reference counting types and functions
+ *
+ * Reference counting is a garbage collection mechanism that is based on
+ * assigning a counter to a data type, or any memory area; the counter is
+ * increased whenever a new reference to that data type is acquired, and
+ * decreased whenever the reference is released. Once the last reference
+ * is released, the resources associated to that data type are freed.
+ *
+ * GLib uses reference counting in many of its data types, and provides
+ * the #grefcount and #gatomicrefcount types to implement safe and atomic
+ * reference counting semantics in new data types.
+ *
+ * It is important to note that #grefcount and #gatomicrefcount should be
+ * considered completely opaque types; you should always use the provided
+ * API to increase and decrease the counters, and you should never check
+ * their content directly, or compare their content with other values.
+ *
+ * Since: 2.58
+ */
+
+#include "config.h"
+
+#include "grefcount.h"
+
+#include "gatomic.h"
+#include "gmessages.h"
+
+/**
+ * grefcount:
+ *
+ * A type for implementing non-atomic reference count semantics.
+ *
+ * Use g_ref_count_init() to initialize it; g_ref_count_inc() to
+ * increase the counter, and g_ref_count_dec() to decrease it.
+ *
+ * It is safe to use #grefcount only if you're expecting to operate
+ * on the reference counter from a single thread. It is entirely up
+ * to you to ensure that all reference count changes happen in the
+ * same thread.
+ *
+ * See also: #gatomicrefcount
+ *
+ * Since: 2.58
+ */
+
+/**
+ * gatomicrefcount:
+ *
+ * A type for implementing atomic reference count semantics.
+ *
+ * Use g_atomic_ref_count_init() to initialize it; g_atomic_ref_count_inc()
+ * to increase the counter, and g_atomic_ref_count_dec() to decrease it.
+ *
+ * It is safe to use #gatomicrefcount if you're expecting to operate on the
+ * reference counter from multiple threads.
+ *
+ * See also: #grefcount
+ *
+ * Since: 2.58
+ */
+
+/**
+ * g_ref_count_init:
+ * @rc: the address of a reference count variable
+ *
+ * Initializes a reference count variable.
+ *
+ * Since: 2.58
+ */
+void
+(g_ref_count_init) (grefcount *rc)
+{
+ g_return_if_fail (rc != NULL);
+
+ /* Non-atomic refcounting is implemented using the negative range
+ * of signed integers:
+ *
+ * G_MININT Z¯< 0 > Z⁺ G_MAXINT
+ * |----------------------------|----------------------------|
+ *
+ * Acquiring a reference moves us towards MININT, and releasing a
+ * reference moves us towards 0.
+ */
+ *rc = -1;
+}
+
+/**
+ * g_ref_count_inc:
+ * @rc: the address of a reference count variable
+ *
+ * Increases the reference count.
+ *
+ * Since: 2.58
+ */
+void
+(g_ref_count_inc) (grefcount *rc)
+{
+ grefcount rrc;
+
+ g_return_if_fail (rc != NULL);
+
+ rrc = *rc;
+
+ g_return_if_fail (rrc < 0);
+
+ /* Check for saturation */
+ if (rrc == G_MININT)
+ {
+ g_critical ("Reference count %p has reached saturation", rc);
+ return;
+ }
+
+ rrc -= 1;
+
+ *rc = rrc;
+}
+
+/**
+ * g_ref_count_dec:
+ * @rc: the address of a reference count variable
+ *
+ * Decreases the reference count.
+ *
+ * Returns: %TRUE if the reference count reached 0, and %FALSE otherwise
+ *
+ * Since: 2.58
+ */
+gboolean
+(g_ref_count_dec) (grefcount *rc)
+{
+ grefcount rrc;
+
+ g_return_val_if_fail (rc != NULL, FALSE);
+
+ rrc = *rc;
+
+ g_return_val_if_fail (rrc < 0, FALSE);
+
+ rrc += 1;
+ if (rrc == 0)
+ return TRUE;
+
+ *rc = rrc;
+
+ return FALSE;
+}
+
+/**
+ * g_ref_count_compare:
+ * @rc: the address of a reference count variable
+ * @val: the value to compare
+ *
+ * Compares the current value of @rc with @val.
+ *
+ * Returns: %TRUE if the reference count is the same
+ * as the given value
+ *
+ * Since: 2.58
+ */
+gboolean
+(g_ref_count_compare) (grefcount *rc,
+ gint val)
+{
+ grefcount rrc;
+
+ g_return_val_if_fail (rc != NULL, FALSE);
+ g_return_val_if_fail (val >= 0, FALSE);
+
+ rrc = *rc;
+
+ if (val == G_MAXINT)
+ return rrc == G_MININT;
+
+ return rrc == -val;
+}
+
+/**
+ * g_atomic_ref_count_init:
+ * @arc: the address of an atomic reference count variable
+ *
+ * Atomically initializes a reference count variable.
+ *
+ * Since: 2.58
+ */
+void
+(g_atomic_ref_count_init) (gatomicrefcount *arc)
+{
+ g_return_if_fail (arc != NULL);
+
+ /* Atomic refcounting is implemented using the positive range
+ * of signed integers:
+ *
+ * G_MININT Z¯< 0 > Z⁺ G_MAXINT
+ * |----------------------------|----------------------------|
+ *
+ * Acquiring a reference moves us towards MAXINT, and releasing a
+ * reference moves us towards 0.
+ */
+ g_atomic_int_set (arc, 1);
+}
+
+/**
+ * g_atomic_ref_count_inc:
+ * @arc: the address of an atomic reference count variable
+ *
+ * Atomically increases the reference count.
+ *
+ * Since: 2.58
+ */
+void
+(g_atomic_ref_count_inc) (gatomicrefcount *arc)
+{
+ g_return_if_fail (arc != NULL);
+ g_return_if_fail (g_atomic_int_get (arc) > 0);
+
+ if (g_atomic_int_get (arc) == G_MAXINT)
+ {
+ g_critical ("Reference count has reached saturation");
+ return;
+ }
+
+ g_atomic_int_inc (arc);
+}
+
+/**
+ * g_atomic_ref_count_dec:
+ * @arc: the address of an atomic reference count variable
+ *
+ * Atomically decreases the reference count.
+ *
+ * Returns: %TRUE if the reference count reached 0, and %FALSE otherwise
+ *
+ * Since: 2.58
+ */
+gboolean
+(g_atomic_ref_count_dec) (gatomicrefcount *arc)
+{
+ g_return_val_if_fail (arc != NULL, FALSE);
+ g_return_val_if_fail (g_atomic_int_get (arc) > 0, FALSE);
+
+ return g_atomic_int_dec_and_test (arc);
+}
+
+/**
+ * g_atomic_ref_count_compare:
+ * @arc: the address of an atomic reference count variable
+ * @val: the value to compare
+ *
+ * Atomically compares the current value of @arc with @val.
+ *
+ * Returns: %TRUE if the reference count is the same
+ * as the given value
+ *
+ * Since: 2.58
+ */
+gboolean
+(g_atomic_ref_count_compare) (gatomicrefcount *arc,
+ gint val)
+{
+ g_return_val_if_fail (arc != NULL, FALSE);
+ g_return_val_if_fail (val >= 0, FALSE);
+
+ return g_atomic_int_get (arc) == val;
+}
diff --git a/glib/grefcount.h b/glib/grefcount.h
new file mode 100644
index 000000000..dec9a5ffb
--- /dev/null
+++ b/glib/grefcount.h
@@ -0,0 +1,122 @@
+/* grefcount.h: Reference counting
+ *
+ * Copyright 2018 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#ifndef __GREFCOUNT_H__
+#define __GREFCOUNT_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#include
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_2_58
+void g_ref_count_init (grefcount *rc);
+GLIB_AVAILABLE_IN_2_58
+void g_ref_count_inc (grefcount *rc);
+GLIB_AVAILABLE_IN_2_58
+gboolean g_ref_count_dec (grefcount *rc);
+GLIB_AVAILABLE_IN_2_58
+gboolean g_ref_count_compare (grefcount *rc,
+ gint val);
+
+GLIB_AVAILABLE_IN_2_58
+void g_atomic_ref_count_init (gatomicrefcount *arc);
+GLIB_AVAILABLE_IN_2_58
+void g_atomic_ref_count_inc (gatomicrefcount *arc);
+GLIB_AVAILABLE_IN_2_58
+gboolean g_atomic_ref_count_dec (gatomicrefcount *arc);
+GLIB_AVAILABLE_IN_2_58
+gboolean g_atomic_ref_count_compare (gatomicrefcount *arc,
+ gint val);
+
+/* On GCC we can use __extension__ to inline the API without using
+ * ancillary functions; we only do this when disabling checks, as
+ * it disables warnings when saturating the reference counters
+ */
+#if defined(__GNUC__) && defined(G_DISABLE_CHECKS)
+
+# define g_ref_count_init(rc) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (grefcount)); \
+ (void) (0 ? *(rc) ^ *(rc) : 1); \
+ *(rc) = -1; \
+ }))
+
+# define g_ref_count_inc(rc) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (grefcount)); \
+ (void) (0 ? *(rc) ^ *(rc) : 1); \
+ if (*(rc) == G_MININT) ; else { \
+ *(rc) -= 1; \
+ } \
+ }))
+
+# define g_ref_count_dec(rc) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (grefcount)); \
+ grefcount __rc = *(rc); \
+ __rc += 1; \
+ if (__rc == 0) ; else { \
+ *(rc) = __rc; \
+ } \
+ (gboolean) (__rc == 0); \
+ }))
+
+# define g_ref_count_compare(rc,val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (grefcount)); \
+ (void) (0 ? *(rc) ^ (val) : 1); \
+ (gboolean) (*(rc) == -(val)); \
+ }))
+
+# define g_atomic_ref_count_init(rc) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (gatomicrefcount)); \
+ (void) (0 ? *(rc) ^ *(rc) : 1); \
+ g_atomic_int_set ((rc), 1); \
+ }))
+
+# define g_atomic_ref_count_inc(rc) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (gatomicrefcount)); \
+ (void) (0 ? *(rc) ^ *(rc) : 1); \
+ (void) (g_atomic_int_get (rc) == G_MAXINT ? 0 : g_atomic_int_inc ((rc))); \
+ }))
+
+# define g_atomic_ref_count_dec(rc) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (gatomicrefcount)); \
+ (void) (0 ? *(rc) ^ *(rc) : 1); \
+ g_atomic_int_dec_and_test ((rc)); \
+ }))
+
+# define g_atomic_ref_count_compare(rc,val) \
+ (G_GNUC_EXTENSION ({ \
+ G_STATIC_ASSERT (sizeof *(rc) == sizeof (gatomicrefcount)); \
+ (void) (0 ? *(rc) ^ (val) : 1); \
+ (gboolean) (g_atomic_int_get (rc) == (val)); \
+ }))
+
+#endif /* __GNUC__ && G_DISABLE_CHECKS */
+
+G_END_DECLS
+
+#endif /* __GREFCOUNT_H__ */
diff --git a/glib/gtypes.h b/glib/gtypes.h
index 09d9bd145..67adb7f1f 100644
--- a/glib/gtypes.h
+++ b/glib/gtypes.h
@@ -510,6 +510,9 @@ struct _GTimeVal
glong tv_usec;
};
+typedef gint grefcount;
+typedef volatile gint gatomicrefcount;
+
G_END_DECLS
/* We prefix variable declarations so they can
diff --git a/glib/meson.build b/glib/meson.build
index 036d1f4d6..76d354c2a 100644
--- a/glib/meson.build
+++ b/glib/meson.build
@@ -76,6 +76,7 @@ glib_sub_headers = files(
'gquark.h',
'gqueue.h',
'grand.h',
+ 'grefcount.h',
'gregex.h',
'gscanner.h',
'gsequence.h',
@@ -159,6 +160,7 @@ glib_sources = files(
'gquark.c',
'gqueue.c',
'grand.c',
+ 'grefcount.c',
'gregex.c',
'gscanner.c',
'gsequence.c',
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index e0619c475..cf05bc74f 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -47,6 +47,8 @@ glib_tests = [
'queue',
'rand',
'rec-mutex',
+ 'refcount',
+ 'refcount-macro',
'regex',
'rwlock',
'scannerapi',
@@ -108,14 +110,23 @@ slow_tests = [
foreach test_name : glib_tests
deps = [libm, thread_dep, libglib_dep]
+ source = test_name + '.c'
+ c_args = test_cargs + ['-DPCRE_STATIC']
if test_name == 'regex'
deps += [pcre]
endif
if test_name == 'gdatetime'
deps += [libintl]
endif
- exe = executable(test_name, '@0@.c'.format(test_name),
- c_args : ['-DPCRE_STATIC'] + test_cargs,
+ # We build the refcount test twice: one to test the function-based API,
+ # and the other to test the macro-based API that is used when disabling
+ # checks
+ if test_name == 'refcount-macro'
+ source = 'refcount.c'
+ c_args += ['-DG_DISABLE_CHECKS']
+ endif
+ exe = executable(test_name, source,
+ c_args : c_args,
dependencies : deps,
install : false,
)
diff --git a/glib/tests/refcount.c b/glib/tests/refcount.c
new file mode 100644
index 000000000..dfccc92c7
--- /dev/null
+++ b/glib/tests/refcount.c
@@ -0,0 +1,221 @@
+/* refcount.c: Tests for reference counting types
+ *
+ * Copyright 2018 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see .
+ */
+
+#include
+#include
+
+/* test_grefcount: test the behavior of the grefcount API */
+static void
+test_grefcount (void)
+{
+ grefcount a, b;
+
+ /* init(a): 1 */
+ g_ref_count_init (&a);
+ if (g_test_verbose ())
+ g_test_message ("init(a) := %d\n", (int) a);
+ g_assert_true (g_ref_count_compare (&a, 1));
+
+ /* inc(a): 2 */
+ g_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+ g_assert_false (g_ref_count_compare (&a, 1));
+ g_assert_false (g_ref_count_compare (&a, G_MAXINT));
+
+ /* b = a = 2 */
+ b = a;
+ if (g_test_verbose ())
+ g_test_message ("a := %d, b := %d\n", (int) a, (int) b);
+
+ /* inc(a): 3 */
+ g_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+
+ /* dec(b) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_false (g_ref_count_dec (&b));
+
+ /* dec(a) = 2 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_ref_count_dec (&a));
+
+ /* dec(b) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_true (g_ref_count_dec (&b));
+
+ /* dec(a) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_ref_count_dec (&a));
+
+ /* dec(a) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_true (g_ref_count_dec (&a));
+}
+
+/* test_grefcount_saturation: Saturating a grefcount counter
+ * does not cause an overflow; additionally, if we're building
+ * with checks enabled, it'll cause a warning
+ */
+static void
+test_grefcount_saturation (void)
+{
+ if (g_test_subprocess ())
+ {
+ grefcount a;
+
+ /* We're breaking abstraction here for convenience */
+ a = G_MININT + 1;
+
+ g_ref_count_inc (&a);
+ g_assert_true (a == G_MININT);
+
+ g_ref_count_inc (&a);
+ g_assert_true (a == G_MININT);
+
+ exit (0);
+ }
+
+ g_test_trap_subprocess (NULL, 0, 0);
+
+#ifndef G_DISABLE_CHECKS
+ /* Ensure that we got a warning when building with checks; the
+ * test will fail because of the critical warning being caught
+ * by GTest
+ */
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*saturation*");
+#else
+ /* With checks disabled we don't get any warning */
+ g_test_trap_assert_passed ();
+#endif
+}
+
+/* test_gatomicrefcount: test the behavior of the gatomicrefcount API */
+static void
+test_gatomicrefcount (void)
+{
+ gatomicrefcount a, b;
+
+ /* init(a): 1 */
+ g_atomic_ref_count_init (&a);
+ if (g_test_verbose ())
+ g_test_message ("init(a) := %d\n", (int) a);
+ g_assert_true (g_atomic_ref_count_compare (&a, 1));
+
+ /* inc(a): 2 */
+ g_atomic_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+ g_assert_false (g_atomic_ref_count_compare (&a, 1));
+ g_assert_false (g_atomic_ref_count_compare (&a, G_MAXINT));
+
+ /* b = a = 2 */
+ b = a;
+ if (g_test_verbose ())
+ g_test_message ("a := %d, b := %d\n", (int) a, (int) b);
+
+ /* inc(a): 3 */
+ g_atomic_ref_count_inc (&a);
+ if (g_test_verbose ())
+ g_test_message ("inc(a) := %d\n", (int) a);
+
+ /* dec(b) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_false (g_atomic_ref_count_dec (&b));
+
+ /* dec(a) = 2 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_atomic_ref_count_dec (&a));
+
+ /* dec(b) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(b) := %d + 1\n", (int) b);
+ g_assert_true (g_atomic_ref_count_dec (&b));
+
+ /* dec(a) = 1 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_false (g_atomic_ref_count_dec (&a));
+
+ /* dec(a) = 0 */
+ if (g_test_verbose ())
+ g_test_message ("dec(a) := %d + 1\n", (int) a);
+ g_assert_true (g_atomic_ref_count_dec (&a));
+}
+
+/* test_grefcount_saturation: Saturating a gatomicrefcount counter
+ * does not cause an overflow; additionally, if we're building
+ * with checks enabled, it'll cause a warning
+ */
+static void
+test_gatomicrefcount_saturation (void)
+{
+ if (g_test_subprocess ())
+ {
+ gatomicrefcount a;
+
+ /* We're breaking abstraction here for convenience */
+ a = G_MAXINT - 1;
+
+ g_atomic_ref_count_inc (&a);
+ g_assert_true (a == G_MAXINT);
+
+ g_atomic_ref_count_inc (&a);
+ g_assert_true (a == G_MAXINT);
+
+ exit (0);
+ }
+
+ g_test_trap_subprocess (NULL, 0, 0);
+
+#ifndef G_DISABLE_CHECKS
+ /* Ensure that we got a warning when building with checks; the
+ * test will fail because of the critical warning being caught
+ * by GTest
+ */
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*saturation*");
+#else
+ /* With checks disabled we don't get any warning */
+ g_test_trap_assert_passed ();
+#endif
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/refcount/grefcount", test_grefcount);
+ g_test_add_func ("/refcount/grefcount/saturation", test_grefcount_saturation);
+
+ g_test_add_func ("/refcount/gatomicrefcount", test_gatomicrefcount);
+ g_test_add_func ("/refcount/gatomicrefcount/saturation", test_gatomicrefcount_saturation);
+
+ return g_test_run ();
+}