xref: /dflybsd-src/lib/libthread_xu/thread/thr_once.c (revision cf8046a92768d53e67d2533fb51b137d5506248d)
171b3fa15SDavid Xu /*
2d3b15642Szrj  * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
371b3fa15SDavid Xu  * All rights reserved.
471b3fa15SDavid Xu  *
571b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
671b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
771b3fa15SDavid Xu  * are met:
871b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
971b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer.
1071b3fa15SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
1171b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
1271b3fa15SDavid Xu  *    documentation and/or other materials provided with the distribution.
1371b3fa15SDavid Xu  *
14d3b15642Szrj  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15d3b15642Szrj  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16d3b15642Szrj  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17d3b15642Szrj  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18d3b15642Szrj  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19d3b15642Szrj  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20d3b15642Szrj  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21d3b15642Szrj  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22d3b15642Szrj  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23d3b15642Szrj  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2471b3fa15SDavid Xu  *
2571b3fa15SDavid Xu  */
2671b3fa15SDavid Xu 
27fc71f871SDavid Xu #include "namespace.h"
2871b3fa15SDavid Xu #include <pthread.h>
29fc71f871SDavid Xu #include "un-namespace.h"
30fc71f871SDavid Xu 
3171b3fa15SDavid Xu #include "thr_private.h"
3271b3fa15SDavid Xu 
3371b3fa15SDavid Xu #define ONCE_NEVER_DONE		PTHREAD_NEEDS_INIT
3471b3fa15SDavid Xu #define ONCE_DONE		PTHREAD_DONE_INIT
3571b3fa15SDavid Xu #define ONCE_IN_PROGRESS	0x02
3671b3fa15SDavid Xu #define ONCE_MASK		0x03
3771b3fa15SDavid Xu 
3871b3fa15SDavid Xu static pthread_mutex_t		once_lock = PTHREAD_MUTEX_INITIALIZER;
3971b3fa15SDavid Xu static pthread_cond_t		once_cv = PTHREAD_COND_INITIALIZER;
4071b3fa15SDavid Xu 
4171b3fa15SDavid Xu /*
4271b3fa15SDavid Xu  * POSIX:
4371b3fa15SDavid Xu  * The pthread_once() function is not a cancellation point. However,
4471b3fa15SDavid Xu  * if init_routine is a cancellation point and is canceled, the effect
4571b3fa15SDavid Xu  * on once_control shall be as if pthread_once() was never called.
4671b3fa15SDavid Xu  */
4771b3fa15SDavid Xu 
4871b3fa15SDavid Xu static void
once_cancel_handler(void * arg)4971b3fa15SDavid Xu once_cancel_handler(void *arg)
5071b3fa15SDavid Xu {
5171b3fa15SDavid Xu 	pthread_once_t *once_control = arg;
5271b3fa15SDavid Xu 
5371b3fa15SDavid Xu 	_pthread_mutex_lock(&once_lock);
54*cf8046a9Szrj 	once_control->__state = ONCE_NEVER_DONE;
5571b3fa15SDavid Xu 	_pthread_mutex_unlock(&once_lock);
5671b3fa15SDavid Xu 	_pthread_cond_broadcast(&once_cv);
5771b3fa15SDavid Xu }
5871b3fa15SDavid Xu 
5971b3fa15SDavid Xu int
_pthread_once(pthread_once_t * once_control,void (* init_routine)(void))6071b3fa15SDavid Xu _pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
6171b3fa15SDavid Xu {
6271b3fa15SDavid Xu 	int wakeup = 0;
6371b3fa15SDavid Xu 
648b03c2a2Szrj 	_thr_check_init();
658b03c2a2Szrj 
66*cf8046a9Szrj 	if (once_control->__state == ONCE_DONE)
6771b3fa15SDavid Xu 		return (0);
6871b3fa15SDavid Xu 	_pthread_mutex_lock(&once_lock);
69*cf8046a9Szrj 	while (*(volatile int *)&(once_control->__state) == ONCE_IN_PROGRESS)
7071b3fa15SDavid Xu 		_pthread_cond_wait(&once_cv, &once_lock);
7171b3fa15SDavid Xu 	/*
7271b3fa15SDavid Xu 	 * If previous thread was canceled, then the state still
7371b3fa15SDavid Xu 	 * could be ONCE_NEVER_DONE, we need to check it again.
7471b3fa15SDavid Xu 	 */
75*cf8046a9Szrj 	if (*(volatile int *)&(once_control->__state) == ONCE_NEVER_DONE) {
76*cf8046a9Szrj 		once_control->__state = ONCE_IN_PROGRESS;
7771b3fa15SDavid Xu 		_pthread_mutex_unlock(&once_lock);
7871b3fa15SDavid Xu 		_pthread_cleanup_push(once_cancel_handler, once_control);
7971b3fa15SDavid Xu 		init_routine();
8071b3fa15SDavid Xu 		_pthread_cleanup_pop(0);
8171b3fa15SDavid Xu 		_pthread_mutex_lock(&once_lock);
82*cf8046a9Szrj 		once_control->__state = ONCE_DONE;
8371b3fa15SDavid Xu 		wakeup = 1;
8471b3fa15SDavid Xu 	}
8571b3fa15SDavid Xu 	_pthread_mutex_unlock(&once_lock);
8671b3fa15SDavid Xu 	if (wakeup)
8771b3fa15SDavid Xu 		_pthread_cond_broadcast(&once_cv);
8871b3fa15SDavid Xu 	return (0);
8971b3fa15SDavid Xu }
9071b3fa15SDavid Xu 
915a1048c8SDavid Xu __strong_reference(_pthread_once, pthread_once);
92