xref: /freebsd/lib/libthr/thread/thr_once.c (revision f8bbbce458194ff4312c610d32a64ff4a3a71d45)
15e53a4f9SPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
4df2cf821SDavid Xu  * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
5bb535300SJeff Roberson  * All rights reserved.
6bb535300SJeff Roberson  *
7bb535300SJeff Roberson  * Redistribution and use in source and binary forms, with or without
8bb535300SJeff Roberson  * modification, are permitted provided that the following conditions
9bb535300SJeff Roberson  * are met:
10bb535300SJeff Roberson  * 1. Redistributions of source code must retain the above copyright
11df2cf821SDavid Xu  *    notice unmodified, this list of conditions, and the following
12df2cf821SDavid Xu  *    disclaimer.
13bb535300SJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
14bb535300SJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
15bb535300SJeff Roberson  *    documentation and/or other materials provided with the distribution.
16bb535300SJeff Roberson  *
17df2cf821SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18df2cf821SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19df2cf821SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20df2cf821SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21df2cf821SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22df2cf821SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23df2cf821SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24df2cf821SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25df2cf821SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26df2cf821SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27bb535300SJeff Roberson  */
28a091d823SDavid Xu 
29a091d823SDavid Xu #include "namespace.h"
30bb535300SJeff Roberson #include <pthread.h>
31a091d823SDavid Xu #include "un-namespace.h"
32a091d823SDavid Xu 
33bb535300SJeff Roberson #include "thr_private.h"
34bb535300SJeff Roberson 
350ab1bfc7SKonstantin Belousov __weak_reference(_thr_once, pthread_once);
360ab1bfc7SKonstantin Belousov __weak_reference(_thr_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
once_cancel_handler(void * arg)51a091d823SDavid Xu once_cancel_handler(void *arg)
52a091d823SDavid Xu {
53b684727bSKonstantin Belousov 	pthread_once_t *once_control;
54a091d823SDavid Xu 
55b684727bSKonstantin Belousov 	once_control = arg;
56b684727bSKonstantin Belousov 	if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS,
57b684727bSKonstantin Belousov 	    ONCE_NEVER_DONE))
581b3418b2SDavid Xu 		return;
591b3418b2SDavid Xu 	atomic_store_rel_int(&once_control->state, ONCE_NEVER_DONE);
601b3418b2SDavid Xu 	_thr_umtx_wake(&once_control->state, INT_MAX, 0);
61a091d823SDavid Xu }
62a091d823SDavid Xu 
63bb535300SJeff Roberson int
_thr_once(pthread_once_t * once_control,void (* init_routine)(void))640ab1bfc7SKonstantin Belousov _thr_once(pthread_once_t *once_control, void (*init_routine)(void))
65bb535300SJeff Roberson {
665150e987SDavid Xu 	struct pthread *curthread;
671b3418b2SDavid Xu 	int state;
68a091d823SDavid Xu 
69aad93b04SRyan Stone 	_thr_check_init();
70aad93b04SRyan Stone 
711b3418b2SDavid Xu 	for (;;) {
721b3418b2SDavid Xu 		state = once_control->state;
733e7e67c0SKonstantin Belousov 		if (state == ONCE_DONE) {
743e7e67c0SKonstantin Belousov 			atomic_thread_fence_acq();
75a091d823SDavid Xu 			return (0);
763e7e67c0SKonstantin Belousov 		}
771b3418b2SDavid Xu 		if (state == ONCE_NEVER_DONE) {
78b684727bSKonstantin Belousov 			if (atomic_cmpset_int(&once_control->state, state,
79b684727bSKonstantin Belousov 			    ONCE_IN_PROGRESS))
801b3418b2SDavid Xu 				break;
811b3418b2SDavid Xu 		} else if (state == ONCE_IN_PROGRESS) {
82b684727bSKonstantin Belousov 			if (atomic_cmpset_int(&once_control->state, state,
83b684727bSKonstantin Belousov 			    ONCE_WAIT))
84b684727bSKonstantin Belousov 				_thr_umtx_wait_uint(&once_control->state,
85b684727bSKonstantin Belousov 				    ONCE_WAIT, NULL, 0);
861b3418b2SDavid Xu 		} else if (state == ONCE_WAIT) {
87b684727bSKonstantin Belousov 			_thr_umtx_wait_uint(&once_control->state, state,
88b684727bSKonstantin Belousov 			    NULL, 0);
891b3418b2SDavid Xu 		} else
901b3418b2SDavid Xu 			return (EINVAL);
911b3418b2SDavid Xu         }
921b3418b2SDavid Xu 
935150e987SDavid Xu 	curthread = _get_curthread();
945150e987SDavid Xu 	THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control);
95bb535300SJeff Roberson 	init_routine();
965150e987SDavid Xu 	THR_CLEANUP_POP(curthread, 0);
97b684727bSKonstantin Belousov 	if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS,
98b684727bSKonstantin Belousov 	    ONCE_DONE))
991b3418b2SDavid Xu 		return (0);
1001b3418b2SDavid Xu 	atomic_store_rel_int(&once_control->state, ONCE_DONE);
1011b3418b2SDavid Xu 	_thr_umtx_wake(&once_control->state, INT_MAX, 0);
102bb535300SJeff Roberson 	return (0);
103bb535300SJeff Roberson }
104