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 (); +}