From 9d7f34966480543079c7268b2667a6647d42ae27 Mon Sep 17 00:00:00 2001 From: guoshichao Date: Sat, 15 Jul 2023 16:37:22 +0800 Subject: [PATCH] libs/pthread/pthread_atfork: fulfill implement pthread_atfork function 1. add the pthread_atfork implementation 2. the pthread_atfork implementation are referred to https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html Signed-off-by: guoshichao --- include/nuttx/list.h | 9 +++ include/nuttx/tls.h | 20 +++++ libs/libc/pthread/Kconfig | 6 ++ libs/libc/pthread/pthread_atfork.c | 54 ++++++++++++-- libs/libc/unistd/lib_fork.c | 113 +++++++++++++++++++++++++++++ sched/tls/task_initinfo.c | 4 + sched/tls/task_uninitinfo.c | 16 ++++ 7 files changed, 217 insertions(+), 5 deletions(-) diff --git a/include/nuttx/list.h b/include/nuttx/list.h index c71ad7f0bc..230acaff43 100644 --- a/include/nuttx/list.h +++ b/include/nuttx/list.h @@ -255,6 +255,15 @@ &entry->member != (list); entry = temp, \ temp = container_of(temp->member.next, type, member)) +/* iterates over the list in reverse order, entry should be the container + * structure type + */ + +#define list_for_every_entry_reverse(list, entry, type, member) \ + for(entry = container_of((list)->prev, type, member); \ + &entry->member != (list); \ + entry = container_of(entry->member.next, type, member)) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/include/nuttx/tls.h b/include/nuttx/tls.h index 69b750885d..72fdf7e438 100644 --- a/include/nuttx/tls.h +++ b/include/nuttx/tls.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -125,6 +126,21 @@ struct getopt_s bool go_binitialized; /* true: getopt() has been initialized */ }; +#ifdef CONFIG_PTHREAD_ATFORK +/* This structure defines the pthread_atfork_s, which is used to manage + * the funcs that operates on pthread_atfork() method + */ + +struct pthread_atfork_s +{ + CODE void (*prepare)(void); + CODE void (*child)(void); + CODE void (*parent)(void); + + struct list_node node; +}; +#endif + struct task_info_s { mutex_t ta_lock; @@ -149,6 +165,10 @@ struct task_info_s #ifdef CONFIG_FILE_STREAM struct streamlist ta_streamlist; /* Holds C buffered I/O info */ #endif + +#ifdef CONFIG_PTHREAD_ATFORK + struct list_node ta_atfork; /* Holds the pthread_atfork_s list */ +#endif }; /* struct pthread_cleanup_s *************************************************/ diff --git a/libs/libc/pthread/Kconfig b/libs/libc/pthread/Kconfig index 8e2c9f8804..19cbff73c9 100644 --- a/libs/libc/pthread/Kconfig +++ b/libs/libc/pthread/Kconfig @@ -14,4 +14,10 @@ config PTHREAD_SPINLOCKS ---help--- Enable support for pthread spinlocks. +config PTHREAD_ATFORK + bool "pthread_atfork support" + default n + ---help--- + Enable support for pthread_atfork. + endmenu # pthread support diff --git a/libs/libc/pthread/pthread_atfork.c b/libs/libc/pthread/pthread_atfork.c index 93d9d56dd3..80b0941041 100644 --- a/libs/libc/pthread/pthread_atfork.c +++ b/libs/libc/pthread/pthread_atfork.c @@ -22,21 +22,65 @@ * Included Files ****************************************************************************/ +#include +#include + #include +#include +#include /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: pthread_atfork + * + * Description: + * To register the methods that need to be executed when fork is called + * by any thread in a process + * + * Input Parameters: + * prepare - the method that is executed in the parent process before + * fork() processing is started + * parent - the method that is executed in the parent process after fork() + * processing completes + * child - the method that is executed in the child process after fork() + * processing completes + * + * Returned Value: + * On success, pthread_atfork() returns 0. + * On error, pthread_atfork() returns -1. + * + * Assumptions: + * + ****************************************************************************/ + int pthread_atfork(CODE void (*prepare)(void), CODE void (*parent)(void), CODE void (*child)(void)) { - /* fork isn't supported yet, so the dummy implementation is enough. */ +#ifdef CONFIG_PTHREAD_ATFORK + FAR struct task_info_s *info = task_get_info(); + FAR struct list_node *list = &info->ta_atfork; + FAR struct pthread_atfork_s *entry = + (FAR struct pthread_atfork_s *) + lib_malloc(sizeof(struct pthread_atfork_s)); - UNUSED(prepare); - UNUSED(parent); - UNUSED(child); + if (entry == NULL) + { + return ENOMEM; + } - return 0; + list_initialize(&entry->node); + entry->prepare = prepare; + entry->parent = parent; + entry->child = child; + + nxmutex_lock(&info->ta_lock); + list_add_head(list, &entry->node); + nxmutex_unlock(&info->ta_lock); +#endif + + return OK; } diff --git a/libs/libc/unistd/lib_fork.c b/libs/libc/unistd/lib_fork.c index e1a6463990..dbd3c1470d 100644 --- a/libs/libc/unistd/lib_fork.c +++ b/libs/libc/unistd/lib_fork.c @@ -24,11 +24,109 @@ #include #include +#include #include +#include #if defined(CONFIG_ARCH_HAVE_FORK) +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_PTHREAD_ATFORK +/**************************************************************************** + * Name: atfork_prepare + * + * Description: + * Invoke this method in the parent process before fork starts + * + ****************************************************************************/ + +static void atfork_prepare(void) +{ + FAR struct task_info_s *info = task_get_info(); + FAR struct list_node *list = &info->ta_atfork; + FAR struct pthread_atfork_s *entry; + + /* According to posix standard, the prepare handlers are called in reverse + * order of registration + * so we iterate over the func list in reverse order + */ + + nxmutex_lock(&info->ta_lock); + list_for_every_entry_reverse(list, entry, + struct pthread_atfork_s, node) + { + if (entry->prepare != NULL) + { + entry->prepare(); + } + } + + nxmutex_unlock(&info->ta_lock); +} + +/**************************************************************************** + * Name: atfork_child + * + * Description: + * Invoke this method in the child process after fork completes + * + ****************************************************************************/ + +static void atfork_child(void) +{ + FAR struct task_info_s *info = task_get_info(); + FAR struct list_node *list = &info->ta_atfork; + FAR struct pthread_atfork_s *entry; + + /* The parent handlers are called in the order of registration */ + + nxmutex_lock(&info->ta_lock); + list_for_every_entry(list, entry, + struct pthread_atfork_s, node) + { + if (entry->child != NULL) + { + entry->child(); + } + } + + nxmutex_unlock(&info->ta_lock); +} + +/**************************************************************************** + * Name: atfork_parent + * + * Description: + * Invoke this method in the parent process after fork completes + * + ****************************************************************************/ + +static void atfork_parent(void) +{ + FAR struct task_info_s *info = task_get_info(); + FAR struct list_node *list = &info->ta_atfork; + FAR struct pthread_atfork_s *entry; + + /* The child handlers are called in the order of registration */ + + nxmutex_lock(&info->ta_lock); + list_for_every_entry(list, entry, + struct pthread_atfork_s, node) + { + if (entry->parent != NULL) + { + entry->parent(); + } + } + + nxmutex_unlock(&info->ta_lock); +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -50,8 +148,23 @@ pid_t fork(void) { pid_t pid; + +#ifdef CONFIG_PTHREAD_ATFORK + atfork_prepare(); +#endif pid = up_fork(); +#ifdef CONFIG_PTHREAD_ATFORK + if (pid == 0) + { + atfork_child(); + } + else + { + atfork_parent(); + } +#endif + return pid; } diff --git a/sched/tls/task_initinfo.c b/sched/tls/task_initinfo.c index 88ded47c3d..a6d67e9183 100644 --- a/sched/tls/task_initinfo.c +++ b/sched/tls/task_initinfo.c @@ -65,6 +65,10 @@ int task_init_info(FAR struct task_group_s *group) nxmutex_init(&info->ta_lock); group->tg_info = info; +#ifdef CONFIG_PTHREAD_ATFORK + list_initialize(&info->ta_atfork); +#endif + #ifdef CONFIG_FILE_STREAM /* Initialize file streams for the task group */ diff --git a/sched/tls/task_uninitinfo.c b/sched/tls/task_uninitinfo.c index 42bf6c23e7..3ccfee3f00 100644 --- a/sched/tls/task_uninitinfo.c +++ b/sched/tls/task_uninitinfo.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "tls.h" @@ -50,6 +51,21 @@ void task_uninit_info(FAR struct task_group_s *group) { FAR struct task_info_s *info = group->tg_info; +#ifdef CONFIG_PTHREAD_ATFORK + /* Remove the functions that registered with pthread_atfork() */ + + FAR struct list_node *list = &info->ta_atfork; + FAR struct pthread_atfork_s *entry; + + while (!list_is_empty(list)) + { + entry = list_first_entry(list, + struct pthread_atfork_s, node); + list_delete_init(&entry->node); + lib_free(entry); + } +#endif + #ifdef CONFIG_FILE_STREAM /* Free resource held by the stream list */