diff --git a/src/system/libroot/posix/stdlib/Jamfile b/src/system/libroot/posix/stdlib/Jamfile index 388f1368e4..f473c95501 100644 --- a/src/system/libroot/posix/stdlib/Jamfile +++ b/src/system/libroot/posix/stdlib/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src system libroot posix stdlib ; UsePrivateHeaders drivers libroot runtime_loader shared ; +UsePrivateHeaders kernel ; # for UsePrivateSystemHeaders ; MergeObject posix_stdlib.o : diff --git a/src/system/libroot/posix/stdlib/exit.cpp b/src/system/libroot/posix/stdlib/exit.cpp index 8e4080abae..ccd3a059da 100644 --- a/src/system/libroot/posix/stdlib/exit.cpp +++ b/src/system/libroot/posix/stdlib/exit.cpp @@ -1,19 +1,25 @@ /* - * Copyright 2004-2009, Haiku Inc. All rights reserved. + * Copyright 2004-2010, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Author(s): * Daniel Reinhold, danielre@users.sf.net * Axel Dörfler, axeld@pinc-software.de + * Ingo Weinhold, ingo_weinhold@gmx.de */ #include #include +#include #include #include -#include + +#include + +#include +#include #include #include @@ -24,28 +30,237 @@ extern "C" void _IO_cleanup(void); extern "C" void _thread_do_exit_work(void); -struct exit_stack_info { - void (*exit_stack[ATEXIT_MAX])(void); - int32 stack_size; - recursive_lock lock; + +struct AtExitInfoBlock; + +struct AtExitInfo : SinglyLinkedListLinkImpl { + AtExitInfoBlock* block; + void (*hook)(void*); + void* data; + void* dsoHandle; +}; + +typedef SinglyLinkedList AtExitInfoList; + + +struct AtExitInfoBlock : DoublyLinkedListLinkImpl { + bool IsEmpty() const + { + return fFirstUnused == ATEXIT_MAX && fFreeList.IsEmpty(); + } + + AtExitInfo* AllocateInfo() + { + // Handle the likely case -- the block is not fully used yet -- first. + // Grab the next info from the array. + if (fFirstUnused < ATEXIT_MAX) { + AtExitInfo* info = &fInfos[fFirstUnused++]; + info->block = this; + return info; + } + + // The block was fully used, but there might be infos in the free list. + return fFreeList.RemoveHead(); + } + + void FreeInfo(AtExitInfo* info) + { + fFreeList.Add(info); + } + +private: + AtExitInfo fInfos[ATEXIT_MAX]; + uint32 fFirstUnused; + AtExitInfoList fFreeList; +}; + +typedef DoublyLinkedList AtExitInfoBlockList; + + +struct DSOPredicate { + DSOPredicate(void* dsoHandle) + : + fDSOHandle(dsoHandle) + { + } + + inline bool operator()(const AtExitInfo* info) const + { + return info->dsoHandle == fDSOHandle; + } + +private: + void* fDSOHandle; }; -static struct exit_stack_info sExitStackInfo - = { {}, 0, RECURSIVE_LOCK_INITIALIZER("exit stack lock") }; +struct AddressRangePredicate { + AddressRangePredicate(addr_t start, size_t size) + : + fStart(start), + fEnd(start + size - 1) + { + } + + inline bool operator()(const AtExitInfo* info) const + { + addr_t address = (addr_t)info->hook; + return info->dsoHandle == NULL && address >= fStart && address <= fEnd; + // Note: We ignore hooks associated with an image (the same one + // likely), since those will be called anyway when __cxa_finalize() + // is invoked for that image. + } + +private: + addr_t fStart; + addr_t fEnd; +}; + + +static AtExitInfoBlock sInitialAtExistInfoBlock; +static AtExitInfoBlockList sAtExitInfoBlocks; +static AtExitInfoList sAtExitInfoStack; +static recursive_lock sAtExitLock = RECURSIVE_LOCK_INITIALIZER("at exit lock"); static void inline _exit_stack_lock() { - recursive_lock_lock(&sExitStackInfo.lock); + recursive_lock_lock(&sAtExitLock); } static void inline _exit_stack_unlock() { - recursive_lock_unlock(&sExitStackInfo.lock); + recursive_lock_unlock(&sAtExitLock); +} + + +template +static void +call_exit_hooks(const Predicate& predicate) +{ + _exit_stack_lock(); + + AtExitInfo* previousInfo = NULL; + AtExitInfo* info = sAtExitInfoStack.Head(); + while (info != NULL) { + AtExitInfo* nextInfo = sAtExitInfoStack.GetNext(info); + + if (predicate(info)) { + // remove info from stack + sAtExitInfoStack.Remove(previousInfo, info); + + // call the hook + info->hook(info->data); + + // return the info to the block + if (info->block->IsEmpty()) + sAtExitInfoBlocks.Add(info->block); + + info->block->FreeInfo(info); + } else + previousInfo = info; + + info = nextInfo; + } + + _exit_stack_unlock(); +} + + +// #pragma mark -- C++ ABI + + +/*! exit() hook registration function (mandated by the C++ ABI). + \param hook Hook function to be called. + \param data The data to be passed to the hook. + \param dsoHandle If non-NULL, the hook is associated with the respective + loaded shared object (aka image) -- the hook will be called either on + exit() or earlier when the shared object is unloaded. If NULL, the hook + is called only on exit(). + \return \c 0 on success, another value on failure. + */ +extern "C" int +__cxa_atexit(void (*hook)(void*), void* data, void* dsoHandle) +{ + if (hook == NULL) + return -1; + + _exit_stack_lock(); + + // We need to allocate an info. Get an info block from which to allocate. + AtExitInfoBlock* block = sAtExitInfoBlocks.Head(); + if (block == NULL) { + // might be the first call -- check the initial block + if (!sInitialAtExistInfoBlock.IsEmpty()) { + block = &sInitialAtExistInfoBlock; + } else { + // no empty block -- let's hope libroot is initialized sufficiently + // for the heap to work + block = new(std::nothrow) AtExitInfoBlock; + if (block == NULL) { + _exit_stack_unlock(); + return -1; + } + } + + sAtExitInfoBlocks.Add(block); + } + + // allocate the info + AtExitInfo* info = block->AllocateInfo(); + + // If the block is empty now, remove it from the list. + if (block->IsEmpty()) + sAtExitInfoBlocks.Remove(block); + + // init and add the info + info->hook = hook; + info->data = data; + info->dsoHandle = dsoHandle; + + sAtExitInfoStack.Add(info); + + _exit_stack_unlock(); + + return 0; +} + + +/*! exit() hook calling function (mandated by the C++ ABI). + + Calls the exit() hooks associated with a certain shared object handle, + respectively calls all hooks when a NULL handle is given. All called + hooks are removed. + + \param dsoHandle If non-NULL, all hooks associated with that handle are + called. If NULL, all hooks are called. + */ +extern "C" void +__cxa_finalize(void* dsoHandle) +{ + if (dsoHandle == NULL) { + // call all hooks + _exit_stack_lock(); + + while (AtExitInfo* info = sAtExitInfoStack.RemoveHead()) { + // call the hook + info->hook(info->data); + + // return the info to the block + if (info->block->IsEmpty()) + sAtExitInfoBlocks.Add(info->block); + + info->block->FreeInfo(info); + } + + _exit_stack_unlock(); + } else { + // call all hooks for the respective DSO + call_exit_hooks(DSOPredicate(dsoHandle)); + } } @@ -55,32 +270,7 @@ _exit_stack_unlock() void _call_atexit_hooks_for_range(addr_t start, addr_t size) { - int32 index; - int32 insertIndex = -1; - - _exit_stack_lock(); - for (index = sExitStackInfo.stack_size - 1; index >= 0; index--) { - addr_t function = (addr_t)sExitStackInfo.exit_stack[index]; - if (function >= start && function < start + size) { - (*sExitStackInfo.exit_stack[index])(); - sExitStackInfo.exit_stack[index] = NULL; - insertIndex = index; - } - } - - if (insertIndex >= 0) { - for (index = insertIndex + 1; - index < sExitStackInfo.stack_size; - index++) { - if (sExitStackInfo.exit_stack[index] != NULL) { - sExitStackInfo.exit_stack[insertIndex++] - = sExitStackInfo.exit_stack[index]; - } - } - sExitStackInfo.stack_size = insertIndex; - } - - _exit_stack_unlock(); + call_exit_hooks(AddressRangePredicate(start, size)); } @@ -100,17 +290,7 @@ abort() int atexit(void (*func)(void)) { - // push the function pointer onto the exit stack - int result = -1; - _exit_stack_lock(); - - if (sExitStackInfo.stack_size < ATEXIT_MAX) { - sExitStackInfo.exit_stack[sExitStackInfo.stack_size++] = func; - result = 0; - } - - _exit_stack_unlock(); - return result; + return __cxa_atexit((void (*)(void*))func, NULL, NULL); } @@ -121,10 +301,7 @@ exit(int status) _thread_do_exit_work(); // unwind the exit stack, calling the registered functions - _exit_stack_lock(); - while (--sExitStackInfo.stack_size >= 0) - (*sExitStackInfo.exit_stack[sExitStackInfo.stack_size])(); - _exit_stack_unlock(); + __cxa_finalize(NULL); // close all open files _IO_cleanup(); @@ -134,4 +311,3 @@ exit(int status) // exit with status code _kern_exit_team(status); } -