once.cpp revision aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/* $Id$ */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** @file
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * IPRT - Execute Once.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Copyright (C) 2007 Sun Microsystems, Inc.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * available from http://www.virtualbox.org. This file is free software;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * you can redistribute it and/or modify it under the terms of the GNU
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * General Public License (GPL) as published by the Free Software
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * The contents of this file may alternatively be used under the terms
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * of the Common Development and Distribution License Version 1.0
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * VirtualBox OSE distribution, in which case the provisions of the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * CDDL are applicable instead of those of the GPL.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * You may elect to license modified versions of this file under the
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * terms and conditions of either the GPL or the CDDL or both.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * additional information or have any questions.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/*******************************************************************************
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync* Header Files *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync*******************************************************************************/
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/once.h>
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync#include "internal/iprt.h"
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/semaphore.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/thread.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/err.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/assert.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/asm.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsyncRTDECL(int) RTOnce(PRTONCE pOnce, PFNRTONCE pfnOnce, void *pvUser1, void *pvUser2)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync{
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Validate input (strict builds only).
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync AssertPtr(pOnce);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync AssertPtr(pfnOnce);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Deal with the 'initialized' case first
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (RT_LIKELY(iState == 2))
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync return ASMAtomicUoReadS32(&pOnce->rc);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync AssertReturn(iState == -1 || iState == 1, VERR_INTERNAL_ERROR);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Do we initialize it?
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (iState == -1)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTSEMEVENTMULTI hEventMulti;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int rc = RTSemEventMultiCreate(&hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (RT_FAILURE(rc))
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync hEventMulti = NIL_RTSEMEVENTMULTI;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (ASMAtomicCmpXchgS32(&pOnce->iState, 1, -1))
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicWriteHandle(&pOnce->hEventMulti, hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicIncS32(&pOnce->cEventRefs);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /* do the execute once stuff. */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int32_t rcOnce = pfnOnce(pvUser1, pvUser2);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /* set the return code, change the state and signal any waiters. */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicWriteS32(&pOnce->rc, rcOnce);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicWriteS32(&pOnce->iState, 2);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (hEventMulti != NIL_RTSEMEVENTMULTI)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTSemEventMultiSignal(hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /* last guy destroys the semaphore. */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (ASMAtomicDecS32(&pOnce->cEventRefs) == 0)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicWriteSize(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync else
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync hEventMulti = NIL_RTSEMEVENTMULTI;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (hEventMulti != NIL_RTSEMEVENTMULTI)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTSemEventMultiDestroy(hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Wait for it to finish initializing.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (ASMAtomicReadS32(&pOnce->iState) == 1)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int i = 0;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync while (ASMAtomicReadS32(&pOnce->iState) == 1)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync bool fYieldSleep = true;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Take care not to increment the counter if it's 0, that indicates
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * that RTONCE::hEventMulti isn't valid either because it's not set
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * yet, or because it's being destroyed.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync int32_t cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync while ( cEventRefs > 0
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync && !ASMAtomicCmpXchgS32(&pOnce->cEventRefs, cEventRefs + 1, cEventRefs))
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync cEventRefs = ASMAtomicUoReadS32(&pOnce->cEventRefs);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (cEventRefs > 1)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * The hEventMulti might be NIL for two reasons, see above in
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * the init code, if it isn't valid just do the yield/sleep thing.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTSEMEVENTMULTI hEventMulti;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicUoReadSize(&pOnce->hEventMulti, &hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (hEventMulti != NIL_RTSEMEVENTMULTI)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync fYieldSleep = false;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTSemEventMultiWait(hEventMulti, RT_INDEFINITE_WAIT);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Last thread cleans up.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (ASMAtomicDecS32(&pOnce->cEventRefs) == 0)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync ASMAtomicXchgHandle(&pOnce->hEventMulti, NIL_RTSEMEVENTMULTI, &hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (hEventMulti != NIL_RTSEMEVENTMULTI)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTSemEventMultiDestroy(hEventMulti);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * If we didn't block, yield or sleep for a bit.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * The sleep is essential to prevent higher priority threads from spinning wildly
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * and preventing a lower priority thread from completing the pfnOnce operation
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * in a timely manner.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (fYieldSleep)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync {
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (ASMAtomicReadS32(&pOnce->iState) != 1)
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync break;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync if (!(++i % 8) )
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTThreadSleep(1);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync else
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync RTThreadYield();
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /*
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Finally, return the status code from the execute once function.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync return ASMAtomicUoReadS32(&pOnce->rc);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync}
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsyncRT_EXPORT_SYMBOL(RTOnce);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsyncRTDECL(void) RTOnceReset(PRTONCE pOnce)
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync{
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync /* Cannot be done while busy! */
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync AssertPtr(pOnce);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync Assert(pOnce->hEventMulti == NIL_RTSEMEVENTMULTI);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync Assert(pOnce->iState != 1);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync /* Do the same as RTONCE_INITIALIZER does. */
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync ASMAtomicWriteS32(&pOnce->rc, VERR_INTERNAL_ERROR);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync ASMAtomicWriteS32(&pOnce->iState, -1);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync}
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsyncRT_EXPORT_SYMBOL(RTOnceReset);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync