1/* Test the allocate_once function.
2 Copyright (C) 2018-2022 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. */
26static void *
27allocate_string (void *closure)
28{
29 return xstrdup (closure);
30}
31
32/* Allocation and deallocation functions which are not expected to be
33 called. */
34
35static void *
36allocate_not_called (void *closure)
37{
38 FAIL_EXIT1 ("allocation function called unexpectedly (%p)", closure);
39}
40
41static void
42deallocate_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. */
49static int function_called;
50
51/* An allocation function which returns NULL and records that it has
52 been called. */
53static void *
54allocate_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. */
65static void *fake_race_place;
66static char fake_race_region[3]; /* To obtain unique addresses. */
67
68static void *
69fake_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
79static void
80fake_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. */
94static void *
95fake_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
105static int
106do_test (void)
107{
108 mtrace ();
109
110 /* Simple allocation. */
111 void *place1 = NULL;
112 char *string1 = allocate_once (place: &place1, allocate: allocate_string,
113 deallocate: deallocate_not_called,
114 closure: (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 (place: &place2, allocate: allocate_string,
127 deallocate: deallocate_not_called,
128 closure: (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 (place: &place3, allocate: allocate_return_null,
137 deallocate: 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 (ptr: place2);
176 free (ptr: place1);
177
178 return 0;
179}
180
181#include <support/test-driver.c>
182

source code of glibc/misc/tst-allocate_once.c