150 lines
4.1 KiB
C
150 lines
4.1 KiB
C
|
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
||
|
Contributed by Oracle.
|
||
|
|
||
|
This file is part of GNU Binutils.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3, or (at your option)
|
||
|
any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
||
|
MA 02110-1301, USA. */
|
||
|
|
||
|
#include "config.h"
|
||
|
#include <pthread.h>
|
||
|
|
||
|
#include "collector.h"
|
||
|
#include "libcol_util.h"
|
||
|
#include "tsd.h"
|
||
|
#include "memmgr.h"
|
||
|
|
||
|
/* TprintfT(<level>,...) definitions. Adjust per module as needed */
|
||
|
#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
|
||
|
#define DBG_LT1 1 // for configuration details, warnings
|
||
|
#define DBG_LT2 2
|
||
|
#define DBG_LT3 3
|
||
|
|
||
|
/*
|
||
|
* Build our thread-specific-data support on pthread interfaces.
|
||
|
*/
|
||
|
#define MAXNKEYS 64 /* hard-wired? really? well, it depends only on us and we have a sense for how many keys we will use */
|
||
|
static pthread_key_t tsd_pkeys[MAXNKEYS];
|
||
|
static size_t tsd_sizes[MAXNKEYS];
|
||
|
static unsigned tsd_nkeys = 0;
|
||
|
|
||
|
int
|
||
|
__collector_tsd_init ()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
__collector_tsd_fini ()
|
||
|
{
|
||
|
Tprintf (DBG_LT1, "tsd_fini()\n");
|
||
|
while (tsd_nkeys)
|
||
|
{
|
||
|
tsd_nkeys--;
|
||
|
pthread_key_delete (tsd_pkeys[tsd_nkeys]);
|
||
|
tsd_sizes[tsd_nkeys] = 0; // should be unneeded
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
__collector_tsd_allocate ()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
__collector_tsd_release () { }
|
||
|
|
||
|
static void
|
||
|
tsd_destructor (void *p)
|
||
|
{
|
||
|
if (p)
|
||
|
__collector_freeCSize (__collector_heap, p, *((size_t *) p));
|
||
|
}
|
||
|
|
||
|
unsigned
|
||
|
__collector_tsd_create_key (size_t sz, void (*init)(void*), void (*fini)(void*))
|
||
|
{
|
||
|
/*
|
||
|
* We no longer support init and fini arguments (and weren't using them anyhow).
|
||
|
* Our hard-wired MAXNKEYS presumably is considerably higher than the number of keys we use.
|
||
|
*/
|
||
|
if (init || fini || (tsd_nkeys >= MAXNKEYS))
|
||
|
return COLLECTOR_TSD_INVALID_KEY;
|
||
|
|
||
|
/*
|
||
|
* A pthread key has a value that is (void *).
|
||
|
* We don't know where it is stored, and can access its value only through {get|set}specific.
|
||
|
* But libcollector expects a pointer to memory that it can modify.
|
||
|
* So we have to allocate that memory and store the pointer.
|
||
|
*
|
||
|
* For now, we just have to register a destructor that will free the memory
|
||
|
* when the thread finishes.
|
||
|
*/
|
||
|
if (pthread_key_create (&tsd_pkeys[tsd_nkeys], &tsd_destructor))
|
||
|
return COLLECTOR_TSD_INVALID_KEY;
|
||
|
tsd_sizes[tsd_nkeys] = sz;
|
||
|
tsd_nkeys++;
|
||
|
return (tsd_nkeys - 1);
|
||
|
}
|
||
|
|
||
|
void *
|
||
|
__collector_tsd_get_by_key (unsigned key_index)
|
||
|
{
|
||
|
if (key_index == COLLECTOR_TSD_INVALID_KEY)
|
||
|
return NULL;
|
||
|
if (key_index < 0 || key_index >= tsd_nkeys)
|
||
|
return NULL;
|
||
|
pthread_key_t key = tsd_pkeys[key_index];
|
||
|
size_t sz = tsd_sizes[key_index];
|
||
|
|
||
|
/*
|
||
|
* When we use __collector_freeCSize(), we need to know the
|
||
|
* size that had been allocated. So, stick a header to the
|
||
|
* front of the allocation to hold the size. The header could
|
||
|
* just be sizeof(size_t), but pad it to preserve alignment for
|
||
|
* the usable area.
|
||
|
*/
|
||
|
size_t header = 8;
|
||
|
void *value = pthread_getspecific (key);
|
||
|
|
||
|
// check whether we have allocated the memory
|
||
|
if (value == NULL)
|
||
|
{
|
||
|
// add room to record the size
|
||
|
value = __collector_allocCSize (__collector_heap, sz + header, 0);
|
||
|
if (value == NULL)
|
||
|
{
|
||
|
// do we need to guard against trying to alloc each time?
|
||
|
return NULL;
|
||
|
}
|
||
|
// write the size of the allocation
|
||
|
*((size_t *) value) = sz + header;
|
||
|
CALL_UTIL (memset)(((char *) value) + header, 0, sz);
|
||
|
|
||
|
// record the allocation for future retrieval
|
||
|
if (pthread_setspecific (key, value))
|
||
|
return NULL;
|
||
|
}
|
||
|
// return the pointer, skipping the header
|
||
|
return ((char *) value) +header;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
__collector_tsd_fork_child_cleanup ()
|
||
|
{
|
||
|
__collector_tsd_fini ();
|
||
|
}
|