Index: /trunk/src/VBox/Runtime/common/misc/term.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/misc/term.cpp	(revision 19898)
+++ /trunk/src/VBox/Runtime/common/misc/term.cpp	(revision 19898)
@@ -0,0 +1,242 @@
+/* $Id$ */
+/** @file
+ * IPRT - Common Termination Code.
+ */
+
+/*
+ * Copyright (C) 2009 Sun Microsystems, Inc.
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
+ * Clara, CA 95054 USA or visit http://www.sun.com if you need
+ * additional information or have any questions.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <iprt/initterm.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/** Pointer to a termination callback record. */
+typedef struct RTTERMCALLBACKREC *PRTTERMCALLBACKREC;
+/**
+ * Termination callback record.
+ */
+typedef struct RTTERMCALLBACKREC
+{
+    /** Pointer to the next record. */
+    PRTTERMCALLBACKREC  pNext;
+    /** Pointer to the callback. */
+    PFNRTTERMCALLBACK   pfnCallback;
+    /** The user argument. */
+    void               *pvUser;
+} RTTERMCALLBACKREC;
+
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+/** Execute once construct protecting lazy callback initialization. */
+static RTONCE               g_InitTermCallbacksOnce = RTONCE_INITIALIZER;
+/** Mutex protecting the callback globals. */
+static RTSEMFASTMUTEX       g_hFastMutex = NIL_RTSEMFASTMUTEX;
+/** Number of registered callbacks.  */
+static uint32_t             g_cCallbacks = 0;
+/** The callback head. */
+static PRTTERMCALLBACKREC   g_pCallbackHead = NULL;
+
+
+
+/**
+ * Initializes the globals.
+ *
+ * @returns IPRT status code
+ * @param   pvUser1             Ignored.
+ * @param   pvUser2             Ignored.
+ */
+static DECLCALLBACK(int32_t) rtTermInitOnce(void *pvUser1, void *pvUser2)
+{
+    RTSEMFASTMUTEX  hFastMutex;
+    int             rc;
+
+    Assert(!g_cCallbacks);
+    Assert(!g_pCallbackHead);
+    Assert(g_hFastMutex == NIL_RTSEMFASTMUTEX);
+
+    rc = RTSemFastMutexCreate(&hFastMutex);
+    if (RT_SUCCESS(rc))
+        g_hFastMutex = hFastMutex;
+
+    NOREF(pvUser1);
+    NOREF(pvUser2);
+
+    return rc;
+}
+
+
+
+RTDECL(int) RTTermRegisterCallback(PFNRTTERMCALLBACK pfnCallback, void *pvUser)
+{
+    int                 rc;
+    PRTTERMCALLBACKREC  pNew;
+
+    /*
+     * Validation and lazy init.
+     */
+    AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+
+    rc = RTOnce(&g_InitTermCallbacksOnce, rtTermInitOnce, NULL, NULL);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Allocate and initialize a new callback record.
+     */
+    pNew = (PRTTERMCALLBACKREC)RTMemAlloc(sizeof(*pNew));
+    if (!pNew)
+        return VERR_NO_MEMORY;
+    pNew->pfnCallback = pfnCallback;
+    pNew->pvUser      = pvUser;
+
+    /*
+     * Insert into the list.
+     */
+    rc = RTSemFastMutexRequest(g_hFastMutex);
+    if (RT_SUCCESS(rc))
+    {
+        g_cCallbacks++;
+        pNew->pNext = g_pCallbackHead;
+        g_pCallbackHead = pNew;
+
+        RTSemFastMutexRelease(g_hFastMutex);
+    }
+    else
+        RTMemFree(pNew);
+
+    return rc;
+}
+
+
+RTDECL(int) RTTermDeregisterCallback(PFNRTTERMCALLBACK pfnCallback, void *pvUser)
+{
+    /*
+     * g_hFastMutex will be NIL if we're not initialized.
+     */
+    int rc;
+    RTSEMFASTMUTEX hFastMutex = g_hFastMutex;
+    if (hFastMutex == NIL_RTSEMFASTMUTEX)
+        return VERR_NOT_FOUND;
+
+    rc = RTSemFastMutexRequest(hFastMutex);
+    if (RT_SUCCESS(rc))
+    {
+
+        /*
+         * Search for the specified pfnCallback/pvUser pair.
+         */
+        PRTTERMCALLBACKREC pPrev = NULL;
+        PRTTERMCALLBACKREC pCur  = g_pCallbackHead;
+        while (pCur)
+        {
+            if (    pCur->pfnCallback == pfnCallback
+                &&  pCur->pvUser      == pvUser)
+            {
+                if (pPrev)
+                    pPrev->pNext = pCur->pNext;
+                else
+                    g_pCallbackHead = pCur->pNext;
+                g_cCallbacks--;
+                RTSemFastMutexRelease(hFastMutex);
+
+                pCur->pfnCallback = NULL;
+                RTMemFree(pCur);
+                return VINF_SUCCESS;
+            }
+
+            /* next */
+            pPrev = pCur;
+            pCur  = pCur->pNext;
+        }
+
+        RTSemFastMutexRelease(hFastMutex);
+        rc = VERR_NOT_FOUND;
+    }
+
+    return rc;
+}
+
+
+RTDECL(void) RTTermRunCallbacks(RTTERMREASON enmReason, int32_t iStatus)
+{
+    RTSEMFASTMUTEX  hFastMutex;
+    Assert(   enmReason == RTTERMREASON_EXIT
+           || enmReason == RTTERMREASON_ABEND
+           || enmReason == RTTERMREASON_SIGNAL
+           || enmReason == RTTERMREASON_UNLOAD);
+
+    /*
+     * Run the callback list. This is a bit paranoid in order to guard aginst
+     * recursive calls to RTTermRunCallbacks.
+     */
+    while (g_hFastMutex != NIL_RTSEMFASTMUTEX)
+    {
+        PRTTERMCALLBACKREC  pCur;
+        RTTERMCALLBACKREC   CurCopy;
+        int                 rc;
+
+        /* Unlink the head of the chain. */
+        rc = RTSemFastMutexRequest(g_hFastMutex);
+        AssertRCReturnVoid(rc);
+        pCur = g_pCallbackHead;
+        if (pCur)
+        {
+            g_pCallbackHead = pCur->pNext;
+            g_cCallbacks--;
+        }
+        RTSemFastMutexRelease(g_hFastMutex);
+        if (!pCur)
+            break;
+
+        /* Copy and free it. */
+        CurCopy = *pCur;
+        RTMemFree(pCur);
+
+        /* Make the call. */
+        CurCopy.pfnCallback(enmReason, iStatus, CurCopy.pvUser);
+    }
+
+    /*
+     * Free the lock.
+     */
+    ASMAtomicXchgHandle(&g_hFastMutex, NIL_RTSEMFASTMUTEX, &hFastMutex);
+    RTSemFastMutexDestroy(hFastMutex);
+    RTOnceReset(&g_InitTermCallbacksOnce); /* for the testcase */
+}
+
