1 /* Deallocation thread-specific data structures related to pthread_key_create.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <pthreadP.h>
19 
20 /* Deallocate POSIX thread-local-storage.  */
21 void
__nptl_deallocate_tsd(void)22 __nptl_deallocate_tsd (void)
23 {
24   struct pthread *self = THREAD_SELF;
25 
26   /* Maybe no data was ever allocated.  This happens often so we have
27      a flag for this.  */
28   if (THREAD_GETMEM (self, specific_used))
29     {
30       size_t round;
31       size_t cnt;
32 
33       round = 0;
34       do
35         {
36           size_t idx;
37 
38           /* So far no new nonzero data entry.  */
39           THREAD_SETMEM (self, specific_used, false);
40 
41           for (cnt = idx = 0; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
42             {
43               struct pthread_key_data *level2;
44 
45               level2 = THREAD_GETMEM_NC (self, specific, cnt);
46 
47               if (level2 != NULL)
48                 {
49                   size_t inner;
50 
51                   for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE;
52                        ++inner, ++idx)
53                     {
54                       void *data = level2[inner].data;
55 
56                       if (data != NULL)
57                         {
58                           /* Always clear the data.  */
59                           level2[inner].data = NULL;
60 
61                           /* Make sure the data corresponds to a valid
62                              key.  This test fails if the key was
63                              deallocated and also if it was
64                              re-allocated.  It is the user's
65                              responsibility to free the memory in this
66                              case.  */
67                           if (level2[inner].seq
68                               == __pthread_keys[idx].seq
69                               /* It is not necessary to register a destructor
70                                  function.  */
71                               && __pthread_keys[idx].destr != NULL)
72                             /* Call the user-provided destructor.  */
73                             __pthread_keys[idx].destr (data);
74                         }
75                     }
76                 }
77               else
78                 idx += PTHREAD_KEY_1STLEVEL_SIZE;
79             }
80 
81           if (THREAD_GETMEM (self, specific_used) == 0)
82             /* No data has been modified.  */
83             goto just_free;
84         }
85       /* We only repeat the process a fixed number of times.  */
86       while (__builtin_expect (++round < PTHREAD_DESTRUCTOR_ITERATIONS, 0));
87 
88       /* Just clear the memory of the first block for reuse.  */
89       memset (&THREAD_SELF->specific_1stblock, '\0',
90               sizeof (self->specific_1stblock));
91 
92     just_free:
93       /* Free the memory for the other blocks.  */
94       for (cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
95         {
96           struct pthread_key_data *level2;
97 
98           level2 = THREAD_GETMEM_NC (self, specific, cnt);
99           if (level2 != NULL)
100             {
101               /* The first block is allocated as part of the thread
102                  descriptor.  */
103               free (level2);
104               THREAD_SETMEM_NC (self, specific, cnt, NULL);
105             }
106         }
107 
108       THREAD_SETMEM (self, specific_used, false);
109     }
110 }
111 libc_hidden_def (__nptl_deallocate_tsd)
112