[libc-commits] [libc] 438e591 - [libc] Add implementation of pthread_atfork.

Siva Chandra Reddy via libc-commits libc-commits at lists.llvm.org
Mon Oct 10 11:28:53 PDT 2022


Author: Siva Chandra Reddy
Date: 2022-10-10T18:28:43Z
New Revision: 438e59182b0c2e44c263f5bacc1add0e514354f8

URL: https://github.com/llvm/llvm-project/commit/438e59182b0c2e44c263f5bacc1add0e514354f8
DIFF: https://github.com/llvm/llvm-project/commit/438e59182b0c2e44c263f5bacc1add0e514354f8.diff

LOG: [libc] Add implementation of pthread_atfork.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D135432

Added: 
    libc/include/llvm-libc-types/__atfork_callback_t.h
    libc/src/__support/fork_callbacks.cpp
    libc/src/__support/fork_callbacks.h
    libc/src/pthread/pthread_atfork.cpp
    libc/src/pthread/pthread_atfork.h

Modified: 
    libc/config/linux/api.td
    libc/config/linux/x86_64/entrypoints.txt
    libc/include/CMakeLists.txt
    libc/include/llvm-libc-types/CMakeLists.txt
    libc/spec/posix.td
    libc/src/__support/CMakeLists.txt
    libc/src/pthread/CMakeLists.txt
    libc/src/unistd/linux/CMakeLists.txt
    libc/src/unistd/linux/fork.cpp
    libc/test/integration/src/unistd/CMakeLists.txt
    libc/test/integration/src/unistd/fork_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 9bb7a22ac0f28..ca79eb76b69ae 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -244,6 +244,7 @@ def ThreadsAPI : PublicAPI<"threads.h"> {
 
 def PThreadAPI : PublicAPI<"pthread.h"> {
   let Types = [
+      "__atfork_callback_t",
       "__pthread_once_func_t",
       "__pthread_start_t",
       "__pthread_tss_dtor_t",

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 7e5fde3f27341..7fd11a389dea3 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -305,6 +305,7 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.dirent.readdir
 
     # pthread.h entrypoints
+    libc.src.pthread.pthread_atfork
     libc.src.pthread.pthread_attr_destroy
     libc.src.pthread.pthread_attr_init
     libc.src.pthread.pthread_attr_getdetachstate

diff  --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index d30009e7e3fe7..4df498c5ba168 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -191,6 +191,7 @@ add_gen_header(
   GEN_HDR pthread.h
   DEPENDS
     .llvm_libc_common_h
+    .llvm-libc-types.__atfork_callback_t
     .llvm-libc-types.__pthread_start_t
     .llvm-libc-types.__pthread_tss_dtor_t
     .llvm-libc-types.pthread_attr_t

diff  --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index ffce26d798b5f..900eec8b9e65d 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_header(off64_t HDR off64_t.h)
 add_header(size_t HDR size_t.h)
+add_header(__atfork_callback_t HDR __atfork_callback_t.h)
 add_header(__bsearchcompare_t HDR __bsearchcompare_t.h)
 add_header(__call_once_func_t HDR __call_once_func_t.h)
 add_header(__exec_argv_t HDR __exec_argv_t.h)

diff  --git a/libc/include/llvm-libc-types/__atfork_callback_t.h b/libc/include/llvm-libc-types/__atfork_callback_t.h
new file mode 100644
index 0000000000000..3da66c23feb0d
--- /dev/null
+++ b/libc/include/llvm-libc-types/__atfork_callback_t.h
@@ -0,0 +1,14 @@
+//===-- Definition of type __atfork_callback_t ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_ATFORK_CALLBACK_T_H__
+#define __LLVM_LIBC_TYPES_ATFORK_CALLBACK_T_H__
+
+typedef void (*__atfork_callback_t)(void);
+
+#endif // __LLVM_LIBC_TYPES_ATFORK_CALLBACK_T_H__

diff  --git a/libc/spec/posix.td b/libc/spec/posix.td
index 912d39ff42830..fc7ac25b10c6f 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -48,6 +48,8 @@ def StructTimeSpecPtr : PtrType<StructTimeSpec>;
 def ExecArgvT : NamedType<"__exec_argv_t">;
 def ExecEnvpT : NamedType<"__exec_envp_t">;
 
+def AtForkCallbackT : NamedType<"__atfork_callback_t">;
+
 def POSIX : StandardSpec<"POSIX"> {
   PtrType CharPtr = PtrType<CharType>;
   RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
@@ -718,6 +720,7 @@ def POSIX : StandardSpec<"POSIX"> {
     "pthread.h",
     [], // Macros
     [
+        AtForkCallbackT,
         PThreadAttrTType,
         PThreadKeyT,
         PThreadMutexAttrTType,
@@ -730,6 +733,11 @@ def POSIX : StandardSpec<"POSIX"> {
     ], // Types
     [], // Enumerations
     [
+      FunctionSpec<
+          "pthread_atfork",
+          RetValSpec<IntType>,
+          [ArgSpec<AtForkCallbackT>, ArgSpec<AtForkCallbackT>, ArgSpec<AtForkCallbackT>]
+      >,
       FunctionSpec<
           "pthread_attr_init",
           RetValSpec<IntType>,

diff  --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 77e7231c66aa0..83495359d7068 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -83,6 +83,16 @@ add_header_library(
     libc.src.errno.errno
 )
 
+add_object_library(
+  fork_callbacks
+  SRCS
+    fork_callbacks.cpp
+  HDRS
+    fork_callbacks.h
+  DEPENDS
+    .threads.mutex
+)
+
 add_header_library(
   integer_operations
   HDRS

diff  --git a/libc/src/__support/fork_callbacks.cpp b/libc/src/__support/fork_callbacks.cpp
new file mode 100644
index 0000000000000..7ac4c485b1f07
--- /dev/null
+++ b/libc/src/__support/fork_callbacks.cpp
@@ -0,0 +1,90 @@
+//===-- Implementation of at-fork callback helpers  -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "fork_callbacks.h"
+
+#include "src/__support/threads/mutex.h"
+
+#include <stddef.h> // For size_t
+
+namespace __llvm_libc {
+
+namespace {
+
+struct ForkCallbackTriple {
+  ForkCallback *prepare = nullptr;
+  ForkCallback *parent = nullptr;
+  ForkCallback *child = nullptr;
+  constexpr ForkCallbackTriple() = default;
+};
+
+class AtForkCallbackManager {
+  static constexpr size_t CALLBACK_SIZE = 32;
+  // TODO: Replace this with block store when integration tests
+  // can use allocators.
+  ForkCallbackTriple list[CALLBACK_SIZE];
+  Mutex mtx;
+  size_t next_index;
+
+public:
+  constexpr AtForkCallbackManager() : mtx(false, false, false), next_index(0) {}
+
+  bool register_triple(const ForkCallbackTriple &triple) {
+    MutexLock lock(&mtx);
+    if (next_index >= CALLBACK_SIZE)
+      return false;
+    list[next_index] = triple;
+    ++next_index;
+    return true;
+  }
+
+  void invoke_prepare() {
+    MutexLock lock(&mtx);
+    for (size_t i = 0; i < next_index; ++i) {
+      auto prepare = list[i].prepare;
+      if (prepare)
+        prepare();
+    }
+  }
+
+  void invoke_parent() {
+    MutexLock lock(&mtx);
+    for (size_t i = 0; i < next_index; ++i) {
+      auto parent = list[i].parent;
+      if (parent)
+        parent();
+    }
+  }
+
+  void invoke_child() {
+    MutexLock lock(&mtx);
+    for (size_t i = 0; i < next_index; ++i) {
+      auto child = list[i].child;
+      if (child)
+        child();
+    }
+  }
+};
+
+AtForkCallbackManager cb_manager;
+
+} // Anonymous namespace
+
+bool register_atfork_callbacks(ForkCallback *prepare_cb,
+                               ForkCallback *parent_cb,
+                               ForkCallback *child_cb) {
+  return cb_manager.register_triple({prepare_cb, parent_cb, child_cb});
+}
+
+void invoke_child_callbacks() { cb_manager.invoke_child(); }
+
+void invoke_prepare_callbacks() { cb_manager.invoke_prepare(); }
+
+void invoke_parent_callbacks() { cb_manager.invoke_parent(); }
+
+} // namespace __llvm_libc

diff  --git a/libc/src/__support/fork_callbacks.h b/libc/src/__support/fork_callbacks.h
new file mode 100644
index 0000000000000..ab43436c2c898
--- /dev/null
+++ b/libc/src/__support/fork_callbacks.h
@@ -0,0 +1,19 @@
+//===-- At-fork callback helpers  -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+namespace __llvm_libc {
+
+using ForkCallback = void(void);
+
+bool register_atfork_callbacks(ForkCallback *prepare_cd,
+                               ForkCallback *parent_cb, ForkCallback *child_cb);
+void invoke_prepare_callbacks();
+void invoke_parent_callbacks();
+void invoke_child_callbacks();
+
+} // namespace __llvm_libc

diff  --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index 7062ed733679a..f2d33b9b71019 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -397,3 +397,15 @@ add_entrypoint_object(
     libc.include.pthread
     libc.src.__support.threads.callonce
 )
+
+add_entrypoint_object(
+  pthread_atfork
+  SRCS
+    pthread_atfork.cpp
+  HDRS
+    pthread_atfork.h
+  DEPENDS
+    libc.include.errno
+    libc.include.pthread
+    libc.src.__support.fork_callbacks
+)

diff  --git a/libc/src/pthread/pthread_atfork.cpp b/libc/src/pthread/pthread_atfork.cpp
new file mode 100644
index 0000000000000..bff80d5fd4563
--- /dev/null
+++ b/libc/src/pthread/pthread_atfork.cpp
@@ -0,0 +1,25 @@
+//===-- Linux implementation of the pthread_atfork function ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "pthread_atfork.h"
+
+#include "src/__support/common.h"
+#include "src/__support/fork_callbacks.h"
+
+#include <errno.h>
+#include <pthread.h> // For pthread_* type definitions.
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, pthread_atfork,
+                   (__atfork_callback_t prepare, __atfork_callback_t parent,
+                    __atfork_callback_t child)) {
+  return register_atfork_callbacks(prepare, parent, child) ? 0 : ENOMEM;
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/pthread/pthread_atfork.h b/libc/src/pthread/pthread_atfork.h
new file mode 100644
index 0000000000000..500478b2c5076
--- /dev/null
+++ b/libc/src/pthread/pthread_atfork.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pthread_atfork function -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_THREADS_PTHREAD_ATFORK_H
+#define LLVM_LIBC_SRC_THREADS_PTHREAD_ATFORK_H
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+int pthread_atfork(__atfork_callback_t prepare, __atfork_callback_t parent,
+                   __atfork_callback_t child);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_THREADS_PTHREAD_ATFORK_H

diff  --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 3529222ace45f..c81b9219de8d6 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -100,6 +100,7 @@ add_entrypoint_object(
     libc.include.errno
     libc.include.unistd
     libc.include.sys_syscall
+    libc.src.__support.fork_callbacks
     libc.src.__support.OSUtil.osutil
     libc.src.__support.threads.thread
     libc.src.errno.errno

diff  --git a/libc/src/unistd/linux/fork.cpp b/libc/src/unistd/linux/fork.cpp
index c51eaeec0d85e..5c12fafd425ae 100644
--- a/libc/src/unistd/linux/fork.cpp
+++ b/libc/src/unistd/linux/fork.cpp
@@ -10,6 +10,7 @@
 
 #include "src/__support/OSUtil/syscall.h" // For internal syscall function.
 #include "src/__support/common.h"
+#include "src/__support/fork_callbacks.h"
 #include "src/__support/threads/thread.h" // For thread self object
 
 #include <errno.h>
@@ -21,6 +22,7 @@ namespace __llvm_libc {
 // functionality and standard compliance in future.
 
 LLVM_LIBC_FUNCTION(pid_t, fork, (void)) {
+  invoke_prepare_callbacks();
 #ifdef SYS_fork
   pid_t ret = __llvm_libc::syscall_impl(SYS_fork);
 #elif defined(SYS_clone)
@@ -34,6 +36,7 @@ LLVM_LIBC_FUNCTION(pid_t, fork, (void)) {
     // copy of parent process' thread which called fork. So, we have to fix up
     // the child process' self object with the new process' tid.
     self.attrib->tid = __llvm_libc::syscall_impl(SYS_gettid);
+    invoke_child_callbacks();
     return 0;
   }
 
@@ -43,6 +46,7 @@ LLVM_LIBC_FUNCTION(pid_t, fork, (void)) {
     return -1;
   }
 
+  invoke_parent_callbacks();
   return ret;
 }
 

diff  --git a/libc/test/integration/src/unistd/CMakeLists.txt b/libc/test/integration/src/unistd/CMakeLists.txt
index 23adb29c7326c..509666cdf0ee6 100644
--- a/libc/test/integration/src/unistd/CMakeLists.txt
+++ b/libc/test/integration/src/unistd/CMakeLists.txt
@@ -14,6 +14,7 @@ add_integration_test(
     libc.include.signal
     libc.include.sys_wait
     libc.include.unistd
+    libc.src.pthread.pthread_atfork
     libc.src.signal.raise
     libc.src.sys.wait.wait
     libc.src.sys.wait.wait4

diff  --git a/libc/test/integration/src/unistd/fork_test.cpp b/libc/test/integration/src/unistd/fork_test.cpp
index e42dbcb96d36a..13c50dd928f5b 100644
--- a/libc/test/integration/src/unistd/fork_test.cpp
+++ b/libc/test/integration/src/unistd/fork_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/pthread/pthread_atfork.h"
 #include "src/signal/raise.h"
 #include "src/sys/wait/wait.h"
 #include "src/sys/wait/wait4.h"
@@ -105,6 +106,39 @@ void fork_and_waitpid_signal_exit() {
   ASSERT_TRUE(WTERMSIG(status) == SIGUSR1);
 }
 
+static int prepare = 0;
+static int parent = 0;
+static int child = 0;
+static constexpr int DONE = 0x600D;
+
+static void prepare_cb() { prepare = DONE; }
+
+static void parent_cb() { parent = DONE; }
+
+static void child_cb() { child = DONE; }
+
+void fork_with_atfork_callbacks() {
+  ASSERT_EQ(__llvm_libc::pthread_atfork(&prepare_cb, &parent_cb, &child_cb), 0);
+  pid_t pid = __llvm_libc::fork();
+  if (pid == 0) {
+    // Raise a signal from the child if unexpected at-fork
+    // behavior is observed.
+    if (child != DONE || prepare != DONE || parent == DONE)
+      __llvm_libc::raise(SIGUSR1);
+    return;
+  }
+
+  ASSERT_TRUE(pid > 0);
+  int status;
+  pid_t cpid = __llvm_libc::waitpid(pid, &status, 0);
+  ASSERT_TRUE(cpid > 0);
+  ASSERT_EQ(cpid, pid);
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(prepare, DONE);
+  ASSERT_EQ(parent, DONE);
+  ASSERT_NE(child, DONE);
+}
+
 TEST_MAIN(int argc, char **argv, char **envp) {
   fork_and_wait_normal_exit();
   fork_and_wait4_normal_exit();
@@ -112,5 +146,6 @@ TEST_MAIN(int argc, char **argv, char **envp) {
   fork_and_wait_signal_exit();
   fork_and_wait4_signal_exit();
   fork_and_waitpid_signal_exit();
+  fork_with_atfork_callbacks();
   return 0;
 }


        


More information about the libc-commits mailing list