xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/libcollector/tsd.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1*cb63e24eSchristos /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
24f645668Schristos    Contributed by Oracle.
34f645668Schristos 
44f645668Schristos    This file is part of GNU Binutils.
54f645668Schristos 
64f645668Schristos    This program is free software; you can redistribute it and/or modify
74f645668Schristos    it under the terms of the GNU General Public License as published by
84f645668Schristos    the Free Software Foundation; either version 3, or (at your option)
94f645668Schristos    any later version.
104f645668Schristos 
114f645668Schristos    This program is distributed in the hope that it will be useful,
124f645668Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
134f645668Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144f645668Schristos    GNU General Public License for more details.
154f645668Schristos 
164f645668Schristos    You should have received a copy of the GNU General Public License
174f645668Schristos    along with this program; if not, write to the Free Software
184f645668Schristos    Foundation, 51 Franklin Street - Fifth Floor, Boston,
194f645668Schristos    MA 02110-1301, USA.  */
204f645668Schristos 
214f645668Schristos #include "config.h"
224f645668Schristos #include <pthread.h>
234f645668Schristos 
244f645668Schristos #include "collector.h"
254f645668Schristos #include "libcol_util.h"
264f645668Schristos #include "tsd.h"
274f645668Schristos #include "memmgr.h"
284f645668Schristos 
294f645668Schristos /* TprintfT(<level>,...) definitions.  Adjust per module as needed */
304f645668Schristos #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
314f645668Schristos #define DBG_LT1 1 // for configuration details, warnings
324f645668Schristos #define DBG_LT2 2
334f645668Schristos #define DBG_LT3 3
344f645668Schristos 
354f645668Schristos /*
364f645668Schristos  * Build our thread-specific-data support on pthread interfaces.
374f645668Schristos  */
384f645668Schristos #define MAXNKEYS    64  /* hard-wired? really? well, it depends only on us and we have a sense for how many keys we will use */
394f645668Schristos static pthread_key_t tsd_pkeys[MAXNKEYS];
404f645668Schristos static size_t tsd_sizes[MAXNKEYS];
414f645668Schristos static unsigned tsd_nkeys = 0;
424f645668Schristos 
434f645668Schristos int
__collector_tsd_init()444f645668Schristos __collector_tsd_init ()
454f645668Schristos {
464f645668Schristos   return 0;
474f645668Schristos }
484f645668Schristos 
494f645668Schristos void
__collector_tsd_fini()504f645668Schristos __collector_tsd_fini ()
514f645668Schristos {
524f645668Schristos   Tprintf (DBG_LT1, "tsd_fini()\n");
534f645668Schristos   while (tsd_nkeys)
544f645668Schristos     {
554f645668Schristos       tsd_nkeys--;
564f645668Schristos       pthread_key_delete (tsd_pkeys[tsd_nkeys]);
574f645668Schristos       tsd_sizes[tsd_nkeys] = 0; // should be unneeded
584f645668Schristos     }
594f645668Schristos }
604f645668Schristos 
614f645668Schristos int
__collector_tsd_allocate()624f645668Schristos __collector_tsd_allocate ()
634f645668Schristos {
644f645668Schristos   return 0;
654f645668Schristos }
664f645668Schristos 
674f645668Schristos void
__collector_tsd_release()684f645668Schristos __collector_tsd_release () { }
694f645668Schristos 
704f645668Schristos static void
tsd_destructor(void * p)714f645668Schristos tsd_destructor (void *p)
724f645668Schristos {
734f645668Schristos   if (p)
744f645668Schristos     __collector_freeCSize (__collector_heap, p, *((size_t *) p));
754f645668Schristos }
764f645668Schristos 
774f645668Schristos unsigned
__collector_tsd_create_key(size_t sz,void (* init)(void *),void (* fini)(void *))784f645668Schristos __collector_tsd_create_key (size_t sz, void (*init)(void*), void (*fini)(void*))
794f645668Schristos {
804f645668Schristos   /*
814f645668Schristos    * We no longer support init and fini arguments (and weren't using them anyhow).
824f645668Schristos    * Our hard-wired MAXNKEYS presumably is considerably higher than the number of keys we use.
834f645668Schristos    */
844f645668Schristos   if (init || fini || (tsd_nkeys >= MAXNKEYS))
854f645668Schristos     return COLLECTOR_TSD_INVALID_KEY;
864f645668Schristos 
874f645668Schristos   /*
884f645668Schristos    * A pthread key has a value that is (void *).
894f645668Schristos    * We don't know where it is stored, and can access its value only through {get|set}specific.
904f645668Schristos    * But libcollector expects a pointer to memory that it can modify.
914f645668Schristos    * So we have to allocate that memory and store the pointer.
924f645668Schristos    *
934f645668Schristos    * For now, we just have to register a destructor that will free the memory
944f645668Schristos    * when the thread finishes.
954f645668Schristos    */
964f645668Schristos   if (pthread_key_create (&tsd_pkeys[tsd_nkeys], &tsd_destructor))
974f645668Schristos    return COLLECTOR_TSD_INVALID_KEY;
984f645668Schristos   tsd_sizes[tsd_nkeys] = sz;
994f645668Schristos   tsd_nkeys++;
1004f645668Schristos   return (tsd_nkeys - 1);
1014f645668Schristos }
1024f645668Schristos 
1034f645668Schristos void *
__collector_tsd_get_by_key(unsigned key_index)1044f645668Schristos __collector_tsd_get_by_key (unsigned key_index)
1054f645668Schristos {
1064f645668Schristos   if (key_index == COLLECTOR_TSD_INVALID_KEY)
1074f645668Schristos     return NULL;
1084f645668Schristos   if (key_index < 0 || key_index >= tsd_nkeys)
1094f645668Schristos     return NULL;
1104f645668Schristos   pthread_key_t key = tsd_pkeys[key_index];
1114f645668Schristos   size_t sz = tsd_sizes[key_index];
1124f645668Schristos 
1134f645668Schristos   /*
1144f645668Schristos    * When we use __collector_freeCSize(), we need to know the
1154f645668Schristos    * size that had been allocated.  So, stick a header to the
1164f645668Schristos    * front of the allocation to hold the size.  The header could
1174f645668Schristos    * just be sizeof(size_t), but pad it to preserve alignment for
1184f645668Schristos    * the usable area.
1194f645668Schristos    */
1204f645668Schristos   size_t header = 8;
1214f645668Schristos   void *value = pthread_getspecific (key);
1224f645668Schristos 
1234f645668Schristos   // check whether we have allocated the memory
1244f645668Schristos   if (value == NULL)
1254f645668Schristos     {
1264f645668Schristos       // add room to record the size
1274f645668Schristos       value = __collector_allocCSize (__collector_heap, sz + header, 0);
1284f645668Schristos       if (value == NULL)
1294f645668Schristos 	{
1304f645668Schristos 	  // do we need to guard against trying to alloc each time?
1314f645668Schristos 	  return NULL;
1324f645668Schristos 	}
1334f645668Schristos       // write the size of the allocation
1344f645668Schristos       *((size_t *) value) = sz + header;
1354f645668Schristos       CALL_UTIL (memset)(((char *) value) + header, 0, sz);
1364f645668Schristos 
1374f645668Schristos       // record the allocation for future retrieval
1384f645668Schristos       if (pthread_setspecific (key, value))
1394f645668Schristos 	return NULL;
1404f645668Schristos     }
1414f645668Schristos   // return the pointer, skipping the header
1424f645668Schristos   return ((char *) value) +header;
1434f645668Schristos }
1444f645668Schristos 
1454f645668Schristos void
__collector_tsd_fork_child_cleanup()1464f645668Schristos __collector_tsd_fork_child_cleanup ()
1474f645668Schristos {
1484f645668Schristos   __collector_tsd_fini ();
1494f645668Schristos }
150