(root)/
glib-2.79.0/
glib/
tests/
rcbox.c
       1  /* rcbox.c: Reference counted data
       2   *
       3   * Copyright 2018  Emmanuele Bassi
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General Public
      18   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   */
      20  
      21  #include <glib.h>
      22  
      23  typedef struct {
      24    float x, y;
      25  } Point;
      26  
      27  static Point *global_point;
      28  
      29  /* test_rcbox_new: Test g_rc_box_new() */
      30  static void
      31  test_rcbox_new (void)
      32  {
      33    Point *a = g_rc_box_new (Point);
      34  
      35    g_assert_nonnull (a);
      36    g_assert_cmpuint (g_rc_box_get_size (a), ==, sizeof (Point));
      37  
      38    g_rc_box_release (a);
      39  
      40    a = g_rc_box_new0 (Point);
      41    g_assert_nonnull (a);
      42    g_assert_cmpfloat (a->x, ==, 0.f);
      43    g_assert_cmpfloat (a->y, ==, 0.f);
      44  
      45    g_rc_box_release (a);
      46  }
      47  
      48  /* test_atomic_rcbox_new: Test g_atomic_rc_box_new() */
      49  static void
      50  test_atomic_rcbox_new (void)
      51  {
      52    Point *a = g_atomic_rc_box_new (Point);
      53  
      54    g_assert_nonnull (a);
      55    g_assert_cmpuint (g_atomic_rc_box_get_size (a), ==, sizeof (Point));
      56  
      57    g_atomic_rc_box_release (a);
      58  
      59    a = g_atomic_rc_box_new0 (Point);
      60    g_assert_nonnull (a);
      61    g_assert_cmpfloat (a->x, ==, 0.f);
      62    g_assert_cmpfloat (a->y, ==, 0.f);
      63  
      64    g_atomic_rc_box_release (a);
      65  }
      66  
      67  static void
      68  point_clear (Point *p)
      69  {
      70    g_assert_nonnull (p);
      71    g_assert_true (global_point == p);
      72  
      73    g_assert_cmpfloat (p->x, ==, 42.0f);
      74    g_assert_cmpfloat (p->y, ==, 47.0f);
      75  
      76    g_test_message ("global_point = %p", p);
      77    global_point = NULL;
      78  }
      79  
      80  /* test_rcbox_release_full: Verify that g_rc_box_release_full() calls
      81   * the clear function only when the last reference is released
      82   */
      83  static void
      84  test_rcbox_release_full (void)
      85  {
      86    Point *p = g_rc_box_new (Point);
      87  
      88    g_assert_nonnull (p);
      89    global_point = p;
      90  
      91    p->x = 42.0f;
      92    p->y = 47.0f;
      93  
      94    g_assert_true (g_rc_box_acquire (p) == p);
      95  
      96    g_rc_box_release_full (p, (GDestroyNotify) point_clear);
      97    g_assert_nonnull (global_point);
      98    g_assert_true (p == global_point);
      99  
     100    g_rc_box_release_full (p, (GDestroyNotify) point_clear);
     101    g_assert_null (global_point);
     102  }
     103  
     104  /* test_atomic_rcbox_release_full: Verify that g_atomic_rc_box_release_full()
     105   * calls the clear function only when the last reference is released
     106   */
     107  static void
     108  test_atomic_rcbox_release_full (void)
     109  {
     110    Point *p = g_atomic_rc_box_new (Point);
     111  
     112    g_assert_nonnull (p);
     113    global_point = p;
     114  
     115    p->x = 42.0f;
     116    p->y = 47.0f;
     117  
     118    g_assert_true (g_atomic_rc_box_acquire (p) == p);
     119  
     120    g_atomic_rc_box_release_full (p, (GDestroyNotify) point_clear);
     121    g_assert_nonnull (global_point);
     122    g_assert_true (p == global_point);
     123  
     124    g_atomic_rc_box_release_full (p, (GDestroyNotify) point_clear);
     125    g_assert_null (global_point);
     126  }
     127  
     128  static Point *global_point_a;
     129  static Point *global_point_b;
     130  
     131  static void
     132  point_clear_dup_a (Point *a)
     133  {
     134    g_assert_true (a == global_point_a);
     135  
     136    g_test_message ("global_point_a = %p", a);
     137    global_point_a = NULL;
     138  }
     139  
     140  static void
     141  point_clear_dup_b (Point *b)
     142  {
     143    g_assert_true (b == global_point_b);
     144  
     145    g_test_message ("global_point_b = %p", b);
     146    global_point_b = NULL;
     147  }
     148  
     149  /* test_rcbox_dup: Verify that g_rc_box_dup() copies only the
     150   * data and does not change the reference count of the original
     151   */
     152  static void
     153  test_rcbox_dup (void)
     154  {
     155    Point *a, *b;
     156  
     157    a = g_rc_box_new (Point);
     158    a->x = 10.f;
     159    a->y = 5.f;
     160  
     161    b = g_rc_box_dup (sizeof (Point), a);
     162    g_assert_true (a != b);
     163    g_assert_cmpfloat (a->x, ==, b->x);
     164    g_assert_cmpfloat (a->y, ==, b->y);
     165  
     166    global_point_a = a;
     167    global_point_b = b;
     168  
     169    a->x = 1.f;
     170    a->y = 1.f;
     171    g_assert_cmpfloat (a->x, !=, b->x);
     172    g_assert_cmpfloat (a->y, !=, b->y);
     173  
     174    b->x = 5.f;
     175    b->y = 10.f;
     176    g_assert_cmpfloat (a->x, !=, b->x);
     177    g_assert_cmpfloat (a->y, !=, b->y);
     178  
     179    g_rc_box_release_full (a, (GDestroyNotify) point_clear_dup_a);
     180    g_assert_null (global_point_a);
     181    g_assert_nonnull (global_point_b);
     182  
     183    g_rc_box_release_full (b, (GDestroyNotify) point_clear_dup_b);
     184    g_assert_null (global_point_b);
     185  }
     186  
     187  /* test_atomic_rcbox_dup: Verify that g_atomic_rc_box_dup() copies
     188   * only the data and does not change the reference count of the original
     189   */
     190  static void
     191  test_atomic_rcbox_dup (void)
     192  {
     193    Point *a, *b;
     194  
     195    a = g_atomic_rc_box_new (Point);
     196    a->x = 10.f;
     197    a->y = 5.f;
     198  
     199    b = g_atomic_rc_box_dup (sizeof (Point), a);
     200    g_assert_true (a != b);
     201    g_assert_cmpfloat (a->x, ==, b->x);
     202    g_assert_cmpfloat (a->y, ==, b->y);
     203  
     204    global_point_a = a;
     205    global_point_b = b;
     206  
     207    a->x = 1.f;
     208    a->y = 1.f;
     209    g_assert_cmpfloat (a->x, !=, b->x);
     210    g_assert_cmpfloat (a->y, !=, b->y);
     211  
     212    b->x = 5.f;
     213    b->y = 10.f;
     214    g_assert_cmpfloat (a->x, !=, b->x);
     215    g_assert_cmpfloat (a->y, !=, b->y);
     216  
     217    g_atomic_rc_box_release_full (a, (GDestroyNotify) point_clear_dup_a);
     218    g_assert_null (global_point_a);
     219    g_assert_nonnull (global_point_b);
     220  
     221    g_atomic_rc_box_release_full (b, (GDestroyNotify) point_clear_dup_b);
     222    g_assert_null (global_point_b);
     223  }
     224  
     225  /* The expected alignment of the refcounted data, absent any other
     226   * alignment requirement, is `2 * sizeof(void*)`; GLib only really
     227   * supports void* sized 8 or 4 (see the comment in gatomic.h)
     228   */
     229  #if GLIB_SIZEOF_VOID_P == 8
     230  static const gsize rcbox_alignment = 16;
     231  #else
     232  static const gsize rcbox_alignment = 8;
     233  #endif
     234  
     235  /* verify that the refcounted allocation is properly aligned */
     236  static void
     237  test_rcbox_alignment (void)
     238  {
     239    const gsize block_sizes[] = {
     240      1,
     241      2,
     242      4,
     243      sizeof (gint32) * 3,
     244    };
     245  
     246    gsize i;
     247  
     248    for (i = 0; i < G_N_ELEMENTS (block_sizes); i++)
     249      {
     250        gpointer p = g_rc_box_alloc0 (block_sizes[i]);
     251  
     252        g_assert_nonnull (p);
     253        g_assert_true (((guintptr) p & (rcbox_alignment - 1)) == 0);
     254  
     255        g_rc_box_release (p);
     256      }
     257  }
     258  
     259  /* verify that the atomically refcounted allocation is properly aligned */
     260  static void
     261  test_atomic_rcbox_alignment (void)
     262  {
     263    const gsize block_sizes[] = {
     264      1,
     265      2,
     266      4,
     267      sizeof (gint32) * 3,
     268    };
     269  
     270    gsize i;
     271  
     272    for (i = 0; i < G_N_ELEMENTS (block_sizes); i++)
     273      {
     274        gpointer p = g_atomic_rc_box_alloc0 (block_sizes[i]);
     275  
     276        g_assert_nonnull (p);
     277        g_assert_true (((guintptr) p & (rcbox_alignment - 1)) == 0);
     278  
     279        g_atomic_rc_box_release (p);
     280      }
     281  }
     282  
     283  int
     284  main (int   argc,
     285        char *argv[])
     286  {
     287    g_test_init (&argc, &argv, NULL);
     288  
     289    g_test_add_func ("/rcbox/new", test_rcbox_new);
     290    g_test_add_func ("/rcbox/release-full", test_rcbox_release_full);
     291    g_test_add_func ("/rcbox/dup", test_rcbox_dup);
     292    g_test_add_func ("/rcbox/alignment", test_rcbox_alignment);
     293  
     294    g_test_add_func ("/atomic-rcbox/new", test_atomic_rcbox_new);
     295    g_test_add_func ("/atomic-rcbox/release-full", test_atomic_rcbox_release_full);
     296    g_test_add_func ("/atomic-rcbox/dup", test_atomic_rcbox_dup);
     297    g_test_add_func ("/atomic-rcbox/alignment", test_atomic_rcbox_alignment);
     298  
     299    return g_test_run ();
     300  }