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