summaryrefslogtreecommitdiff
path: root/gprofng/libcollector/tsd.c
blob: bbd405766df07575a6e410a5aacefc1c059965c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* Copyright (C) 2021-2023 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 ();
}