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