mirror of
https://github.com/reactos/reactos.git
synced 2025-04-21 04:37:15 +00:00
231 lines
7.4 KiB
C++
231 lines
7.4 KiB
C++
//
|
|
// onexit.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// The _onexit registry, which stores pointers to functions to be called when
|
|
// the program terminates or, when the CRT is statically linked into a DLL, when
|
|
// the DLL is unloaded.
|
|
//
|
|
// When the CRT is statically linked into an EXE or a DLL, this registry is used
|
|
// to hold the on-exit functions for the module (EXE or DLL) into which it is
|
|
// linked.
|
|
//
|
|
// When the dynamic CRT is used, this object is part of the AppCRT DLL and this
|
|
// registry stores the on-exit functions for the VCRuntime. If the EXE for the
|
|
// process uses this VCRuntime, then this registry also stores the on-exit
|
|
// functions for that EXE. If a DLL uses the dynamic CRT, then that DLL has its
|
|
// own registry, defined in the statically-linked VCStartup library.
|
|
//
|
|
#include <corecrt_internal.h>
|
|
|
|
|
|
|
|
// The global atexit and at_quick_exit registries
|
|
extern "C" { _onexit_table_t __acrt_atexit_table{}; }
|
|
extern "C" { _onexit_table_t __acrt_at_quick_exit_table{}; }
|
|
|
|
|
|
|
|
enum : size_t
|
|
{
|
|
initial_table_count = 32,
|
|
minimum_table_increment = 4,
|
|
maximum_table_increment = 512
|
|
};
|
|
|
|
|
|
|
|
// Registers a function to be executed on exit. This function modifies the global
|
|
// onexit table.
|
|
extern "C" int __cdecl _crt_atexit(_PVFV const function)
|
|
{
|
|
return _register_onexit_function(&__acrt_atexit_table, reinterpret_cast<_onexit_t>(function));
|
|
}
|
|
|
|
extern "C" int __cdecl _crt_at_quick_exit(_PVFV const function)
|
|
{
|
|
return _register_onexit_function(&__acrt_at_quick_exit_table, reinterpret_cast<_onexit_t>(function));
|
|
}
|
|
|
|
|
|
|
|
extern "C" int __cdecl _initialize_onexit_table(_onexit_table_t* const table)
|
|
{
|
|
if (!table)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// If the table has already been initialized, do not do anything. Note that
|
|
// this handles both the case where the table was value initialized and where
|
|
// the table was initialized with encoded null pointers.
|
|
if (table->_first != table->_end)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
_PVFV* const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
|
|
|
table->_first = encoded_nullptr;
|
|
table->_last = encoded_nullptr;
|
|
table->_end = encoded_nullptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// Appends the given 'function' to the given onexit 'table'. Returns 0 on
|
|
// success; returns -1 on failure. In general, failures are considered fatal
|
|
// in calling code.
|
|
extern "C" int __cdecl _register_onexit_function(_onexit_table_t* const table, _onexit_t const function)
|
|
{
|
|
return __acrt_lock_and_call(__acrt_select_exit_lock(), [&]
|
|
{
|
|
if (!table)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
_PVFV* first = __crt_fast_decode_pointer(table->_first);
|
|
_PVFV* last = __crt_fast_decode_pointer(table->_last);
|
|
_PVFV* end = __crt_fast_decode_pointer(table->_end);
|
|
|
|
// If there is no room for the new entry, reallocate a larger table:
|
|
if (last == end)
|
|
{
|
|
size_t const old_count = end - first;
|
|
|
|
size_t const increment = old_count > maximum_table_increment ? maximum_table_increment : old_count;
|
|
|
|
// First, try to double the capacity of the table:
|
|
size_t new_count = old_count + increment;
|
|
if (new_count == 0)
|
|
{
|
|
new_count = initial_table_count;
|
|
}
|
|
|
|
_PVFV* new_first = nullptr;
|
|
if (new_count >= old_count)
|
|
{
|
|
new_first = _recalloc_crt_t(_PVFV, first, new_count).detach();
|
|
}
|
|
|
|
// If that didn't work, try to allocate a smaller increment:
|
|
if (new_first == nullptr)
|
|
{
|
|
new_count = old_count + minimum_table_increment;
|
|
new_first = _recalloc_crt_t(_PVFV, first, new_count).detach();
|
|
}
|
|
|
|
if (new_first == nullptr)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
first = new_first;
|
|
last = new_first + old_count;
|
|
end = new_first + new_count;
|
|
|
|
// The "additional" storage obtained from recalloc is sero-initialized.
|
|
// The array holds encoded function pointers, so we need to fill the
|
|
// storage with encoded nullptrs:
|
|
_PVFV const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
|
for (auto it = last; it != end; ++it)
|
|
{
|
|
*it = encoded_nullptr;
|
|
}
|
|
}
|
|
|
|
*last++ = reinterpret_cast<_PVFV>(__crt_fast_encode_pointer(function));
|
|
|
|
table->_first = __crt_fast_encode_pointer(first);
|
|
table->_last = __crt_fast_encode_pointer(last);
|
|
table->_end = __crt_fast_encode_pointer(end);
|
|
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
|
|
|
|
// This function executes a table of _onexit()/atexit() functions. The
|
|
// terminators are executed in reverse order, to give the required LIFO
|
|
// execution order. If the table is uninitialized, this function has no
|
|
// effect. After executing the terminators, this function resets the table
|
|
// so that it is uninitialized. Returns 0 on success; -1 on failure.
|
|
extern "C" int __cdecl _execute_onexit_table(_onexit_table_t* const table)
|
|
{
|
|
return __acrt_lock_and_call(__acrt_select_exit_lock(), [&]
|
|
{
|
|
if (!table)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
_PVFV* first = __crt_fast_decode_pointer(table->_first);
|
|
_PVFV* last = __crt_fast_decode_pointer(table->_last);
|
|
if (!first || first == reinterpret_cast<_PVFV*>(-1))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// This loop calls through caller-provided function pointers. We must
|
|
// save and reset the global state mode before calling them, to maintain
|
|
// proper mode nesting. (These calls to caller-provided function pointers
|
|
// are the only non-trivial calls, so we can do this once for the entire
|
|
// loop.)
|
|
{
|
|
__crt_state_management::scoped_global_state_reset saved_state;
|
|
|
|
_PVFV const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
|
|
|
_PVFV* saved_first = first;
|
|
_PVFV* saved_last = last;
|
|
for (;;)
|
|
{
|
|
// Find the last valid function pointer to call:
|
|
while (--last >= first && *last == encoded_nullptr)
|
|
{
|
|
// Keep going backwards
|
|
}
|
|
|
|
if (last < first)
|
|
{
|
|
// There are no more valid entries in the list; we are done:
|
|
break;
|
|
}
|
|
|
|
// Store the function pointer and mark it as visited in the list:
|
|
_PVFV const function = __crt_fast_decode_pointer(*last);
|
|
*last = encoded_nullptr;
|
|
|
|
function();
|
|
|
|
_PVFV* const new_first = __crt_fast_decode_pointer(table->_first);
|
|
_PVFV* const new_last = __crt_fast_decode_pointer(table->_last);
|
|
|
|
// Reset iteration if either the begin or end pointer has changed:
|
|
if (new_first != saved_first || new_last != saved_last)
|
|
{
|
|
first = saved_first = new_first;
|
|
last = saved_last = new_last;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (first != reinterpret_cast<_PVFV*>(-1))
|
|
{
|
|
_free_crt(first);
|
|
}
|
|
|
|
_PVFV* const encoded_nullptr = __crt_fast_encode_pointer(nullptr);
|
|
|
|
table->_first = encoded_nullptr;
|
|
table->_last = encoded_nullptr;
|
|
table->_end = encoded_nullptr;
|
|
|
|
return 0;
|
|
});
|
|
}
|