Index: /trunk/include/iprt/initterm.h
===================================================================
--- /trunk/include/iprt/initterm.h	(revision 19896)
+++ /trunk/include/iprt/initterm.h	(revision 19897)
@@ -4,5 +4,5 @@
 
 /*
- * Copyright (C) 2006-2007 Sun Microsystems, Inc.
+ * Copyright (C) 2006-2009 Sun Microsystems, Inc.
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -134,4 +134,85 @@
 
 
+/**
+ * Termination reason.
+ */
+typedef enum RTTERMREASON
+{
+    /** Normal exit. iStatus contains the exit code. */
+    RTTERMREASON_EXIT = 1,
+    /** Any abnormal exit. iStatus is 0 and has no meaning. */
+    RTTERMREASON_ABEND,
+    /** Killed by a signal. The iStatus contains the signal number. */
+    RTTERMREASON_SIGNAL,
+    /** The IPRT module is being unloaded. iStatus is 0 and has no meaning. */
+    RTTERMREASON_UNLOAD
+} RTTERMREASON;
+
+/** Whether lazy clean up is Okay or not.
+ * When the process is exiting, it is a waste of time to for instance free heap
+ * memory or close open files. OTOH, when the runtime is unloaded from the
+ * process, it is important to release absolutely all resources to prevent
+ * resource leaks. */
+#define RTTERMREASON_IS_LAZY_CLEANUP_OK(enmReason)  ((enmReason) != RTTERMREASON_UNLOAD)
+
+
+/**
+ * IPRT termination callback function.
+ *
+ * @param   enmReason           The cause of the termination.
+ * @param   iStatus             The meaning of this depends on enmReason.
+ * @param   pvUser              User argument passed to RTTermRegisterCallback.
+ */
+typedef DECLCALLBACK(void) FNRTTERMCALLBACK(RTTERMREASON enmReason, int32_t iStatus, void *pvUser);
+/** Pointer to an IPRT termination callback function. */
+typedef FNRTTERMCALLBACK *PFNRTTERMCALLBACK;
+
+
+/**
+ * Registers a termination callback.
+ *
+ * This is intended for performing clean up during IPRT termination. Frequently
+ * paired with lazy initialization thru RTOnce.
+ *
+ * The callbacks are called in LIFO order.
+ *
+ * @returns IPRT status code.
+ *
+ * @param   pfnCallback         The callback function.
+ * @param   pvUser              The user argument for the callback.
+ *
+ * @remarks May need to acquire a fast mutex or critical section, so use with
+ *          some care in ring-0 context.
+ *
+ * @remarks Be very careful using this from code that may be unloaded before
+ *          IPRT terminates. Unlike some atexit and on_exit implementations,
+ *          IPRT will not automatically unregister callbacks when a module gets
+ *          unloaded.
+ */
+RTDECL(int) RTTermRegisterCallback(PFNRTTERMCALLBACK pfnCallback, void *pvUser);
+
+/**
+ * Deregister a termination callback.
+ *
+ * @returns VINF_SUCCESS if found, VERR_NOT_FOUND if the callback/pvUser pair
+ *          wasn't found.
+ *
+ * @param   pfnCallback         The callback function.
+ * @param   pvUser              The user argument for the callback.
+ */
+RTDECL(int) RTTermDeregisterCallback(PFNRTTERMCALLBACK pfnCallback, void *pvUser);
+
+/**
+ * Runs the termination callback queue.
+ *
+ * Normally called by an internal IPRT termination function, but may also be
+ * called by external code immediately prior to terminating IPRT if it is in a
+ * better position to state the termination reason and/or status.
+ *
+ * @param   enmReason           The reason why it's called.
+ * @param   iStatus             The associated exit status or signal number.
+ */
+RTDECL(void) RTTermRunCallbacks(RTTERMREASON enmReason, int32_t iStatus);
+
 /** @} */
 
Index: /trunk/src/VBox/Runtime/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/Makefile.kmk	(revision 19896)
+++ /trunk/src/VBox/Runtime/Makefile.kmk	(revision 19897)
@@ -228,4 +228,5 @@
 	common/misc/thread.cpp \
 	common/misc/zip.cpp \
+	common/misc/term.cpp \
 	common/rand/rand.cpp \
 	common/rand/randadv.cpp \
@@ -972,4 +973,5 @@
 	common/misc/sanity-cpp.cpp \
 	common/misc/RTAssertMsg2.cpp \
+	common/misc/term.cpp \
 	common/string/strformat.cpp \
 	common/string/strformatrt.cpp \
@@ -1072,4 +1074,5 @@
 	common/misc/sanity-c.c \
 	common/misc/sanity-cpp.cpp \
+	common/misc/term.cpp \
 	common/rand/rand.cpp \
 	common/rand/randadv.cpp \
Index: /trunk/src/VBox/Runtime/testcase/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/testcase/Makefile.kmk	(revision 19896)
+++ /trunk/src/VBox/Runtime/testcase/Makefile.kmk	(revision 19897)
@@ -87,4 +87,5 @@
 	tstStrToNum \
 	tstSystemQueryOsInfo \
+	tstTermCallbacks \
 	tstThread-1 \
 	tstTime \
