xref: /freebsd/lib/libthr/thread/thr_once.c (revision aad93b043af83366e3169493e494ab8ec620249a)
1bb535300SJeff Roberson /*
2df2cf821SDavid Xu  * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
3bb535300SJeff Roberson  * All rights reserved.
4bb535300SJeff Roberson  *
5bb535300SJeff Roberson  * Redistribution and use in source and binary forms, with or without
6bb535300SJeff Roberson  * modification, are permitted provided that the following conditions
7bb535300SJeff Roberson  * are met:
8bb535300SJeff Roberson  * 1. Redistributions of source code must retain the above copyright
9df2cf821SDavid Xu  *    notice unmodified, this list of conditions, and the following
10df2cf821SDavid Xu  *    disclaimer.
11bb535300SJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
12bb535300SJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
13bb535300SJeff Roberson  *    documentation and/or other materials provided with the distribution.
14bb535300SJeff Roberson  *
15df2cf821SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16df2cf821SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17df2cf821SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18df2cf821SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19df2cf821SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20df2cf821SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21df2cf821SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22df2cf821SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23df2cf821SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24df2cf821SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25bb535300SJeff Roberson  *
26bb535300SJeff Roberson  * $FreeBSD$
27df2cf821SDavid Xu  *
28bb535300SJeff Roberson  */
29a091d823SDavid Xu 
30a091d823SDavid Xu #include "namespace.h"
31bb535300SJeff Roberson #include <pthread.h>
32a091d823SDavid Xu #include "un-namespace.h"
33a091d823SDavid Xu 
34bb535300SJeff Roberson #include "thr_private.h"
35bb535300SJeff Roberson 
36bb535300SJeff Roberson __weak_reference(_pthread_once, pthread_once);
37bb535300SJeff Roberson 
38a091d823SDavid Xu #define ONCE_NEVER_DONE		PTHREAD_NEEDS_INIT
39a091d823SDavid Xu #define ONCE_DONE		PTHREAD_DONE_INIT
40a091d823SDavid Xu #define	ONCE_IN_PROGRESS	0x02
411b3418b2SDavid Xu #define ONCE_WAIT		0x03
42a091d823SDavid Xu 
43a091d823SDavid Xu /*
44a091d823SDavid Xu  * POSIX:
45a091d823SDavid Xu  * The pthread_once() function is not a cancellation point. However,
46a091d823SDavid Xu  * if init_routine is a cancellation point and is canceled, the effect
47a091d823SDavid Xu  * on once_control shall be as if pthread_once() was never called.
48a091d823SDavid Xu  */
49a091d823SDavid Xu 
50a091d823SDavid Xu static void
51a091d823SDavid Xu once_cancel_handler(void *arg)
52a091d823SDavid Xu {
53a091d823SDavid Xu 	pthread_once_t *once_control = arg;
54a091d823SDavid Xu 
551b3418b2SDavid Xu 	if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS, ONCE_NEVER_DONE))
561b3418b2SDavid Xu 		return;
571b3418b2SDavid Xu 	atomic_store_rel_int(&once_control->state, ONCE_NEVER_DONE);
581b3418b2SDavid Xu 	_thr_umtx_wake(&once_control->state, INT_MAX, 0);
59a091d823SDavid Xu }
60a091d823SDavid Xu 
61bb535300SJeff Roberson int
62bb535300SJeff Roberson _pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
63bb535300SJeff Roberson {
645150e987SDavid Xu 	struct pthread *curthread;
651b3418b2SDavid Xu 	int state;
66a091d823SDavid Xu 
67*aad93b04SRyan Stone 	_thr_check_init();
68*aad93b04SRyan Stone 
691b3418b2SDavid Xu 	for (;;) {
701b3418b2SDavid Xu 		state = once_control->state;
711b3418b2SDavid Xu 		if (state == ONCE_DONE)
72a091d823SDavid Xu 			return (0);
731b3418b2SDavid Xu 		if (state == ONCE_NEVER_DONE) {
741b3418b2SDavid Xu 			if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_IN_PROGRESS))
751b3418b2SDavid Xu 				break;
761b3418b2SDavid Xu 		} else if (state == ONCE_IN_PROGRESS) {
771b3418b2SDavid Xu 			if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_WAIT))
781b3418b2SDavid Xu 				_thr_umtx_wait_uint(&once_control->state, ONCE_WAIT, NULL, 0);
791b3418b2SDavid Xu 		} else if (state == ONCE_WAIT) {
801b3418b2SDavid Xu 			_thr_umtx_wait_uint(&once_control->state, state, NULL, 0);
811b3418b2SDavid Xu 		} else
821b3418b2SDavid Xu 			return (EINVAL);
831b3418b2SDavid Xu         }
841b3418b2SDavid Xu 
855150e987SDavid Xu 	curthread = _get_curthread();
865150e987SDavid Xu 	THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control);
87bb535300SJeff Roberson 	init_routine();
885150e987SDavid Xu 	THR_CLEANUP_POP(curthread, 0);
891b3418b2SDavid Xu 	if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS, ONCE_DONE))
901b3418b2SDavid Xu 		return (0);
911b3418b2SDavid Xu 	atomic_store_rel_int(&once_control->state, ONCE_DONE);
921b3418b2SDavid Xu 	_thr_umtx_wake(&once_control->state, INT_MAX, 0);
93bb535300SJeff Roberson 	return (0);
94bb535300SJeff Roberson }
95a091d823SDavid Xu 
966f716c2fSDavid Xu void
976f716c2fSDavid Xu _thr_once_init()
986f716c2fSDavid Xu {
996f716c2fSDavid Xu }
100