[newlib-cygwin/main] Cygwin: add _Fork() system call per POSIX.1-2024

Corinna Vinschen via Cygwin-cvs <[email protected]> Tue, 31 Mar 2026 15:12:36 +0000 (GMT)
Newsgroups gmane.os.cygwin.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dnewlib-cygwin.git;h=3D5f515cf3d6e=
338e062ba25e93abccd5b6eb1d596

commit 5f515cf3d6e338e062ba25e93abccd5b6eb1d596
Author:     Corinna Vinschen <[email protected]>
AuthorDate: Mon Mar 30 16:24:07 2026 +0200
Commit:     Corinna Vinschen <[email protected]>
CommitDate: Tue Mar 31 12:12:08 2026 +0200

    Cygwin: add _Fork() system call per POSIX.1-2024
   =20
    The _Fork() function shall be equivalent to fork(), except that fork
    handlers established by means of the pthread_atfork() function shall
    not be called and _Fork() shall be async-signal-safe.  Our fork()
    already is async-signal-safe, so just make sure the pthread_atfork()
    handlers are not called.
   =20
    Signed-off-by: Corinna Vinschen <[email protected]>

Diff:
---
 newlib/libc/include/sys/unistd.h       |  3 +++
 winsup/cygwin/cygwin.din               |  1 +
 winsup/cygwin/fork.cc                  | 40 ++++++++++++++++++++++++++----=
----
 winsup/cygwin/include/cygwin/version.h |  3 ++-
 winsup/cygwin/local_includes/sigproc.h | 13 +++++++++--
 winsup/cygwin/release/3.7.0            |  1 +
 6 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/newlib/libc/include/sys/unistd.h b/newlib/libc/include/sys/uni=
std.h
index 4cf9f0636276..d1c9126e53ab 100644
--- a/newlib/libc/include/sys/unistd.h
+++ b/newlib/libc/include/sys/unistd.h
@@ -96,6 +96,9 @@ int	fchownat (int __dirfd, const char *__path, uid_t __ow=
ner, gid_t __group, int
 int	fexecve (int __fd, char * const __argv[], char * const __envp[]);
 #endif
 pid_t   fork (void);
+#if __POSIX_VISIBLE >=3D 202405
+pid_t   _Fork (void);
+#endif
 long    fpathconf (int __fd, int __name);
 int     fsync (int __fd);
 #if __POSIX_VISIBLE >=3D 199309
diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din
index 7709a0653eb9..76477bb4aec2 100644
--- a/winsup/cygwin/cygwin.din
+++ b/winsup/cygwin/cygwin.din
@@ -147,6 +147,7 @@ __xpg_strerror_r SIGFE
 _dll_crt0 NOSIGFE
 _Exit SIGFE
 _exit SIGFE
+_Fork SIGFE
 _feinitialise NOSIGFE
 _fscanf_r SIGFE
 _get_osfhandle SIGFE
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 48e8b7557d00..722d21bdb023 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -31,7 +31,7 @@ details. */
 /* FIXME: Once things stabilize, bump up to a few minutes.  */
 #define FORK_WAIT_TIMEOUT (300 * 1000)     /* 300 seconds */
=20
-static int dofork (void **proc, bool *with_forkables);
+static int dofork (void **proc, bool do_atfork_handlers, bool *with_forkab=
les);
 class frok
 {
   frok (bool *forkables)
@@ -47,7 +47,7 @@ class frok
   int parent (volatile char * volatile here);
   int child (volatile char * volatile here);
   bool error (const char *fmt, ...);
-  friend int dofork (void **proc, bool *with_forkables);
+  friend int dofork (void **, bool, bool *);
 };
=20
 static void
@@ -201,7 +201,6 @@ frok::child (volatile char * volatile here)
   CloseHandle (hParent);
   hParent =3D NULL;
   cygwin_finished_initializing =3D true;
-  pthread::atforkchild ();
   return 0;
 }
=20
@@ -609,15 +608,33 @@ extern "C" int
 fork ()
 {
   bool with_forkables =3D false; /* do not force hardlinks on first try */
-  int res =3D dofork (NULL, &with_forkables);
+  int res =3D dofork (NULL, false, &with_forkables);
   if (res >=3D 0)
     return res;
   if (with_forkables)
     return res; /* no need for second try when already enabled */
   with_forkables =3D true; /* enable hardlinks for second try */
-  return dofork (NULL, &with_forkables);
+  return dofork (NULL, false, &with_forkables);
 }
