113546Sjulian/*
213546Sjulian * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
313546Sjulian * All rights reserved.
413546Sjulian *
513546Sjulian * Redistribution and use in source and binary forms, with or without
613546Sjulian * modification, are permitted provided that the following conditions
713546Sjulian * are met:
813546Sjulian * 1. Redistributions of source code must retain the above copyright
913546Sjulian *    notice, this list of conditions and the following disclaimer.
1013546Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1113546Sjulian *    notice, this list of conditions and the following disclaimer in the
1213546Sjulian *    documentation and/or other materials provided with the distribution.
13165967Simp * 3. Neither the name of the author nor the names of any co-contributors
1413546Sjulian *    may be used to endorse or promote products derived from this software
1513546Sjulian *    without specific prior written permission.
1613546Sjulian *
1713546Sjulian * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
1813546Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1913546Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2049439Sdeischen * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2113546Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2213546Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2313546Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2413546Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2513546Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2613546Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2713546Sjulian * SUCH DAMAGE.
2813546Sjulian *
2950476Speter * $FreeBSD$
3013546Sjulian */
31174112Sdeischen
32113658Sdeischen#include "namespace.h"
3313546Sjulian#include <pthread.h>
34113658Sdeischen#include "un-namespace.h"
35103388Smini#include "thr_private.h"
3613546Sjulian
3775369Sdeischen__weak_reference(_pthread_once, pthread_once);
3871581Sdeischen
39119928Sdavidxu#define ONCE_NEVER_DONE		PTHREAD_NEEDS_INIT
40119928Sdavidxu#define ONCE_DONE		PTHREAD_DONE_INIT
41119928Sdavidxu#define	ONCE_IN_PROGRESS	0x02
42119928Sdavidxu#define	ONCE_MASK		0x03
43119928Sdavidxu
44119928Sdavidxustatic pthread_mutex_t		once_lock = PTHREAD_MUTEX_INITIALIZER;
45119928Sdavidxustatic pthread_cond_t		once_cv = PTHREAD_COND_INITIALIZER;
46119928Sdavidxu
47119928Sdavidxu/*
48119928Sdavidxu * POSIX:
49119928Sdavidxu * The pthread_once() function is not a cancellation point. However,
50119928Sdavidxu * if init_routine is a cancellation point and is canceled, the effect
51119928Sdavidxu * on once_control shall be as if pthread_once() was never called.
52119928Sdavidxu */
53119928Sdavidxu
54119928Sdavidxustatic void
55119928Sdavidxuonce_cancel_handler(void *arg)
56119928Sdavidxu{
57119928Sdavidxu	pthread_once_t *once_control = arg;
58119928Sdavidxu
59119928Sdavidxu	_pthread_mutex_lock(&once_lock);
60119928Sdavidxu	once_control->state = ONCE_NEVER_DONE;
61119928Sdavidxu	_pthread_mutex_unlock(&once_lock);
62119928Sdavidxu	_pthread_cond_broadcast(&once_cv);
63119928Sdavidxu}
64119928Sdavidxu
6513546Sjulianint
66113658Sdeischen_pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
6713546Sjulian{
68139023Sdeischen	struct pthread *curthread;
69119928Sdavidxu	int wakeup = 0;
70119928Sdavidxu
71119928Sdavidxu	if (once_control->state == ONCE_DONE)
72119928Sdavidxu		return (0);
73119928Sdavidxu	_pthread_mutex_lock(&once_lock);
74119928Sdavidxu	while (*(volatile int *)&(once_control->state) == ONCE_IN_PROGRESS)
75119928Sdavidxu		_pthread_cond_wait(&once_cv, &once_lock);
76119928Sdavidxu	/*
77119928Sdavidxu	 * If previous thread was canceled, then the state still
78119928Sdavidxu	 * could be ONCE_NEVER_DONE, we need to check it again.
79119928Sdavidxu	 */
80119928Sdavidxu	if (*(volatile int *)&(once_control->state) == ONCE_NEVER_DONE) {
81119928Sdavidxu		once_control->state = ONCE_IN_PROGRESS;
82119928Sdavidxu		_pthread_mutex_unlock(&once_lock);
83139023Sdeischen		curthread = _get_curthread();
84139023Sdeischen		THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control);
85119928Sdavidxu		init_routine();
86139023Sdeischen		THR_CLEANUP_POP(curthread, 0);
87119928Sdavidxu		_pthread_mutex_lock(&once_lock);
88119928Sdavidxu		once_control->state = ONCE_DONE;
89119928Sdavidxu		wakeup = 1;
9013546Sjulian	}
91119928Sdavidxu	_pthread_mutex_unlock(&once_lock);
92119928Sdavidxu	if (wakeup)
93119928Sdavidxu		_pthread_cond_broadcast(&once_cv);
9413546Sjulian	return (0);
9513546Sjulian}
96119928Sdavidxu
97