@@ -321,4 +322,6 @@
 tstSystemQueryOsInfo_SOURCES = tstSystemQueryOsInfo.cpp
 
+tstTermCallbacks_SOURCES = tstTermCallbacks.cpp
+
 tstThread-1_SOURCES = tstThread-1.cpp
 
Index: /trunk/src/VBox/Runtime/testcase/tstTermCallbacks.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstTermCallbacks.cpp	(revision 19897)
+++ /trunk/src/VBox/Runtime/testcase/tstTermCallbacks.cpp	(revision 19897)
@@ -0,0 +1,156 @@
+/* $Id$ */
+/** @file
+ * IPRT Testcase - Termination Callbacks.
+ */
+
+/*
+ * 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/test.h>
+#include <iprt/stream.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+static uint32_t g_cCalls;
+static uint32_t g_fCalled;
+
+
+static DECLCALLBACK(void) tstTermCallback0(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+    RTTESTI_CHECK(pvUser == (void *)0);
+    g_cCalls++;
+    g_fCalled |= RT_BIT_32(0);
+}
+
+
+static DECLCALLBACK(void) tstTermCallback1(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+    RTTESTI_CHECK(pvUser == (void *)1);
+    g_cCalls++;
+    g_fCalled |= RT_BIT_32(1);
+}
+
+
+static DECLCALLBACK(void) tstTermCallback2(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+    RTTESTI_CHECK(pvUser == (void *)2);
+    g_cCalls++;
+    g_fCalled |= RT_BIT_32(2);
+}
+
+
+static DECLCALLBACK(void) tstTermCallback3(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+    RTTESTI_CHECK(pvUser == (void *)3);
+    g_cCalls++;
+    g_fCalled |= RT_BIT_32(3);
+}
+
+
+int main(int argc, char **argv)
+{
+    int rc;
+    RTTEST hTest;
+    if (    RT_FAILURE(rc = RTR3Init())
+        ||  RT_FAILURE(rc = RTTestCreate("tstTermCallback", &hTest)))
+    {
+        RTPrintf("tstTermCallbacks: RTR3Init or RTTestCreate failed: %Rrc\n", rc);
+        return 1;
+    }
+    RTTestBanner(hTest);
+
+    /*
+     * Just some basics.
+     */
+    RTTestSub(hTest, "Uninitialized");
+    RTTESTI_CHECK_RC(RTTermDeregisterCallback(tstTermCallback1, (void *)1), VERR_NOT_FOUND);
+    RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+
+    RTTestSub(hTest, "One callback");
+    g_cCalls = g_fCalled = 0;
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)0), VINF_SUCCESS);
+    RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+    RTTESTI_CHECK(g_cCalls == 1);
+    RTTESTI_CHECK(g_fCalled == RT_BIT_32(0));
+
+    RTTestSub(hTest, "Two callbacks");
+    g_cCalls = g_fCalled = 0;
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)0), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback1, (void *)1), VINF_SUCCESS);
+    RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+    RTTESTI_CHECK(g_cCalls == 2);
+    RTTESTI_CHECK(g_fCalled == (RT_BIT_32(0) | RT_BIT_32(1)));
+
+    RTTestSub(hTest, "Three callbacks");
+    g_cCalls = g_fCalled = 0;
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)0), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback1, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback2, (void *)2), VINF_SUCCESS);
+    RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+    RTTESTI_CHECK(g_cCalls == 3);
+    RTTESTI_CHECK(g_fCalled == (RT_BIT_32(0) | RT_BIT_32(1) | RT_BIT_32(2)));
+
+    RTTestSub(hTest, "Four callbacks");
+    g_cCalls = g_fCalled = 0;
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)0), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback1, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback2, (void *)2), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback3, (void *)3), VINF_SUCCESS);
+    RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+    RTTESTI_CHECK(g_cCalls == 4);
+    RTTESTI_CHECK(g_fCalled == (RT_BIT_32(0) | RT_BIT_32(1) | RT_BIT_32(2) | RT_BIT_32(3)));
+
+    RTTestSub(hTest, "Callback deregistration");
+    g_cCalls = g_fCalled = 0;
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback0, (void *)0), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback1, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermRegisterCallback(tstTermCallback1, (void *)0), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermDeregisterCallback(tstTermCallback0, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermDeregisterCallback(tstTermCallback0, (void *)1), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermDeregisterCallback(tstTermCallback0, (void *)1), VERR_NOT_FOUND);
+    RTTESTI_CHECK_RC(RTTermDeregisterCallback(tstTermCallback1, (void *)0), VINF_SUCCESS);
+    RTTESTI_CHECK_RC(RTTermDeregisterCallback(tstTermCallback1, (void *)0), VERR_NOT_FOUND);
+    RTTermRunCallbacks(RTTERMREASON_UNLOAD, 0);
+    RTTESTI_CHECK(g_cCalls == 2);
+    RTTESTI_CHECK(g_fCalled == (RT_BIT_32(0) | RT_BIT_32(1)));
+
+    /*
+     * Summary.
+     */
+    return RTTestSummaryAndDestroy(hTest);
+}
+
+
