Reactos
at master 231 lines 7.6 kB view raw
1// 2// onexit.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// The _onexit registry, which stores pointers to functions to be called when 7// the program terminates or, when the CRT is statically linked into a DLL, when 8// the DLL is unloaded. 9// 10// When the CRT is statically linked into an EXE or a DLL, this registry is used 11// to hold the on-exit functions for the module (EXE or DLL) into which it is 12// linked. 13// 14// When the dynamic CRT is used, this object is part of the AppCRT DLL and this 15// registry stores the on-exit functions for the VCRuntime. If the EXE for the 16// process uses this VCRuntime, then this registry also stores the on-exit 17// functions for that EXE. If a DLL uses the dynamic CRT, then that DLL has its 18// own registry, defined in the statically-linked VCStartup library. 19// 20#include <corecrt_internal.h> 21 22 23 24// The global atexit and at_quick_exit registries 25extern "C" { _onexit_table_t __acrt_atexit_table{}; } 26extern "C" { _onexit_table_t __acrt_at_quick_exit_table{}; } 27 28 29 30enum : size_t 31{ 32 initial_table_count = 32, 33 minimum_table_increment = 4, 34 maximum_table_increment = 512 35}; 36 37 38 39// Registers a function to be executed on exit. This function modifies the global 40// onexit table. 41extern "C" int __cdecl _crt_atexit(_PVFV const function) 42{ 43 return _register_onexit_function(&__acrt_atexit_table, reinterpret_cast<_onexit_t>(function)); 44} 45 46extern "C" int __cdecl _crt_at_quick_exit(_PVFV const function) 47{ 48 return _register_onexit_function(&__acrt_at_quick_exit_table, reinterpret_cast<_onexit_t>(function)); 49} 50 51 52 53extern "C" int __cdecl _initialize_onexit_table(_onexit_table_t* const table) 54{ 55 if (!table) 56 { 57 return -1; 58 } 59 60 // If the table has already been initialized, do not do anything. Note that 61 // this handles both the case where the table was value initialized and where 62 // the table was initialized with encoded null pointers. 63 if (table->_first != table->_end) 64 { 65 return 0; 66 } 67 68 _PVFV* const encoded_nullptr = __crt_fast_encode_pointer(nullptr); 69 70 table->_first = encoded_nullptr; 71 table->_last = encoded_nullptr; 72 table->_end = encoded_nullptr; 73 74 return 0; 75} 76 77 78 79// Appends the given 'function' to the given onexit 'table'. Returns 0 on 80// success; returns -1 on failure. In general, failures are considered fatal 81// in calling code. 82extern "C" int __cdecl _register_onexit_function(_onexit_table_t* const table, _onexit_t const function) 83{ 84 return __acrt_lock_and_call(__acrt_select_exit_lock(), [&] 85 { 86 if (!table) 87 { 88 return -1; 89 } 90 91 _PVFV* first = __crt_fast_decode_pointer(table->_first); 92 _PVFV* last = __crt_fast_decode_pointer(table->_last); 93 _PVFV* end = __crt_fast_decode_pointer(table->_end); 94 95 // If there is no room for the new entry, reallocate a larger table: 96 if (last == end) 97 { 98 size_t const old_count = end - first; 99 100 size_t const increment = old_count > maximum_table_increment ? maximum_table_increment : old_count; 101 102 // First, try to double the capacity of the table: 103 size_t new_count = old_count + increment; 104 if (new_count == 0) 105 { 106 new_count = initial_table_count; 107 } 108 109 _PVFV* new_first = nullptr; 110 if (new_count >= old_count) 111 { 112 new_first = _recalloc_crt_t(_PVFV, first, new_count).detach(); 113 } 114 115 // If that didn't work, try to allocate a smaller increment: 116 if (new_first == nullptr) 117 { 118 new_count = old_count + minimum_table_increment; 119 new_first = _recalloc_crt_t(_PVFV, first, new_count).detach(); 120 } 121 122 if (new_first == nullptr) 123 { 124 return -1; 125 } 126 127 first = new_first; 128 last = new_first + old_count; 129 end = new_first + new_count; 130 131 // The "additional" storage obtained from recalloc is sero-initialized. 132 // The array holds encoded function pointers, so we need to fill the 133 // storage with encoded nullptrs: 134 _PVFV const encoded_nullptr = __crt_fast_encode_pointer(nullptr); 135 for (auto it = last; it != end; ++it) 136 { 137 *it = encoded_nullptr; 138 } 139 } 140 141 *last++ = reinterpret_cast<_PVFV>(__crt_fast_encode_pointer(function)); 142 143 table->_first = __crt_fast_encode_pointer(first); 144 table->_last = __crt_fast_encode_pointer(last); 145 table->_end = __crt_fast_encode_pointer(end); 146 147 return 0; 148 }); 149} 150 151 152 153// This function executes a table of _onexit()/atexit() functions. The 154// terminators are executed in reverse order, to give the required LIFO 155// execution order. If the table is uninitialized, this function has no 156// effect. After executing the terminators, this function resets the table 157// so that it is uninitialized. Returns 0 on success; -1 on failure. 158extern "C" int __cdecl _execute_onexit_table(_onexit_table_t* const table) 159{ 160 return __acrt_lock_and_call(__acrt_select_exit_lock(), [&] 161 { 162 if (!table) 163 { 164 return -1; 165 } 166 167 _PVFV* first = __crt_fast_decode_pointer(table->_first); 168 _PVFV* last = __crt_fast_decode_pointer(table->_last); 169 if (!first || first == reinterpret_cast<_PVFV*>(-1)) 170 { 171 return 0; 172 } 173 174 // This loop calls through caller-provided function pointers. We must 175 // save and reset the global state mode before calling them, to maintain 176 // proper mode nesting. (These calls to caller-provided function pointers 177 // are the only non-trivial calls, so we can do this once for the entire 178 // loop.) 179 { 180 __crt_state_management::scoped_global_state_reset saved_state; 181 182 _PVFV const encoded_nullptr = __crt_fast_encode_pointer(nullptr); 183 184 _PVFV* saved_first = first; 185 _PVFV* saved_last = last; 186 for (;;) 187 { 188 // Find the last valid function pointer to call: 189 while (--last >= first && *last == encoded_nullptr) 190 { 191 // Keep going backwards 192 } 193 194 if (last < first) 195 { 196 // There are no more valid entries in the list; we are done: 197 break; 198 } 199 200 // Store the function pointer and mark it as visited in the list: 201 _PVFV const function = __crt_fast_decode_pointer(*last); 202 *last = encoded_nullptr; 203 204 function(); 205 206 _PVFV* const new_first = __crt_fast_decode_pointer(table->_first); 207 _PVFV* const new_last = __crt_fast_decode_pointer(table->_last); 208 209 // Reset iteration if either the begin or end pointer has changed: 210 if (new_first != saved_first || new_last != saved_last) 211 { 212 first = saved_first = new_first; 213 last = saved_last = new_last; 214 } 215 } 216 } 217 218 if (first != reinterpret_cast<_PVFV*>(-1)) 219 { 220 _free_crt(first); 221 } 222 223 _PVFV* const encoded_nullptr = __crt_fast_encode_pointer(nullptr); 224 225 table->_first = encoded_nullptr; 226 table->_last = encoded_nullptr; 227 table->_end = encoded_nullptr; 228 229 return 0; 230 }); 231}