|
@@ -3,8 +3,7 @@
|
|
|
#include <stdarg.h>
|
|
|
#include "pthread_impl.h"
|
|
|
#include "dynlink.h"
|
|
|
-#include "lock.h"
|
|
|
-#include "fork_impl.h"
|
|
|
+#include "atomic.h"
|
|
|
|
|
|
#define malloc __libc_malloc
|
|
|
#define calloc __libc_calloc
|
|
@@ -23,28 +22,31 @@ char *dlerror()
|
|
|
return s;
|
|
|
}
|
|
|
|
|
|
-static volatile int freebuf_queue_lock[1];
|
|
|
-static void **freebuf_queue;
|
|
|
-volatile int *const __dlerror_lockptr = freebuf_queue_lock;
|
|
|
+/* 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;
|