For that to work, MSVCRT__register_onexit_function and MSVCRT__execute_onexit_table have to available all the time, not only when _MSVCR_VER>=140
tests based on code by Piotr Caban
v2: Create exit_event2 in test function, so it exists in child process
v3: Keep exitlock Keep _MSVCR_VER>=140 functions _MSVCR_VER>=140
Signed-off-by: Fabian Maurer dark.shadow4@web.de --- dlls/msvcrt/exit.c | 195 ++++++++++++++++++------------------- dlls/ucrtbase/tests/misc.c | 74 +++++++++++++- 2 files changed, 166 insertions(+), 103 deletions(-)
diff --git a/dlls/msvcrt/exit.c b/dlls/msvcrt/exit.c index 7e1805569c4..602a80a0513 100644 --- a/dlls/msvcrt/exit.c +++ b/dlls/msvcrt/exit.c @@ -29,9 +29,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); #define LOCK_EXIT _mlock(_EXIT_LOCK1) #define UNLOCK_EXIT _munlock(_EXIT_LOCK1)
-static MSVCRT__onexit_t *MSVCRT_atexit_table = NULL; -static int MSVCRT_atexit_table_size = 0; -static int MSVCRT_atexit_registered = 0; /* Points to free slot */ static MSVCRT_purecall_handler purecall_handler = NULL;
typedef struct MSVCRT__onexit_table_t @@ -41,6 +38,8 @@ typedef struct MSVCRT__onexit_table_t MSVCRT__onexit_t *_end; } MSVCRT__onexit_table_t;
+static MSVCRT__onexit_table_t MSVCRT_atexit_table; + typedef void (__stdcall *_tls_callback_type)(void*,ULONG,void*); static _tls_callback_type tls_atexit_callback;
@@ -61,21 +60,97 @@ static int MSVCRT_error_mode = MSVCRT__OUT_TO_DEFAULT;
void (*CDECL _aexit_rtn)(int) = MSVCRT__exit;
+static int __MSVCRT__initialize_onexit_table(MSVCRT__onexit_table_t *table) +{ + if (!table) + return -1; + + if (table->_first == table->_end) + table->_last = table->_end = table->_first = NULL; + return 0; +} + + +/* INTERNAL: register onexit function */ +static int __MSVCRT__register_onexit_function(MSVCRT__onexit_table_t *table, MSVCRT__onexit_t func) +{ + if (!table) + return -1; + + EnterCriticalSection(&MSVCRT_onexit_cs); + if (!table->_first) + { + table->_first = MSVCRT_calloc(32, sizeof(void *)); + if (!table->_first) + { + WARN("failed to allocate initial table.\n"); + LeaveCriticalSection(&MSVCRT_onexit_cs); + return -1; + } + table->_last = table->_first; + table->_end = table->_first + 32; + } + + /* grow if full */ + if (table->_last == table->_end) + { + int len = table->_end - table->_first; + MSVCRT__onexit_t *tmp = MSVCRT_realloc(table->_first, 2 * len * sizeof(void *)); + if (!tmp) + { + WARN("failed to grow table.\n"); + LeaveCriticalSection(&MSVCRT_onexit_cs); + return -1; + } + table->_first = tmp; + table->_end = table->_first + 2 * len; + table->_last = table->_first + len; + } + + *table->_last = func; + table->_last++; + LeaveCriticalSection(&MSVCRT_onexit_cs); + return 0; +} + +/* INTERNAL: call onexit functions */ +static int __MSVCRT__execute_onexit_table(MSVCRT__onexit_table_t *table) +{ + MSVCRT__onexit_t *func; + MSVCRT__onexit_table_t copy; + + if (!table) + return -1; + + EnterCriticalSection(&MSVCRT_onexit_cs); + if (!table->_first || table->_first >= table->_last) + { + LeaveCriticalSection(&MSVCRT_onexit_cs); + return 0; + } + copy._first = table->_first; + copy._last = table->_last; + copy._end = table->_end; + memset(table, 0, sizeof(*table)); + __MSVCRT__initialize_onexit_table(table); + LeaveCriticalSection(&MSVCRT_onexit_cs); + + for (func = copy._last - 1; func >= copy._first; func--) + { + if (*func) + (*func)(); + } + + MSVCRT_free(copy._first); + return 0; +} + /* INTERNAL: call atexit functions */ static void __MSVCRT__call_atexit(void) { /* Note: should only be called with the exit lock held */ - TRACE("%d atext functions to call\n", MSVCRT_atexit_registered); if (tls_atexit_callback) tls_atexit_callback(NULL, DLL_PROCESS_DETACH, NULL); - /* Last registered gets executed first */ - while (MSVCRT_atexit_registered > 0) - { - MSVCRT_atexit_registered--; - TRACE("next is %p\n",MSVCRT_atexit_table[MSVCRT_atexit_registered]); - if (MSVCRT_atexit_table[MSVCRT_atexit_registered]) - (*MSVCRT_atexit_table[MSVCRT_atexit_registered])(); - TRACE("returned\n"); - } + __MSVCRT__execute_onexit_table(&MSVCRT_atexit_table); }
/********************************************************************* @@ -292,25 +367,9 @@ MSVCRT__onexit_t CDECL MSVCRT__onexit(MSVCRT__onexit_t func) return NULL;
LOCK_EXIT; - if (MSVCRT_atexit_registered > MSVCRT_atexit_table_size - 1) - { - MSVCRT__onexit_t *newtable; - TRACE("expanding table\n"); - newtable = MSVCRT_calloc(MSVCRT_atexit_table_size + 32, sizeof(void *)); - if (!newtable) - { - TRACE("failed!\n"); - UNLOCK_EXIT; - return NULL; - } - memcpy (newtable, MSVCRT_atexit_table, MSVCRT_atexit_table_size*sizeof(void *)); - MSVCRT_atexit_table_size += 32; - MSVCRT_free (MSVCRT_atexit_table); - MSVCRT_atexit_table = newtable; - } - MSVCRT_atexit_table[MSVCRT_atexit_registered] = func; - MSVCRT_atexit_registered++; + __MSVCRT__register_onexit_function(&MSVCRT_atexit_table, func); UNLOCK_EXIT; + return func; }
@@ -359,7 +418,6 @@ int CDECL MSVCRT__crt_atexit(void (*func)(void)) return MSVCRT__onexit((MSVCRT__onexit_t)func) == (MSVCRT__onexit_t)func ? 0 : -1; }
- /********************************************************************* * _initialize_onexit_table (UCRTBASE.@) */ @@ -367,12 +425,7 @@ int CDECL MSVCRT__initialize_onexit_table(MSVCRT__onexit_table_t *table) { TRACE("(%p)\n", table);
- if (!table) - return -1; - - if (table->_first == table->_end) - table->_last = table->_end = table->_first = NULL; - return 0; + return __MSVCRT__initialize_onexit_table(table); }
/********************************************************************* @@ -382,43 +435,7 @@ int CDECL MSVCRT__register_onexit_function(MSVCRT__onexit_table_t *table, MSVCRT { TRACE("(%p %p)\n", table, func);
- if (!table) - return -1; - - EnterCriticalSection(&MSVCRT_onexit_cs); - if (!table->_first) - { - table->_first = MSVCRT_calloc(32, sizeof(void *)); - if (!table->_first) - { - WARN("failed to allocate initial table.\n"); - LeaveCriticalSection(&MSVCRT_onexit_cs); - return -1; - } - table->_last = table->_first; - table->_end = table->_first + 32; - } - - /* grow if full */ - if (table->_last == table->_end) - { - int len = table->_end - table->_first; - MSVCRT__onexit_t *tmp = MSVCRT_realloc(table->_first, 2 * len * sizeof(void *)); - if (!tmp) - { - WARN("failed to grow table.\n"); - LeaveCriticalSection(&MSVCRT_onexit_cs); - return -1; - } - table->_first = tmp; - table->_end = table->_first + 2 * len; - table->_last = table->_first + len; - } - - *table->_last = func; - table->_last++; - LeaveCriticalSection(&MSVCRT_onexit_cs); - return 0; + return __MSVCRT__register_onexit_function(table, func); }
/********************************************************************* @@ -426,35 +443,9 @@ int CDECL MSVCRT__register_onexit_function(MSVCRT__onexit_table_t *table, MSVCRT */ int CDECL MSVCRT__execute_onexit_table(MSVCRT__onexit_table_t *table) { - MSVCRT__onexit_t *func; - MSVCRT__onexit_table_t copy; - TRACE("(%p)\n", table);
- if (!table) - return -1; - - EnterCriticalSection(&MSVCRT_onexit_cs); - if (!table->_first || table->_first >= table->_last) - { - LeaveCriticalSection(&MSVCRT_onexit_cs); - return 0; - } - copy._first = table->_first; - copy._last = table->_last; - copy._end = table->_end; - memset(table, 0, sizeof(*table)); - MSVCRT__initialize_onexit_table(table); - LeaveCriticalSection(&MSVCRT_onexit_cs); - - for (func = copy._last - 1; func >= copy._first; func--) - { - if (*func) - (*func)(); - } - - MSVCRT_free(copy._first); - return 0; + return __MSVCRT__execute_onexit_table(table); }
/********************************************************************* diff --git a/dlls/ucrtbase/tests/misc.c b/dlls/ucrtbase/tests/misc.c index 354fab1e940..ed5bb0589c8 100644 --- a/dlls/ucrtbase/tests/misc.c +++ b/dlls/ucrtbase/tests/misc.c @@ -125,6 +125,8 @@ static int (CDECL *p_fesetround)(int); static void (CDECL *p___setusermatherr)(MSVCRT_matherr_func); static int* (CDECL *p_errno)(void); static char* (CDECL *p_asctime)(const struct tm *); +static void (CDECL *p_exit)(int); +static int (CDECL *p__crt_atexit)(void (CDECL*)(void));
static void test__initialize_onexit_table(void) { @@ -429,6 +431,8 @@ static BOOL init(void) p___setusermatherr = (void*)GetProcAddress(module, "__setusermatherr"); p_errno = (void*)GetProcAddress(module, "_errno"); p_asctime = (void*)GetProcAddress(module, "asctime"); + p__crt_atexit = (void*)GetProcAddress(module, "_crt_atexit"); + p_exit = (void*)GetProcAddress(module, "exit");
return TRUE; } @@ -765,6 +769,70 @@ static void test_asctime(void) ok(!strcmp(ret, "Thu Jan 1 00:00:00 1970\n"), "asctime returned %s\n", ret); }
+static void test_exit(const char *argv0) +{ + HANDLE exit_event1, exit_event2; + PROCESS_INFORMATION proc; + STARTUPINFOA startup = {0}; + char path[MAX_PATH]; + DWORD ret; + + exit_event1 = CreateEventA(NULL, FALSE, FALSE, "exit_event1"); + exit_event2 = CreateEventA(NULL, FALSE, FALSE, "exit_event2"); + + sprintf(path, "%s misc exit", argv0); + startup.cb = sizeof(startup); + CreateProcessA(NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &proc); + winetest_wait_child_process(proc.hProcess); + + ret = WaitForSingleObject(exit_event1, 0); + ok(ret == WAIT_OBJECT_0, "exit_event1 was not set (%x)\n", ret); + + CloseHandle(exit_event1); + CloseHandle(exit_event2); +} + +static void CDECL at_exit_func1(void) +{ + HANDLE exit_event1 = CreateEventA(NULL, FALSE, FALSE, "exit_event1"); + HANDLE exit_event2 = CreateEventA(NULL, FALSE, FALSE, "exit_event2"); + DWORD ret; + + ok(exit_event1 != NULL, "CreateEvent failed: %d\n", GetLastError()); + ok(exit_event2 != NULL, "CreateEvent failed: %d\n", GetLastError()); + + ret = WaitForSingleObject(exit_event2, 0); + ok(ret == WAIT_OBJECT_0, "exit_event2 was not set (%x)\n", ret); + + SetEvent(exit_event1); + CloseHandle(exit_event1); + CloseHandle(exit_event2); +} + +static void CDECL at_exit_func2(void) +{ + HANDLE exit_event1 = CreateEventA(NULL, FALSE, FALSE, "exit_event1"); + HANDLE exit_event2 = CreateEventA(NULL, FALSE, FALSE, "exit_event2"); + DWORD ret; + + ok(exit_event1 != NULL, "CreateEvent failed: %d\n", GetLastError()); + ok(exit_event2 != NULL, "CreateEvent failed: %d\n", GetLastError()); + + ret = WaitForSingleObject(exit_event1, 0); + ok(ret == WAIT_TIMEOUT, "exit_event1 should not be set (%x)\n", ret); + + SetEvent(exit_event2); + CloseHandle(exit_event1); + CloseHandle(exit_event2); +} + +static void test_call_exit(void) +{ + ok(!p__crt_atexit(at_exit_func1), "_crt_atexit failed\n"); + ok(!p__crt_atexit(at_exit_func2), "_crt_atexit failed\n"); + p_exit(0); +} + START_TEST(misc) { int arg_c; @@ -775,7 +843,10 @@ START_TEST(misc)
arg_c = winetest_get_mainargs(&arg_v); if(arg_c == 3) { - test__get_narrow_winmain_command_line(NULL); + if(!strcmp(arg_v[2], "cmd")) + test__get_narrow_winmain_command_line(NULL); + else if(!strcmp(arg_v[2], "exit")) + test_call_exit(); return; }
@@ -791,4 +862,5 @@ START_TEST(misc) test_isblank(); test_math_errors(); test_asctime(); + test_exit(arg_v[0]); }