1 /* Copyright (C) 2002-2022 Free Software Foundation, Inc.
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 <errno.h>
19 #include <stdlib.h>
20 #include "pthreadP.h"
21 #include <shlib-compat.h>
22 
23 int
___pthread_setspecific(pthread_key_t key,const void * value)24 ___pthread_setspecific (pthread_key_t key, const void *value)
25 {
26   struct pthread *self;
27   unsigned int idx1st;
28   unsigned int idx2nd;
29   struct pthread_key_data *level2;
30   unsigned int seq;
31 
32   self = THREAD_SELF;
33 
34   /* Special case access to the first 2nd-level block.  This is the
35      usual case.  */
36   if (__glibc_likely (key < PTHREAD_KEY_2NDLEVEL_SIZE))
37     {
38       /* Verify the key is sane.  */
39       if (KEY_UNUSED ((seq = __pthread_keys[key].seq)))
40 	/* Not valid.  */
41 	return EINVAL;
42 
43       level2 = &self->specific_1stblock[key];
44 
45       /* Remember that we stored at least one set of data.  */
46       if (value != NULL)
47 	THREAD_SETMEM (self, specific_used, true);
48     }
49   else
50     {
51       if (key >= PTHREAD_KEYS_MAX
52 	  || KEY_UNUSED ((seq = __pthread_keys[key].seq)))
53 	/* Not valid.  */
54 	return EINVAL;
55 
56       idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
57       idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
58 
59       /* This is the second level array.  Allocate it if necessary.  */
60       level2 = THREAD_GETMEM_NC (self, specific, idx1st);
61       if (level2 == NULL)
62 	{
63 	  if (value == NULL)
64 	    /* We don't have to do anything.  The value would in any case
65 	       be NULL.  We can save the memory allocation.  */
66 	    return 0;
67 
68 	  level2
69 	    = (struct pthread_key_data *) calloc (PTHREAD_KEY_2NDLEVEL_SIZE,
70 						  sizeof (*level2));
71 	  if (level2 == NULL)
72 	    return ENOMEM;
73 
74 	  THREAD_SETMEM_NC (self, specific, idx1st, level2);
75 	}
76 
77       /* Pointer to the right array element.  */
78       level2 = &level2[idx2nd];
79 
80       /* Remember that we stored at least one set of data.  */
81       THREAD_SETMEM (self, specific_used, true);
82     }
83 
84   /* Store the data and the sequence number so that we can recognize
85      stale data.  */
86   level2->seq = seq;
87   level2->data = (void *) value;
88 
89   return 0;
90 }
91 versioned_symbol (libc, ___pthread_setspecific, pthread_setspecific,
92 		  GLIBC_2_34);
93 libc_hidden_ver (___pthread_setspecific, __pthread_setspecific)
94 #ifndef SHARED
95 strong_alias (___pthread_setspecific, __pthread_setspecific)
96 #endif
97 
98 #if OTHER_SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_34)
99 compat_symbol (libpthread, ___pthread_setspecific, __pthread_setspecific,
100 	       GLIBC_2_0);
101 compat_symbol (libpthread, ___pthread_setspecific, pthread_setspecific,
102 	       GLIBC_2_0);
103 #endif
104