Example #1
0
void
_thr_free(struct pthread *curthread, struct pthread *thread)
{
	DBG_MSG("Freeing thread %p\n", thread);
	if (thread->name) {
		free(thread->name);
		thread->name = NULL;
	}
	/*
	 * Always free tcb, as we only know it is part of RTLD TLS
	 * block, but don't know its detail and can not assume how
	 * it works, so better to avoid caching it here.
	 */
	if (curthread != NULL) {
		THR_LOCK_ACQUIRE(curthread, &tcb_lock);
		_tcb_dtor(thread->tcb);
		THR_LOCK_RELEASE(curthread, &tcb_lock);
	} else {
		_tcb_dtor(thread->tcb);
	}
	thread->tcb = NULL;
	if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) {
		thr_destroy(curthread, thread);
	} else {
		/*
		 * Add the thread to the free thread list, this also avoids
		 * pthread id is reused too quickly, may help some buggy apps.
		 */
		THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
		TAILQ_INSERT_TAIL(&free_threadq, thread, tle);
		free_thread_count++;
		THR_LOCK_RELEASE(curthread, &free_thread_lock);
	}
}
Example #2
0
int
_pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
{
	struct pthread *curthread;
	int i;

	if (_thr_initial == NULL)
		_libpthread_init(NULL);
	curthread = _get_curthread();

	/* Lock the key table: */
	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {

		if (_thread_keytable[i].allocated == 0) {
			_thread_keytable[i].allocated = 1;
			_thread_keytable[i].destructor = destructor;
			_thread_keytable[i].seqno++;

			/* Unlock the key table: */
			THR_LOCK_RELEASE(curthread, &_keytable_lock);
			*key = i;
			return (0);
		}

	}
	/* Unlock the key table: */
	THR_LOCK_RELEASE(curthread, &_keytable_lock);
	return (EAGAIN);
}
Example #3
0
void 
_thread_cleanupspecific(void)
{
	struct pthread	*curthread = _get_curthread();
	const_key_destructor_t destructor;
	const void	*data = NULL;
	int		key;
	int		i;

	if (curthread->specific == NULL)
		return;

	/* Lock the key table: */
	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
	for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) &&
	    (curthread->specific_data_count > 0); i++) {
		for (key = 0; (key < PTHREAD_KEYS_MAX) &&
		    (curthread->specific_data_count > 0); key++) {
			destructor = NULL;

			if (_thread_keytable[key].allocated &&
			    (curthread->specific[key].data != NULL)) {
				if (curthread->specific[key].seqno ==
				    _thread_keytable[key].seqno) {
					data = curthread->specific[key].data;
					destructor = (const_key_destructor_t)
					    _thread_keytable[key].destructor;
				}
				curthread->specific[key].data = NULL;
				curthread->specific_data_count--;
			}

			/*
			 * If there is a destructore, call it
			 * with the key table entry unlocked:
			 */
			if (destructor != NULL) {
				/*
				 * Don't hold the lock while calling the
				 * destructor:
				 */
				THR_LOCK_RELEASE(curthread, &_keytable_lock);
				destructor(data);
				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
			}
		}
	}
	THR_LOCK_RELEASE(curthread, &_keytable_lock);
	free(curthread->specific);
	curthread->specific = NULL;
	if (curthread->specific_data_count > 0)
		stderr_debug("Thread %p has exited with leftover "
		    "thread-specific data after %d destructor iterations\n",
		    curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
}
Example #4
0
static void
cond_wait_backout(void *arg)
{
	struct pthread *curthread = (struct pthread *)arg;
	pthread_cond_t	cond;

	cond = curthread->data.cond;
	if (cond != NULL) {
		/* Lock the condition variable structure: */
		THR_LOCK_ACQUIRE(curthread, &cond->c_lock);

		/* Process according to condition variable type: */
		switch (cond->c_type) {
		/* Fast condition variable: */
		case COND_TYPE_FAST:
			cond_queue_remove(cond, curthread);

			/* Check for no more waiters: */
			if (TAILQ_EMPTY(&cond->c_queue))
				cond->c_mutex = NULL;
			break;

		default:
			break;
		}

		/* Unlock the condition variable structure: */
		THR_LOCK_RELEASE(curthread, &cond->c_lock);
	}
	/* No need to call this again. */
	curthread->sigbackout = NULL;
}
Example #5
0
/*
 * Allocate wake-address, the memory area is never freed after
 * allocated, this becauses threads may be referencing it.
 */
