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 <guoshichao@xiaomi.com>
This commit is contained in:
guoshichao 2023-07-15 16:37:22 +08:00 committed by Alin Jerpelea
parent 3524f4b9ce
commit 9d7f349664
7 changed files with 217 additions and 5 deletions

View File

@ -255,6 +255,15 @@
&entry->member != (list); entry = temp, \ &entry->member != (list); entry = temp, \
temp = container_of(temp->member.next, type, member)) 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 * Public Type Definitions
****************************************************************************/ ****************************************************************************/

View File

@ -30,6 +30,7 @@
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/atexit.h> #include <nuttx/atexit.h>
#include <nuttx/fs/fs.h> #include <nuttx/fs/fs.h>
#include <nuttx/list.h>
#include <sys/types.h> #include <sys/types.h>
#include <pthread.h> #include <pthread.h>
@ -125,6 +126,21 @@ struct getopt_s
bool go_binitialized; /* true: getopt() has been initialized */ 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 struct task_info_s
{ {
mutex_t ta_lock; mutex_t ta_lock;
@ -149,6 +165,10 @@ struct task_info_s
#ifdef CONFIG_FILE_STREAM #ifdef CONFIG_FILE_STREAM
struct streamlist ta_streamlist; /* Holds C buffered I/O info */ struct streamlist ta_streamlist; /* Holds C buffered I/O info */
#endif #endif
#ifdef CONFIG_PTHREAD_ATFORK
struct list_node ta_atfork; /* Holds the pthread_atfork_s list */
#endif
}; };
/* struct pthread_cleanup_s *************************************************/ /* struct pthread_cleanup_s *************************************************/

View File

@ -14,4 +14,10 @@ config PTHREAD_SPINLOCKS
---help--- ---help---
Enable support for pthread spinlocks. Enable support for pthread spinlocks.
config PTHREAD_ATFORK
bool "pthread_atfork support"
default n
---help---
Enable support for pthread_atfork.
endmenu # pthread support endmenu # pthread support

View File

@ -22,21 +22,65 @@
* Included Files * Included Files
****************************************************************************/ ****************************************************************************/
#include <nuttx/tls.h>
#include <nuttx/lib/lib.h>
#include <pthread.h> #include <pthread.h>
#include <errno.h>
#include <stdlib.h>
/**************************************************************************** /****************************************************************************
* Public Functions * 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), int pthread_atfork(CODE void (*prepare)(void),
CODE void (*parent)(void), CODE void (*parent)(void),
CODE void (*child)(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); if (entry == NULL)
UNUSED(parent); {
UNUSED(child); 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;
} }

View File

@ -24,11 +24,109 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/tls.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h>
#if defined(CONFIG_ARCH_HAVE_FORK) #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 * Public Functions
****************************************************************************/ ****************************************************************************/
@ -50,8 +148,23 @@
pid_t fork(void) pid_t fork(void)
{ {
pid_t pid; pid_t pid;
#ifdef CONFIG_PTHREAD_ATFORK
atfork_prepare();
#endif
pid = up_fork(); pid = up_fork();
#ifdef CONFIG_PTHREAD_ATFORK
if (pid == 0)
{
atfork_child();
}
else
{
atfork_parent();
}
#endif
return pid; return pid;
} }

View File

@ -65,6 +65,10 @@ int task_init_info(FAR struct task_group_s *group)
nxmutex_init(&info->ta_lock); nxmutex_init(&info->ta_lock);
group->tg_info = info; group->tg_info = info;
#ifdef CONFIG_PTHREAD_ATFORK
list_initialize(&info->ta_atfork);
#endif
#ifdef CONFIG_FILE_STREAM #ifdef CONFIG_FILE_STREAM
/* Initialize file streams for the task group */ /* Initialize file streams for the task group */

View File

@ -25,6 +25,7 @@
#include <nuttx/kmalloc.h> #include <nuttx/kmalloc.h>
#include <nuttx/mutex.h> #include <nuttx/mutex.h>
#include <nuttx/lib/lib.h> #include <nuttx/lib/lib.h>
#include <nuttx/list.h>
#include "tls.h" #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; 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 #ifdef CONFIG_FILE_STREAM
/* Free resource held by the stream list */ /* Free resource held by the stream list */