33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** @file
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * IPRT - Execute Once.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/*
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * Copyright (C) 2006-2012 Oracle Corporation
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
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#ifndef ___iprt_once_h
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#define ___iprt_once_h
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/cdefs.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/types.h>
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync#include <iprt/asm.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#include <iprt/err.h>
a438caaf732f7839dc66b4f8dad672527845a003vboxsync#include <iprt/list.h>
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
590bfe12ce22cd3716448fbb9f4dc51664bfe5e2vboxsyncRT_C_DECLS_BEGIN
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** @defgroup grp_rt_once RTOnce - Execute Once
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @ingroup grp_rt
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @{
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
a438caaf732f7839dc66b4f8dad672527845a003vboxsync/**
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * Callback that gets executed once.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync *
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @returns IPRT style status code, RTOnce returns this.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync *
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pvUser The user parameter.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync */
a438caaf732f7839dc66b4f8dad672527845a003vboxsynctypedef DECLCALLBACK(int32_t) FNRTONCE(void *pvUser);
a438caaf732f7839dc66b4f8dad672527845a003vboxsync/** Pointer to a FNRTONCE. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsynctypedef FNRTONCE *PFNRTONCE;
a438caaf732f7839dc66b4f8dad672527845a003vboxsync
a438caaf732f7839dc66b4f8dad672527845a003vboxsync/**
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * Callback that gets executed on IPRT/process termination.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync *
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pvUser The user parameter.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param fLazyCleanUpOk Indicates whether lazy clean-up is OK (see
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * initterm.h).
a438caaf732f7839dc66b4f8dad672527845a003vboxsync */
a438caaf732f7839dc66b4f8dad672527845a003vboxsynctypedef DECLCALLBACK(void) FNRTONCECLEANUP(void *pvUser, bool fLazyCleanUpOk);
a438caaf732f7839dc66b4f8dad672527845a003vboxsync/** Pointer to a FNRTONCE. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsynctypedef FNRTONCECLEANUP *PFNRTONCECLEANUP;
a438caaf732f7839dc66b4f8dad672527845a003vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/**
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Execute once structure.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * This is typically a global variable that is statically initialized
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * by RTONCE_INITIALIZER.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsynctypedef struct RTONCE
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync{
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /** Event semaphore that the other guys are blocking on. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync RTSEMEVENTMULTI volatile hEventMulti;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /** Reference counter for hEventMulti. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync int32_t volatile cEventRefs;
a438caaf732f7839dc66b4f8dad672527845a003vboxsync /** See RTONCESTATE. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync int32_t volatile iState;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync /** The return code of pfnOnce. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync int32_t volatile rc;
a438caaf732f7839dc66b4f8dad672527845a003vboxsync
a438caaf732f7839dc66b4f8dad672527845a003vboxsync /** Pointer to the clean-up function. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync PFNRTONCECLEANUP pfnCleanUp;
a438caaf732f7839dc66b4f8dad672527845a003vboxsync /** Argument to hand to the clean-up function. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync void *pvUser;
a438caaf732f7839dc66b4f8dad672527845a003vboxsync /** Clean-up list entry. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync RTLISTNODE CleanUpNode;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync} RTONCE;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** Pointer to a execute once struct. */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsynctypedef RTONCE *PRTONCE;
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync/**
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * The execute once statemachine.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsynctypedef enum RTONCESTATE
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync{
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** RTOnce() has not been called.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: NO_SEM */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_UNINITIALIZED = 1,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** RTOnce() is busy, no race.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: CREATING_SEM, DONE */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_BUSY_NO_SEM,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** More than one RTOnce() caller is busy.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: BUSY_HAVE_SEM, BUSY_SPIN, DONE_CREATING_SEM, DONE */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_BUSY_CREATING_SEM,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** More than one RTOnce() caller, the first is busy, the others are
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * waiting.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: DONE */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_BUSY_HAVE_SEM,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** More than one RTOnce() caller, the first is busy, the others failed to
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * create a semaphore and are spinning.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: DONE */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_BUSY_SPIN,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** More than one RTOnce() caller, the first has completed, the others
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * are busy creating the semaphore.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: DONE_HAVE_SEM */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_DONE_CREATING_SEM,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** More than one RTOnce() caller, the first is busy grabbing the
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * semaphore, while the others are waiting.
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync * Next: DONE */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_DONE_HAVE_SEM,
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync /** The execute once stuff has completed. */
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync RTONCESTATE_DONE = 16
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync} RTONCESTATE;
96ae2df030763cee874d3f5ac0be07cd1f793281vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** Static initializer for RTONCE variables. */
a438caaf732f7839dc66b4f8dad672527845a003vboxsync#define RTONCE_INITIALIZER \
a438caaf732f7839dc66b4f8dad672527845a003vboxsync { NIL_RTSEMEVENTMULTI, 0, RTONCESTATE_UNINITIALIZED, VERR_INTERNAL_ERROR, NULL, NULL, { NULL, NULL } }
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/**
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * Serializes execution of the pfnOnce function, making sure it's
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * executed exactly once and that nobody returns from RTOnce before
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * it has executed successfully.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @returns IPRT like status code returned by pfnOnce.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pOnce Pointer to the execute once variable.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pfnOnce The function to executed once.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pfnCleanUp The function that will be doing the cleaning up.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * Optional.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pvUser The user parameter for pfnOnce.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
a438caaf732f7839dc66b4f8dad672527845a003vboxsyncRTDECL(int) RTOnceSlow(PRTONCE pOnce, PFNRTONCE pfnOnce, FNRTONCECLEANUP pfnCleanUp, void *pvUser);
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/**
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * Serializes execution of the pfnOnce function, making sure it's
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * executed exactly once and that nobody returns from RTOnce before
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * it has executed successfully.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @returns IPRT like status code returned by pfnOnce.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync *
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param pOnce Pointer to the execute once variable.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync * @param pfnOnce The function to executed once.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pvUser The user parameter for pfnOnce.
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync */
a438caaf732f7839dc66b4f8dad672527845a003vboxsyncDECLINLINE(int) RTOnce(PRTONCE pOnce, PFNRTONCE pfnOnce, void *pvUser)
a438caaf732f7839dc66b4f8dad672527845a003vboxsync{
a438caaf732f7839dc66b4f8dad672527845a003vboxsync int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
a438caaf732f7839dc66b4f8dad672527845a003vboxsync if (RT_LIKELY( iState == RTONCESTATE_DONE
a438caaf732f7839dc66b4f8dad672527845a003vboxsync || iState == RTONCESTATE_DONE_CREATING_SEM
a438caaf732f7839dc66b4f8dad672527845a003vboxsync || iState == RTONCESTATE_DONE_HAVE_SEM ))
a438caaf732f7839dc66b4f8dad672527845a003vboxsync return ASMAtomicUoReadS32(&pOnce->rc);
a438caaf732f7839dc66b4f8dad672527845a003vboxsync return RTOnceSlow(pOnce, pfnOnce, NULL, pvUser);
a438caaf732f7839dc66b4f8dad672527845a003vboxsync}
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync/**
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * Execute pfnOnce once and register a termination clean-up callback.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync *
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * Serializes execution of the pfnOnce function, making sure it's
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * executed exactly once and that nobody returns from RTOnce before
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * it has executed successfully.
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync *
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * @returns IPRT like status code returned by pfnOnce.
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync *
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * @param pOnce Pointer to the execute once variable.
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync * @param pfnOnce The function to executed once.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pfnCleanUp The function that will be doing the cleaning up.
a438caaf732f7839dc66b4f8dad672527845a003vboxsync * @param pvUser The user parameter for pfnOnce.
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync */
a438caaf732f7839dc66b4f8dad672527845a003vboxsyncDECLINLINE(int) RTOnceEx(PRTONCE pOnce, PFNRTONCE pfnOnce, PFNRTONCECLEANUP pfnCleanUp, void *pvUser)
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync{
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync int32_t iState = ASMAtomicUoReadS32(&pOnce->iState);
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync if (RT_LIKELY( iState == RTONCESTATE_DONE
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync || iState == RTONCESTATE_DONE_CREATING_SEM
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync || iState == RTONCESTATE_DONE_HAVE_SEM ))
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync return ASMAtomicUoReadS32(&pOnce->rc);
a438caaf732f7839dc66b4f8dad672527845a003vboxsync return RTOnceSlow(pOnce, pfnOnce, pfnCleanUp, pvUser);
e420253a89a6e07613f49cfd9a1db9e217039575vboxsync}
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
a438caaf732f7839dc66b4f8dad672527845a003vboxsync
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync/**
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync * Resets an execute once variable.
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync *
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync * The caller is responsible for making sure there are no concurrent accesses to
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync * the execute once variable.
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync *
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync * @param pOnce Pointer to the execute once variable.
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync */
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsyncRTDECL(void) RTOnceReset(PRTONCE pOnce);
6e59cff536e8de93f15fa1fb6b46d0ee01e06829vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync/** @} */
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
590bfe12ce22cd3716448fbb9f4dc51664bfe5e2vboxsyncRT_C_DECLS_END
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync#endif
33b0e5fd502b10034575ffa7c8a30c6816222ce2vboxsync