struct wake_addr *
_thr_alloc_wake_addr(void)
{
	struct pthread *curthread;
	struct wake_addr *p;

	if (_thr_initial == NULL) {
		return &default_wake_addr;
	}

	curthread = _get_curthread();

	THR_LOCK_ACQUIRE(curthread, &addr_lock);
	if (wake_addr_head == NULL) {
		unsigned i;
		unsigned pagesize = getpagesize();
		struct wake_addr *pp = (struct wake_addr *)
			mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
			MAP_ANON|MAP_PRIVATE, -1, 0);
		for (i = 1; i < pagesize/sizeof(struct wake_addr); ++i)
			pp[i].link = &pp[i+1];
		pp[i-1].link = NULL;	
		wake_addr_head = &pp[1];
		p = &pp[0];
	} else {
		p = wake_addr_head;
		wake_addr_head = p->link;
	}
	THR_LOCK_RELEASE(curthread, &addr_lock);
	p->value = 0;
	return (p);
}
Example #6
0
int
_pthread_mutex_destroy(pthread_mutex_t *mutex)
{
	struct pthread	*curthread = _get_curthread();
	pthread_mutex_t m;
	int ret = 0;

	if (mutex == NULL || *mutex == NULL)
		ret = EINVAL;
	else {
		/* Lock the mutex structure: */
		THR_LOCK_ACQUIRE(curthread, &(*mutex)->m_lock);

		/*
		 * Check to see if this mutex is in use:
		 */
		if (((*mutex)->m_owner != NULL) ||
		    (!TAILQ_EMPTY(&(*mutex)->m_queue)) ||
		    ((*mutex)->m_refcount != 0)) {
			ret = EBUSY;

			/* Unlock the mutex structure: */
			THR_LOCK_RELEASE(curthread, &(*mutex)->m_lock);
		} else {
			/*
			 * Save a pointer to the mutex so it can be free'd
			 * and set the caller's pointer to NULL:
			 */
			m = *mutex;
			*mutex = NULL;

			/* Unlock the mutex structure: */
			THR_LOCK_RELEASE(curthread, &m->m_lock);

			/*
			 * Free the memory allocated for the mutex
			 * structure:
			 */
			MUTEX_ASSERT_NOT_OWNED(m);
			MUTEX_DESTROY(m);
		}
	}

	/* Return the completion status: */
	return (ret);
}
Example #7
0
void
_thr_release_wake_addr(struct wake_addr *wa)
{
	struct pthread *curthread = _get_curthread();

	if (wa == &default_wake_addr)
		return;
	THR_LOCK_ACQUIRE(curthread, &addr_lock);
	wa->link = wake_addr_head;
	wake_addr_head = wa;
	THR_LOCK_RELEASE(curthread, &addr_lock);
}
Example #8
0
struct pthread *
_thr_alloc(struct pthread *curthread)
{
	struct pthread	*thread = NULL;
	struct tls_tcb	*tcb;

	if (curthread != NULL) {
		if (GC_NEEDED())
			_thr_gc(curthread);
		if (free_thread_count > 0) {
			THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
			if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) {
				TAILQ_REMOVE(&free_threadq, thread, tle);
				free_thread_count--;
			}
			THR_LOCK_RELEASE(curthread, &free_thread_lock);
		}
	}
	if (thread == NULL) {
		thread = malloc(sizeof(struct pthread));
		if (thread == NULL)
			return (NULL);
	}
	if (curthread != NULL) {
		THR_LOCK_ACQUIRE(curthread, &tcb_lock);
		tcb = _tcb_ctor(thread, 0 /* not initial tls */);
		THR_LOCK_RELEASE(curthread, &tcb_lock);
	} else {
		tcb = _tcb_ctor(thread, 1 /* initial tls */);
	}
	if (tcb != NULL) {
		memset(thread, 0, sizeof(*thread));
		thread->tcb = tcb;
	} else {
		thr_destroy(curthread, thread);
		thread = NULL;
	}
	return (thread);
}
Example #9
0
static int
init_static_private(struct pthread *thread, pthread_mutex_t *mutex)
{
	int ret;

	THR_LOCK_ACQUIRE(thread, &_mutex_static_lock);

	if (*mutex == NULL)
		ret = _pthread_mutex_init(mutex, &static_mattr);
	else
		ret = 0;

	THR_LOCK_RELEASE(thread, &_mutex_static_lock);

	return (ret);
}
Example #10
0
static int
init_static(pthread_rwlock_t *rwlock)
{
	struct pthread *thread = _get_curthread();
	int ret;

	THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);

	if (*rwlock == NULL)
		ret = _pthread_rwlock_init(rwlock, NULL);
	else
		ret = 0;

	THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
	return (ret);
}
Example #11
0
static int
init_static(struct pthread *thread, pthread_rwlock_t *rwlock)
{
	int ret;

	THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);

	if (*rwlock == THR_RWLOCK_INITIALIZER)
		ret = rwlock_init(rwlock, NULL);
	else
		ret = 0;

	THR_LOCK_RELEASE(thread, &_rwlock_static_lock);

	return (ret);
}
Example #12
0
static int
init_static(struct pthread *thread, pthread_cond_t *cond)
{
	int ret;

	THR_LOCK_ACQUIRE(thread, &_cond_static_lock);

	if (*cond == NULL)
		ret = cond_init(cond, NULL);
	else
		ret = 0;

	THR_LOCK_RELEASE(thread, &_cond_static_lock);

	return (ret);
}
Example #13
0
void
_thr_tsd_unload(struct dl_phdr_info *phdr_info)
{
	struct pthread *curthread = _get_curthread();
	void (*destructor)(void *);
	int key;

	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
	for (key = 0; key < PTHREAD_KEYS_MAX; key++) {
		if (_thread_keytable[key].allocated) {
			destructor = _thread_keytable[key].destructor;
			if (destructor != NULL) {
				if (__elf_phdr_match_addr(phdr_info, destructor))
					_thread_keytable[key].destructor = NULL;
			}
		}
	}
	THR_LOCK_RELEASE(curthread, &_keytable_lock);
}
Example #14
0
static inline void
check_continuation(struct pthread *curthread, struct pthread_cond *cond,
    pthread_mutex_t *mutex)
{
	if ((curthread->interrupted != 0) &&
	    (curthread->continuation != NULL)) {
		if (cond != NULL)
			/* Unlock the condition variable structure: */
			THR_LOCK_RELEASE(curthread, &cond->c_lock);
		/*
		 * Note that even though this thread may have been
		 * canceled, POSIX requires that the mutex be
		 * reaquired prior to cancellation.
		 */
		if (mutex != NULL)
			_mutex_cv_lock(mutex);
		curthread->continuation((void *) curthread);
		PANIC("continuation returned in pthread_cond_wait.\n");
	}
}
Example #15
0
int
_pthread_key_delete(pthread_key_t key)
{
	struct pthread *curthread = _get_curthread();
	int ret = 0;

	if ((unsigned int)key < PTHREAD_KEYS_MAX) {
		/* Lock the key table: */
		THR_LOCK_ACQUIRE(curthread, &_keytable_lock);

		if (_thread_keytable[key].allocated)
			_thread_keytable[key].allocated = 0;
		else
			ret = EINVAL;

		/* Unlock the key table: */
		THR_LOCK_RELEASE(curthread, &_keytable_lock);
	} else
		ret = EINVAL;
	return (ret);
}
Example #16
0
int
_pthread_cond_destroy(pthread_cond_t *cond)
{
	struct pthread_cond	*cv;
	struct pthread		*curthread = _get_curthread();
	int			rval = 0;

	if (cond == NULL || *cond == NULL)
		rval = EINVAL;
	else {
		/* Lock the condition variable structure: */
		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);

		/*
		 * NULL the caller's pointer now that the condition
		 * variable has been destroyed:
		 */
		cv = *cond;
		*cond = NULL;

		/* Unlock the condition variable structure: */
		THR_LOCK_RELEASE(curthread, &cv->c_lock);

		/* Free the cond lock structure: */
		_lock_destroy(&cv->c_lock);

		/*
		 * Free the memory allocated for the condition
		 * variable structure:
		 */
		free(cv);

	}
	/* Return the completion status: */
	return (rval);
}
Example #17
0
int
_pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
		       const struct timespec * abstime)
{
	struct pthread	*curthread = _get_curthread();
	int	rval = 0;
	int	done = 0;
	int	mutex_locked = 1;
	int	seqno;

	THR_ASSERT(curthread->locklevel == 0,
	    "cv_timedwait: locklevel is not zero!");

	if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
	    abstime->tv_nsec >= 1000000000)
		return (EINVAL);
	/*
	 * If the condition variable is statically initialized, perform dynamic
	 * initialization.
	 */
	if (*cond == NULL && (rval = _pthread_cond_init(cond, NULL)) != 0)
		return (rval);

	if (!_kse_isthreaded())
		_kse_setthreaded(1);

	/*
	 * Enter a loop waiting for a condition signal or broadcast
	 * to wake up this thread.  A loop is needed in case the waiting
	 * thread is interrupted by a signal to execute a signal handler.
	 * It is not (currently) possible to remain in the waiting queue
	 * while running a handler.  Instead, the thread is interrupted
	 * and backed out of the waiting queue prior to executing the
	 * signal handler.
	 */

	/* Lock the condition variable structure: */
	THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
	seqno = (*cond)->c_seqno;
	do {
		/*
		 * If the condvar was statically allocated, properly
		 * initialize the tail queue.
		 */
		if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) {
			TAILQ_INIT(&(*cond)->c_queue);
			(*cond)->c_flags |= COND_FLAGS_INITED;
		}

		/* Process according to condition variable type: */
		switch ((*cond)->c_type) {
		/* Fast condition variable: */
		case COND_TYPE_FAST:
			if ((mutex == NULL) || (((*cond)->c_mutex != NULL) &&
			    ((*cond)->c_mutex != *mutex))) {
				/* Return invalid argument error: */
				rval = EINVAL;
			} else {
				/* Reset the timeout and interrupted flags: */
				curthread->timeout = 0;
				curthread->interrupted = 0;

				/*
				 * Queue the running thread for the condition
				 * variable:
				 */
				cond_queue_enq(*cond, curthread);

				/* Unlock the mutex: */
				if (mutex_locked &&
				   ((rval = _mutex_cv_unlock(mutex)) != 0)) {
					/*
					 * Cannot unlock the mutex; remove the
					 * running thread from the condition
					 * variable queue: 
					 */
					cond_queue_remove(*cond, curthread);
				} else {
					/* Remember the mutex: */
					(*cond)->c_mutex = *mutex;

					/*
					 * Don't unlock the mutex the next
					 * time through the loop (if the
					 * thread has to be requeued after
					 * handling a signal).
					 */
					mutex_locked = 0;

					/*
					 * This thread is active and is in a
					 * critical region (holding the cv
					 * lock); we should be able to safely
					 * set the state.
					 */
					THR_SCHED_LOCK(curthread, curthread);
					/* Set the wakeup time: */
					curthread->wakeup_time.tv_sec =
					    abstime->tv_sec;
					curthread->wakeup_time.tv_nsec =
					    abstime->tv_nsec;
					THR_SET_STATE(curthread, PS_COND_WAIT);

					/* Remember the CV: */
					curthread->data.cond = *cond;
					curthread->sigbackout = cond_wait_backout;
					THR_SCHED_UNLOCK(curthread, curthread);

					/* Unlock the CV structure: */
					THR_LOCK_RELEASE(curthread,
					    &(*cond)->c_lock);

					/* Schedule the next thread: */
					_thr_sched_switch(curthread);

					/*
					 * XXX - This really isn't a good check
					 * since there can be more than one
					 * thread waiting on the CV.  Signals
					 * sent to threads waiting on mutexes
					 * or CVs should really be deferred
					 * until the threads are no longer
					 * waiting, but POSIX says that signals
					 * should be sent "as soon as possible".
					 */
					done = (seqno != (*cond)->c_seqno);
					if (done && !THR_IN_CONDQ(curthread)) {
						/*
						 * The thread is dequeued, so
						 * it is safe to clear these.
						 */
						curthread->data.cond = NULL;
						curthread->sigbackout = NULL;
						check_continuation(curthread,
						    NULL, mutex);
						return (_mutex_cv_lock(mutex));
					}

					/* Relock the CV structure: */
					THR_LOCK_ACQUIRE(curthread,
					    &(*cond)->c_lock);

					/*
					 * Clear these after taking the lock to
					 * prevent a race condition where a
					 * signal can arrive before dequeueing
					 * the thread.
					 */
					curthread->data.cond = NULL;
					curthread->sigbackout = NULL;

					done = (seqno != (*cond)->c_seqno);

					if (THR_IN_CONDQ(curthread)) {
						cond_queue_remove(*cond,
						    curthread);

						/* Check for no more waiters: */
						if (TAILQ_EMPTY(&(*cond)->c_queue))
							(*cond)->c_mutex = NULL;
					}

					if (curthread->timeout != 0) {
						/* The wait timedout. */
						rval = ETIMEDOUT;
					}
				}
			}
			break;

		/* Trap invalid condition variable types: */
		default:
			/* Return an invalid argument error: */
			rval = EINVAL;
			break;
		}

		check_continuation(curthread, *cond,
		    mutex_locked ? NULL : mutex);
	} while ((done == 0) && (rval == 0));

	/* Unlock the condition variable structure: */
	THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);

	if (mutex_locked == 0)
		_mutex_cv_lock(mutex);

	/* Return the completion status: */
	return (rval);
}
Example #18
0
int
_pthread_cond_signal(pthread_cond_t * cond)
{
	struct pthread	*curthread = _get_curthread();
	struct pthread	*pthread;
	struct kse_mailbox *kmbx;
	int		rval = 0;

	THR_ASSERT(curthread->locklevel == 0,
	    "cv_timedwait: locklevel is not zero!");
	if (cond == NULL)
		rval = EINVAL;
       /*
        * If the condition variable is statically initialized, perform dynamic
        * initialization.
        */
	else if (*cond != NULL || (rval = _pthread_cond_init(cond, NULL)) == 0) {
		/* Lock the condition variable structure: */
		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);

		/* Process according to condition variable type: */
		switch ((*cond)->c_type) {
		/* Fast condition variable: */
		case COND_TYPE_FAST:
			/* Increment the sequence number: */
			(*cond)->c_seqno++;

			/*
			 * Wakeups have to be done with the CV lock held;
			 * otherwise there is a race condition where the
			 * thread can timeout, run on another KSE, and enter
			 * another blocking state (including blocking on a CV).
			 */
			if ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
			    != NULL) {
				THR_SCHED_LOCK(curthread, pthread);
				cond_queue_remove(*cond, pthread);
				pthread->sigbackout = NULL;
				if ((pthread->kseg == curthread->kseg) &&
				    (pthread->active_priority >
				    curthread->active_priority))
					curthread->critical_yield = 1;
				kmbx = _thr_setrunnable_unlocked(pthread);
				THR_SCHED_UNLOCK(curthread, pthread);
				if (kmbx != NULL)
					kse_wakeup(kmbx);
			}
			/* Check for no more waiters: */
			if (TAILQ_EMPTY(&(*cond)->c_queue))
				(*cond)->c_mutex = NULL;
			break;

		/* Trap invalid condition variable types: */
		default:
			/* Return an invalid argument error: */
			rval = EINVAL;
			break;
		}

		/* Unlock the condition variable structure: */
		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
	}

	/* Return the completion status: */
	return (rval);
}
Example #19
0
int
_pthread_cond_broadcast(pthread_cond_t * cond)
{
	struct pthread	*curthread = _get_curthread();
	struct pthread	*pthread;
	struct kse_mailbox *kmbx;
	int		rval = 0;

	THR_ASSERT(curthread->locklevel == 0,
	    "cv_timedwait: locklevel is not zero!");
	if (cond == NULL)
		rval = EINVAL;
       /*
        * If the condition variable is statically initialized, perform dynamic
        * initialization.
        */
	else if (*cond != NULL || (rval = _pthread_cond_init(cond, NULL)) == 0) {
		/* Lock the condition variable structure: */
		THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);

		/* Process according to condition variable type: */
		switch ((*cond)->c_type) {
		/* Fast condition variable: */
		case COND_TYPE_FAST:
			/* Increment the sequence number: */
			(*cond)->c_seqno++;

			/*
			 * Enter a loop to bring all threads off the
			 * condition queue:
			 */
			while ((pthread = TAILQ_FIRST(&(*cond)->c_queue))
			    != NULL) {
				THR_SCHED_LOCK(curthread, pthread);
				cond_queue_remove(*cond, pthread);
				pthread->sigbackout = NULL;
				if ((pthread->kseg == curthread->kseg) &&
				    (pthread->active_priority >
				    curthread->active_priority))
					curthread->critical_yield = 1;
				kmbx = _thr_setrunnable_unlocked(pthread);
				THR_SCHED_UNLOCK(curthread, pthread);
				if (kmbx != NULL)
					kse_wakeup(kmbx);
			}

			/* There are no more waiting threads: */
			(*cond)->c_mutex = NULL;
			break;

		/* Trap invalid condition variable types: */
		default:
			/* Return an invalid argument error: */
			rval = EINVAL;
			break;
		}

		/* Unlock the condition variable structure: */
		THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
	}

	/* Return the completion status: */
	return (rval);
}
Example #20
0
void 
_thread_cleanupspecific(void)
{
	struct pthread	*curthread = _get_curthread();
	void		(*destructor)( void *);
	const void	*data = NULL;
	int		key;
	int		i;

	if (curthread->specific == NULL)
		return;

	/* Lock the key table: */
	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
	for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) &&
	    (curthread->specific_data_count > 0); i++) {
		for (key = 0; (key < PTHREAD_KEYS_MAX) &&
		    (curthread->specific_data_count > 0); key++) {
			destructor = NULL;

			if (_thread_keytable[key].allocated &&
			    (curthread->specific[key].data != NULL)) {
				if (curthread->specific[key].seqno ==
				    _thread_keytable[key].seqno) {
					data = curthread->specific[key].data;
					destructor = _thread_keytable[key].destructor;
				}
				curthread->specific[key].data = NULL;
				curthread->specific_data_count--;
			}
			else if (curthread->specific[key].data != NULL) {
				/* 
				 * This can happen if the key is deleted via
				 * pthread_key_delete without first setting the value
				 * to NULL in all threads.  POSIX says that the
				 * destructor is not invoked in this case.
				 */
				curthread->specific[key].data = NULL;
				curthread->specific_data_count--;
			}

			/*
			 * If there is a destructor, call it
			 * with the key table entry unlocked:
			 */
			if (destructor != NULL) {
				/*
				 * Don't hold the lock while calling the
				 * destructor:
				 */
				THR_LOCK_RELEASE(curthread, &_keytable_lock);
				destructor(__DECONST(void *, data));
				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
			}
		}
	}
	THR_LOCK_RELEASE(curthread, &_keytable_lock);
	free(curthread->specific);
	curthread->specific = NULL;
	if (curthread->specific_data_count > 0)
		stderr_debug("Thread %p has exited with leftover "
		    "thread-specific data after %d destructor iterations\n",
		    curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
}