(root)/
glibc-2.38/
misc/
tst-allocate_once.c
       1  /* Test the allocate_once function.
       2     Copyright (C) 2018-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <allocate_once.h>
      20  #include <mcheck.h>
      21  #include <string.h>
      22  #include <support/check.h>
      23  #include <support/support.h>
      24  
      25  /* Allocate a new string.  */
      26  static void *
      27  allocate_string (void *closure)
      28  {
      29    return xstrdup (closure);
      30  }
      31  
      32  /* Allocation and deallocation functions which are not expected to be
      33     called.  */
      34  
      35  static void *
      36  allocate_not_called (void *closure)
      37  {
      38    FAIL_EXIT1 ("allocation function called unexpectedly (%p)", closure);
      39  }
      40  
      41  static void
      42  deallocate_not_called (void *closure, void *ptr)
      43  {
      44    FAIL_EXIT1 ("deallocate function called unexpectedly (%p, %p)",
      45                closure, ptr);
      46  }
      47  
      48  /* Counter for various function calls.  */
      49  static int function_called;
      50  
      51  /* An allocation function which returns NULL and records that it has
      52     been called.  */
      53  static void *
      54  allocate_return_null (void *closure)
      55  {
      56    /* The function should only be called once.  */
      57    TEST_COMPARE (function_called, 0);
      58    ++function_called;
      59    return NULL;
      60  }
      61  
      62  
      63  /* The following is used to check the retry logic, by causing a fake
      64     race condition.  */
      65  static void *fake_race_place;
      66  static char fake_race_region[3]; /* To obtain unique addresses.  */
      67  
      68  static void *
      69  fake_race_allocate (void *closure)
      70  {
      71    TEST_VERIFY (closure == &fake_race_region[0]);
      72    TEST_COMPARE (function_called, 0);
      73    ++function_called;
      74    /* Fake allocation by another thread.  */
      75    fake_race_place = &fake_race_region[1];
      76    return &fake_race_region[2];
      77  }
      78  
      79  static void
      80  fake_race_deallocate (void *closure, void *ptr)
      81  {
      82    /* Check that the pointer returned from fake_race_allocate is
      83       deallocated (and not the one stored in fake_race_place).  */
      84    TEST_VERIFY (ptr == &fake_race_region[2]);
      85  
      86    TEST_VERIFY (fake_race_place == &fake_race_region[1]);
      87    TEST_VERIFY (closure == &fake_race_region[0]);
      88    TEST_COMPARE (function_called, 1);
      89    ++function_called;
      90  }
      91  
      92  /* Similar to fake_race_allocate, but expects to be paired with free
      93     as the deallocation function.  */
      94  static void *
      95  fake_race_allocate_for_free (void *closure)
      96  {
      97    TEST_VERIFY (closure == &fake_race_region[0]);
      98    TEST_COMPARE (function_called, 0);
      99    ++function_called;
     100    /* Fake allocation by another thread.  */
     101    fake_race_place = &fake_race_region[1];
     102    return xstrdup ("to be freed");
     103  }
     104  
     105  static int
     106  do_test (void)
     107  {
     108    mtrace ();
     109  
     110    /* Simple allocation.  */
     111    void *place1 = NULL;
     112    char *string1 = allocate_once (&place1, allocate_string,
     113                                     deallocate_not_called,
     114                                     (char *) "test string 1");
     115    TEST_VERIFY_EXIT (string1 != NULL);
     116    TEST_VERIFY (strcmp ("test string 1", string1) == 0);
     117    /* Second call returns the first pointer, without calling any
     118       callbacks.  */
     119    TEST_VERIFY (string1
     120                 == allocate_once (&place1, allocate_not_called,
     121                                   deallocate_not_called,
     122                                   (char *) "test string 1a"));
     123  
     124    /* Different place should result in another call.  */
     125    void *place2 = NULL;
     126    char *string2 = allocate_once (&place2, allocate_string,
     127                                   deallocate_not_called,
     128                                   (char *) "test string 2");
     129    TEST_VERIFY_EXIT (string2 != NULL);
     130    TEST_VERIFY (strcmp ("test string 2", string2) == 0);
     131    TEST_VERIFY (string1 != string2);
     132  
     133    /* Check error reporting (NULL return value from the allocation
     134       function).  */
     135    void *place3 = NULL;
     136    char *string3 = allocate_once (&place3, allocate_return_null,
     137                                   deallocate_not_called, NULL);
     138    TEST_VERIFY (string3 == NULL);
     139    TEST_COMPARE (function_called, 1);
     140  
     141    /* Check that the deallocation function is called if the race is
     142       lost.  */
     143    function_called = 0;
     144    TEST_VERIFY (allocate_once (&fake_race_place,
     145                                fake_race_allocate,
     146                                fake_race_deallocate,
     147                                &fake_race_region[0])
     148                 == &fake_race_region[1]);
     149    TEST_COMPARE (function_called, 2);
     150    function_called = 3;
     151    TEST_VERIFY (allocate_once (&fake_race_place,
     152                                fake_race_allocate,
     153                                fake_race_deallocate,
     154                                &fake_race_region[0])
     155                 == &fake_race_region[1]);
     156    TEST_COMPARE (function_called, 3);
     157  
     158    /* Similar, but this time rely on that free is called.  */
     159    function_called = 0;
     160    fake_race_place = NULL;
     161    TEST_VERIFY (allocate_once (&fake_race_place,
     162                                  fake_race_allocate_for_free,
     163                                  NULL,
     164                                  &fake_race_region[0])
     165                 == &fake_race_region[1]);
     166    TEST_COMPARE (function_called, 1);
     167    function_called = 3;
     168    TEST_VERIFY (allocate_once (&fake_race_place,
     169                                fake_race_allocate_for_free,
     170                                NULL,
     171                                &fake_race_region[0])
     172                 == &fake_race_region[1]);
     173    TEST_COMPARE (function_called, 3);
     174  
     175    free (place2);
     176    free (place1);
     177  
     178    return 0;
     179  }
     180  
     181  #include <support/test-driver.c>