Skip to content

Commit

Permalink
exit: change _atexit_call into __cxa_finalize
Browse files Browse the repository at this point in the history
JIRA: RTOS-900
  • Loading branch information
badochov committed Sep 16, 2024
1 parent 96c4c08 commit c466de1
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 22 deletions.
68 changes: 48 additions & 20 deletions stdlib/atexit.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef void (*destructor_t)(void);
struct atexit_node {
destructor_t destructors[ATEXIT_MAX];
void *args[ATEXIT_MAX];
void *handles[ATEXIT_MAX];
uint32_t fntype;
struct atexit_node *prev;
};
Expand All @@ -39,6 +40,7 @@ struct atexit_node {
static struct {
handle_t lock;
struct atexit_node *head;
struct atexit_node *newestNode;
unsigned int idx;
} atexit_common = { .head = &((struct atexit_node) {}) };

Expand All @@ -49,11 +51,12 @@ void _atexit_init(void)
mutexCreate(&atexit_common.lock);
memset(atexit_common.head, 0, sizeof(struct atexit_node));
atexit_common.idx = 0;
atexit_common.newestNode = atexit_common.head;
}


/* Generic function to register destructors */
static int _atexit_register(int isarg, void (*fn)(void), void *arg)
static int _atexit_register(int isarg, void (*fn)(void), void *arg, void *handle)
{
struct atexit_node *node;

Expand All @@ -71,13 +74,15 @@ static int _atexit_register(int isarg, void (*fn)(void), void *arg)

atexit_common.head = node;
atexit_common.idx = 0;
atexit_common.newestNode = node;
}

if (isarg != 0) {
node->fntype |= (1u << atexit_common.idx);
node->args[atexit_common.idx] = arg;
}
node->destructors[atexit_common.idx] = fn;
node->handles[atexit_common.idx] = handle;

atexit_common.idx++;

Expand All @@ -86,46 +91,69 @@ static int _atexit_register(int isarg, void (*fn)(void), void *arg)
}


/* Call all registered destructors */
void _atexit_call(void)
/* Call destructors registered for given object, or all in case of NULL */
/* Conforming: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor */
void __cxa_finalize(void *handle)
{
/* No handlers registered. */
if (atexit_common.idx == 0) {
return;
}

mutexLock(atexit_common.lock);
while ((atexit_common.head != NULL) && (atexit_common.idx != 0)) {
/* Iteration has to be over the atexit_common.head and newest node must be restored at the end
* as the atexit functions may register new atexit functions. */
while (atexit_common.head != NULL) {
atexit_common.idx--;
destructor_t destructor = atexit_common.head->destructors[atexit_common.idx];
if (((atexit_common.head->fntype >> atexit_common.idx) & 1) == 1) {
void *arg = atexit_common.head->args[atexit_common.idx];
mutexUnlock(atexit_common.lock);
((void (*)(void *))destructor)(arg);
}
else {
mutexUnlock(atexit_common.lock);
destructor();
/* Do not call already called destructors and destructors not from current handle. */
if ((destructor != NULL) && ((handle == atexit_common.head->handles[atexit_common.idx]) || (handle == NULL))) {
/* Mark destructor as called. */
atexit_common.head->destructors[atexit_common.idx] = NULL;

if (((atexit_common.head->fntype >> atexit_common.idx) & 1) == 1) {
void *arg = atexit_common.head->args[atexit_common.idx];
mutexUnlock(atexit_common.lock);
((void (*)(void *))destructor)(arg);
}
else {
mutexUnlock(atexit_common.lock);
destructor();
}
mutexLock(atexit_common.lock);
}
mutexLock(atexit_common.lock);

if (atexit_common.idx == 0) {
atexit_common.head = atexit_common.head->prev;
atexit_common.idx = ATEXIT_MAX;
}
}

atexit_common.head = atexit_common.newestNode;

/* All nodes are fully emptied from destructors, free memory. */
if (handle == NULL) {
/* Ensure the first node that is statically allocated is not freed */
while (atexit_common.head->prev != NULL) {
struct atexit_node *last = atexit_common.head;
atexit_common.head = atexit_common.head->prev;
if (atexit_common.head != NULL) {
/* Free only if it's not the first node (statically allocated) */
free(last);
atexit_common.idx = ATEXIT_MAX;
}
free(last);
}
atexit_common.newestNode = atexit_common.head;
}

mutexUnlock(atexit_common.lock);
}


int atexit(void (*func)(void))
{
return _atexit_register(0, func, NULL);
return _atexit_register(0, func, NULL, NULL);
}


/* Register a function to run at process termination with given arguments */
int __cxa_atexit(void (*func)(void *), void *arg, void *handler)
{
return _atexit_register(1, (void (*)(void))func, arg);
return _atexit_register(1, (void (*)(void))func, arg, handler);
}
4 changes: 2 additions & 2 deletions stdlib/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include <stdlib.h>
#include <stdio.h>

extern void _atexit_call(void);
extern void __cxa_finalize(void *);
extern void sys_exit(int) __attribute__((noreturn));


Expand All @@ -34,7 +34,7 @@ void _Exit(int status)

void exit(int status)
{
_atexit_call();
__cxa_finalize(NULL);
fflush(NULL);
_exit(status);
for(;;);
Expand Down

0 comments on commit c466de1

Please sign in to comment.