=20
+/* POSIX.1-2024:
+
+    The _Fork() function shall be equivalent to fork(), except that fork
+    handlers established by means of the pthread_atfork() function shall
+    not be called and _Fork() shall be async-signal-safe.  Our fork()
+    already is async-signal-safe. */
+extern "C" int
+_Fork ()
+{
+  bool with_forkables =3D false; /* do not force hardlinks on first try */
+  int res =3D dofork (NULL, true, &with_forkables);
+  if (res >=3D 0)
+    return res;
+  if (with_forkables)
+    return res; /* no need for second try when already enabled */
+  with_forkables =3D true; /* enable hardlinks for second try */
+  return dofork (NULL, true, &with_forkables);
+}
=20
 /* __posix_spawn_fork is called from newlib's posix_spawn implementation.
    The original code in newlib has been taken from FreeBSD, and the core
@@ -628,17 +645,17 @@ extern "C" int
 __posix_spawn_fork (void **proc)
 {
   bool with_forkables =3D false; /* do not force hardlinks on first try */
-  int res =3D dofork (proc, &with_forkables);
+  int res =3D dofork (proc, false, &with_forkables);
   if (res >=3D 0)
     return res;
   if (with_forkables)
     return res; /* no need for second try when already enabled */
   with_forkables =3D true; /* enable hardlinks for second try */
-  return dofork (proc, &with_forkables);
+  return dofork (proc, false, &with_forkables);
 }
=20
 static int
-dofork (void **proc, bool *with_forkables)
+dofork (void **proc, bool do_atfork_handlers, bool *with_forkables)
 {
   frok grouped (with_forkables);
=20
@@ -659,7 +676,7 @@ dofork (void **proc, bool *with_forkables)
     }
=20
   {
-    hold_everything held_everything (ischild);
+    hold_everything held_everything (ischild, do_atfork_handlers);
     /* This tmp_pathbuf constructor is required here because the below set=
jmp
        magic will otherwise not restore the original buffer count values in
        the thread-local storage.  A process forking too deeply will run in=
to
@@ -695,6 +712,11 @@ dofork (void **proc, bool *with_forkables)
     else
       {
 	res =3D grouped.child (stackp);
+	/* So far pthread::atforkchild() was called as last function
+	   from inside frok::child().  Move the call here, so we don't have
+	   to propagate the do_atfork_handlers variable to frok::child(). */
+	if (!do_atfork_handlers)
+	  pthread::atforkchild ();
 	__in_forkee =3D FORKED;
 	ischild =3D true;	/* might have been reset by fork mem copy */
       }
diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include=
/cygwin/version.h
index ef552ffcba9c..695477bec265 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -500,12 +500,13 @@ details. */
        acl_is_trivial_np, acl_set_fd_np, acl_set_link_np, acl_strip_np.
   359: Export wrappers for C++14 and C++17 new and delete overloads.
   360: Add RLIMIT_NPROC.
+  361: Export _Fork.
=20
   Note that we forgot to bump the api for ualarm, strtoll, strtoull,
   sigaltstack, sethostname. */
=20
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 360
+#define CYGWIN_VERSION_API_MINOR 361
=20
 /* There is also a compatibity version number associated with the shared m=
emory
    regions.  It is incremented when incompatible changes are made to the s=
hared
diff --git a/winsup/cygwin/local_includes/sigproc.h b/winsup/cygwin/local_i=
ncludes/sigproc.h
index ce7263338f0a..92cda94dcbb8 100644
--- a/winsup/cygwin/local_includes/sigproc.h
+++ b/winsup/cygwin/local_includes/sigproc.h
@@ -131,7 +131,8 @@ class lock_pthread
 {
   bool bother;
 public:
-  lock_pthread (): bother (1)
+  lock_pthread (): bother (1) {}
+  void prepare ()
   {
     pthread::atforkprepare ();
   }
@@ -165,7 +166,15 @@ class hold_everything
   lock_process process;
=20
 public:
-  hold_everything (bool& x): ischild (x) {}
+  hold_everything (bool& x, bool do_atfork_handlers): ischild (x)
+  {
+    /* POSIX.1-2024: _Fork() does not call any handler established
+		     by pthread_atfork(). */
+    if (do_atfork_handlers)
+      pthread.dont_bother ();
+    else
+      pthread.prepare ();
+  }
   operator int () const {return signals;}
=20
   ~hold_everything()
diff --git a/winsup/cygwin/release/3.7.0 b/winsup/cygwin/release/3.7.0
index 4736fd17c3f4..d5b63b0586a9 100644
--- a/winsup/cygwin/release/3.7.0
+++ b/winsup/cygwin/release/3.7.0
@@ -11,3 +11,4 @@ What's new:
 - Improved support for soft and hard limits in setrlimit(2), support
   RLIMIT_NPROC.
=20
+- New API: _Fork.