(root)/
glibc-2.38/
nptl/
pthread_setspecific.c
       1  /* Copyright (C) 2002-2023 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
      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