#include <stdarg.h>
#include "pthread_impl.h"
#include "dynlink.h"
-#include "lock.h"
+#include "atomic.h"
#define malloc __libc_malloc
#define calloc __libc_calloc
return s;
}
-static volatile int freebuf_queue_lock[1];
-static void **freebuf_queue;
+/* Atomic singly-linked list, used to store list of thread-local dlerror
+ * buffers for deferred free. They cannot be freed at thread exit time
+ * because, by the time it's known they can be freed, the exiting thread
+ * is in a highly restrictive context where it cannot call (even the
+ * libc-internal) free. It also can't take locks; thus the atomic list. */
+
+static void *volatile freebuf_queue;
void __dl_thread_cleanup(void)
{
pthread_t self = __pthread_self();
- if (self->dlerror_buf && self->dlerror_buf != (void *)-1) {
- LOCK(freebuf_queue_lock);
- void **p = (void **)self->dlerror_buf;
- *p = freebuf_queue;
- freebuf_queue = p;
- UNLOCK(freebuf_queue_lock);
- }
+ if (!self->dlerror_buf || self->dlerror_buf == (void *)-1)
+ return;
+ void *h;
+ do {
+ h = freebuf_queue;
+ *(void **)self->dlerror_buf = h;
+ } while (a_cas_p(&freebuf_queue, h, self->dlerror_buf) != h);
}
hidden void __dl_vseterr(const char *fmt, va_list ap)
{
- LOCK(freebuf_queue_lock);
- void **q = freebuf_queue;
- freebuf_queue = 0;
- UNLOCK(freebuf_queue_lock);
+ void **q;
+ do q = freebuf_queue;
+ while (q && a_cas_p(&freebuf_queue, q, 0) != q);
while (q) {
void **p = *q;