summaryrefslogtreecommitdiff
path: root/gprofng/libcollector/tsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'gprofng/libcollector/tsd.c')
-rw-r--r--gprofng/libcollector/tsd.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/gprofng/libcollector/tsd.c b/gprofng/libcollector/tsd.c
new file mode 100644
index 00000000000..416c3e76778
--- /dev/null
+++ b/gprofng/libcollector/tsd.c
@@ -0,0 +1,149 @@
+/* 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 ();
+}