From d680c5d0278ff88c71d311cb9a4fd2fa8fb886b1 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Sat, 21 Nov 2020 01:36:55 +0100
Subject: [PATCH] WIP stdio *_unlocked()

---
 gnu/usr.bin/cc47/cc_prep/auto-host.h          | 10 +++----
 gnu/usr.bin/cc47/libcpp/config.h              | 10 +++----
 gnu/usr.bin/cc80/cc_prep/auto-host.h          | 10 +++----
 gnu/usr.bin/cc80/support-libs/libcpp/config.h | 10 +++----
 gnu/usr.bin/cvs/lib/config.h.proto            | 10 +++----
 gnu/usr.bin/diff/libdiffutils/config.h        | 10 +++----
 gnu/usr.bin/grep/libgreputils/config.h        | 10 +++----
 include/stdio.h                               |  9 ++++++-
 lib/libc/stdio/Makefile.inc                   | 10 ++++---
 lib/libc/stdio/Symbol.map                     |  5 ++++
 lib/libc/stdio/fflush.3                       | 15 ++++++++++-
 lib/libc/stdio/fflush.c                       |  2 ++
 lib/libc/stdio/fputc.c                        | 11 ++++++--
 lib/libc/stdio/fputs.3                        | 15 ++++++++++-
 lib/libc/stdio/fputs.c                        | 13 ++++++++--
 lib/libc/stdio/fread.3                        | 26 ++++++++++++++++---
 lib/libc/stdio/fread.c                        |  2 ++
 lib/libc/stdio/fwrite.c                       | 18 ++++++++++---
 lib/libc/stdio/putc.3                         | 12 ++++++---
 lib/libc/stdio/putc.c                         |  1 -
 sys/sys/param.h                               |  3 ++-
 21 files changed, 154 insertions(+), 58 deletions(-)

diff --git a/gnu/usr.bin/cc47/cc_prep/auto-host.h b/gnu/usr.bin/cc47/cc_prep/auto-host.h
index b27b7a21ab..f39f8f5aa4 100644
--- a/gnu/usr.bin/cc47/cc_prep/auto-host.h
+++ b/gnu/usr.bin/cc47/cc_prep/auto-host.h
@@ -622,7 +622,7 @@
 /* Define to 1 if we found a declaration for 'fflush_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 #endif
 
 
@@ -657,21 +657,21 @@
 /* Define to 1 if we found a declaration for 'fputc_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 #endif
 
 
 /* Define to 1 if we found a declaration for 'fputs_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 #endif
 
 
 /* Define to 1 if we found a declaration for 'fread_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 #endif
 
 
@@ -684,7 +684,7 @@
 /* Define to 1 if we found a declaration for 'fwrite_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 #endif
 
 
diff --git a/gnu/usr.bin/cc47/libcpp/config.h b/gnu/usr.bin/cc47/libcpp/config.h
index 3241a1d849..0ec0f43c8a 100644
--- a/gnu/usr.bin/cc47/libcpp/config.h
+++ b/gnu/usr.bin/cc47/libcpp/config.h
@@ -59,7 +59,7 @@
 
 /* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fgetc_unlocked', and to 0 if
    you don't. */
@@ -79,19 +79,19 @@
 
 /* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if
    you don't. */
diff --git a/gnu/usr.bin/cc80/cc_prep/auto-host.h b/gnu/usr.bin/cc80/cc_prep/auto-host.h
index bc1a79912b..eaf0083dfe 100644
--- a/gnu/usr.bin/cc80/cc_prep/auto-host.h
+++ b/gnu/usr.bin/cc80/cc_prep/auto-host.h
@@ -873,7 +873,7 @@
 /* Define to 1 if we found a declaration for 'fflush_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 #endif
 
 
@@ -914,21 +914,21 @@
 /* Define to 1 if we found a declaration for 'fputc_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 #endif
 
 
 /* Define to 1 if we found a declaration for 'fputs_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 #endif
 
 
 /* Define to 1 if we found a declaration for 'fread_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 #endif
 
 
@@ -941,7 +941,7 @@
 /* Define to 1 if we found a declaration for 'fwrite_unlocked', otherwise
    define to 0. */
 #ifndef USED_FOR_TARGET
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 #endif
 
 
diff --git a/gnu/usr.bin/cc80/support-libs/libcpp/config.h b/gnu/usr.bin/cc80/support-libs/libcpp/config.h
index acb7e1135f..8c666ea7f0 100644
--- a/gnu/usr.bin/cc80/support-libs/libcpp/config.h
+++ b/gnu/usr.bin/cc80/support-libs/libcpp/config.h
@@ -76,7 +76,7 @@
 
 /* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fgetc_unlocked', and to 0 if
    you don't. */
@@ -96,19 +96,19 @@
 
 /* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if
    you don't. */
diff --git a/gnu/usr.bin/cvs/lib/config.h.proto b/gnu/usr.bin/cvs/lib/config.h.proto
index 9c2f5a3c59..220fdd4418 100644
--- a/gnu/usr.bin/cvs/lib/config.h.proto
+++ b/gnu/usr.bin/cvs/lib/config.h.proto
@@ -168,7 +168,7 @@
 
 /* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
    you don't. */
@@ -180,15 +180,15 @@
 
 /* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `funlockfile', and to 0 if you
    don't. */
@@ -196,7 +196,7 @@
 
 /* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if
    you don't. */
diff --git a/gnu/usr.bin/diff/libdiffutils/config.h b/gnu/usr.bin/diff/libdiffutils/config.h
index f0d8e6e5fd..8d1473951b 100644
--- a/gnu/usr.bin/diff/libdiffutils/config.h
+++ b/gnu/usr.bin/diff/libdiffutils/config.h
@@ -411,7 +411,7 @@
 
 /* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
    you don't. */
@@ -419,19 +419,19 @@
 
 /* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if
    you don't. */
diff --git a/gnu/usr.bin/grep/libgreputils/config.h b/gnu/usr.bin/grep/libgreputils/config.h
index b993c2970e..081f16d464 100644
--- a/gnu/usr.bin/grep/libgreputils/config.h
+++ b/gnu/usr.bin/grep/libgreputils/config.h
@@ -504,7 +504,7 @@
 
 /* Define to 1 if you have the declaration of `fflush_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FFLUSH_UNLOCKED 0
+#define HAVE_DECL_FFLUSH_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if
    you don't. */
@@ -512,19 +512,19 @@
 
 /* Define to 1 if you have the declaration of `fputc_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTC_UNLOCKED 0
+#define HAVE_DECL_FPUTC_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fputs_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FPUTS_UNLOCKED 0
+#define HAVE_DECL_FPUTS_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fread_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FREAD_UNLOCKED 0
+#define HAVE_DECL_FREAD_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `fwrite_unlocked', and to 0 if
    you don't. */
-#define HAVE_DECL_FWRITE_UNLOCKED 0
+#define HAVE_DECL_FWRITE_UNLOCKED 1
 
 /* Define to 1 if you have the declaration of `getchar_unlocked', and to 0 if
    you don't. */
diff --git a/include/stdio.h b/include/stdio.h
index b69bd67853..55c53ac766 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -310,7 +310,13 @@ int	 putchar_unlocked(int);
 void	 clearerr_unlocked(FILE *);
 int	 feof_unlocked(FILE *);
 int	 ferror_unlocked(FILE *);
+int	 fflush_unlocked(FILE *);
 int	 fileno_unlocked(FILE *);
+int	 fputc_unlocked(int, FILE *);
+int	 fputs_unlocked(const char * __restrict, FILE * __restrict);
+size_t	 fread_unlocked(void * __restrict, size_t, size_t, FILE * __restrict);
+size_t	 fwrite_unlocked(const void * __restrict, size_t, size_t,
+	     FILE * __restrict);
 #endif
 
 #if __XSI_VISIBLE >= 500 || __POSIX_VISIBLE >= 200112
@@ -462,10 +468,11 @@ extern int __isthreaded;
  * See ISO/IEC 9945-1 ANSI/IEEE Std 1003.1 Second Edition 1996-07-12
  * B.8.2.7 for the rationale behind the *_unlocked() macros.
  */
+#define	clearerr_unlocked(p)	__sclearerr(p)
 #define	feof_unlocked(p)	__sfeof(p)
 #define	ferror_unlocked(p)	__sferror(p)
-#define	clearerr_unlocked(p)	__sclearerr(p)
 #define	fileno_unlocked(p)	__sfileno(p)
+#define	fputc_unlocked(s, p)	__sputc(s, p)
 #endif
 #if __POSIX_VISIBLE >= 199506
 #define	getc_unlocked(fp)	__sgetc(fp)
diff --git a/lib/libc/stdio/Makefile.inc b/lib/libc/stdio/Makefile.inc
index 8dc605f98a..abf1e56e47 100644
--- a/lib/libc/stdio/Makefile.inc
+++ b/lib/libc/stdio/Makefile.inc
@@ -49,16 +49,20 @@ MLINKS+=ferror.3 clearerr.3 \
 	ferror.3 ferror_unlocked.3 \
 	ferror.3 fileno.3 \
 	ferror.3 fileno_unlocked.3
-MLINKS+=fflush.3 fpurge.3
+MLINKS+=fflush.3 fflush_unlocked.3 \
+	fflush.3 fpurge.3
 MLINKS+=fgets.3 gets_s.3
 MLINKS+=fgetws.3 fgetws_l.3
 MLINKS+=flockfile.3 ftrylockfile.3 \
 	flockfile.3 funlockfile.3
 MLINKS+=fopen.3 fdopen.3 \
 	fopen.3 freopen.3
-MLINKS+=fputs.3 puts.3
+MLINKS+=fputs.3 fputs_unlocked.3 \
+	fputs.3 puts.3
 MLINKS+=fputws.3 fputws_l.3
-MLINKS+=fread.3 fwrite.3
+MLINKS+=fread.3 fread_unlocked.3 \
+	fread.3 fwrite.3 \
+	fread.3 fwrite_unlocked.3
 MLINKS+=fseek.3 fgetpos.3 \
 	fseek.3 fseeko.3 \
 	fseek.3 fsetpos.3 \
diff --git a/lib/libc/stdio/Symbol.map b/lib/libc/stdio/Symbol.map
index 5ec6c68a41..020e196d7f 100644
--- a/lib/libc/stdio/Symbol.map
+++ b/lib/libc/stdio/Symbol.map
@@ -162,7 +162,12 @@ DF508.0 {
 };
 
 DF510.0 {
+    fflush_unlocked;
     fparseln;
+    fputc_unlocked;
+    fputs_unlocked;
+    fread_unlocked;
+    fwrite_unlocked;
 };
 
 DFprivate_1.0 {
diff --git a/lib/libc/stdio/fflush.3 b/lib/libc/stdio/fflush.3
index ffbe40f7ae..5840a497a5 100644
--- a/lib/libc/stdio/fflush.3
+++ b/lib/libc/stdio/fflush.3
@@ -32,11 +32,12 @@
 .\"     @(#)fflush.3	8.1 (Berkeley) 6/4/93
 .\" $FreeBSD: src/lib/libc/stdio/fflush.3,v 1.11 2007/01/09 00:28:06 imp Exp $
 .\"
-.Dd December 23, 2017
+.Dd November 20, 2020
 .Dt FFLUSH 3
 .Os
 .Sh NAME
 .Nm fflush ,
+.Nm fflush_unlocked ,
 .Nm fpurge
 .Nd flush a stream
 .Sh LIBRARY
@@ -46,6 +47,8 @@
 .Ft int
 .Fn fflush "FILE *stream"
 .Ft int
+.Fn fflush_unlocked "FILE *stream"
+.Ft int
 .Fn fpurge "FILE *stream"
 .Sh DESCRIPTION
 The function
@@ -64,6 +67,16 @@ flushes
 .Em all
 open output streams.
 .Pp
+The
+.Fn fflush_unlocked
+function is equivalent to
+.Fn fflush ,
+except that the caller is responsible for locking the stream with
+.Xr flockfile 3
+before calling it.
+This function may be used to avoid the overhead of locking the stream and to
+prevent races when multiple threads are operating on the same stream.
+.Pp
 The function
 .Fn fpurge
 erases any input or output buffered in the given
diff --git a/lib/libc/stdio/fflush.c b/lib/libc/stdio/fflush.c
index 93e2ff7bce..aacc74341d 100644
--- a/lib/libc/stdio/fflush.c
+++ b/lib/libc/stdio/fflush.c
@@ -94,6 +94,8 @@ __fflush(FILE *fp)
 	return (retval);
 }
 
+__weak_reference(__fflush, fflush_unlocked);
+
 int
 __sflush(FILE *fp)
 {
diff --git a/lib/libc/stdio/fputc.c b/lib/libc/stdio/fputc.c
index daed23cd8d..1295478b36 100644
--- a/lib/libc/stdio/fputc.c
+++ b/lib/libc/stdio/fputc.c
@@ -31,7 +31,6 @@
  *
  * @(#)fputc.c	8.1 (Berkeley) 6/4/93
  * $FreeBSD: src/lib/libc/stdio/fputc.c,v 1.14 2007/01/09 00:28:06 imp Exp $
- * $DragonFly: src/lib/libc/stdio/fputc.c,v 1.5 2005/01/31 22:29:40 dillon Exp $
  */
 
 #include "namespace.h"
@@ -40,12 +39,20 @@
 #include "local.h"
 #include "libc_private.h"
 
+#undef fputc_unlocked
+
+int
+fputc_unlocked(int c, FILE *fp)
+{
+	return (__sputc(c, fp));
+}
+
 int
 fputc(int c, FILE *fp)
 {
 	int retval;
 	FLOCKFILE(fp);
-	retval = __sputc(c, fp);
+	retval = fputc_unlocked(c, fp);
 	FUNLOCKFILE(fp);
 	return (retval);
 }
diff --git a/lib/libc/stdio/fputs.3 b/lib/libc/stdio/fputs.3
index 83fd32e36a..b82b5a6ffb 100644
--- a/lib/libc/stdio/fputs.3
+++ b/lib/libc/stdio/fputs.3
@@ -32,11 +32,12 @@
 .\"     @(#)fputs.3	8.1 (Berkeley) 6/4/93
 .\" $FreeBSD: src/lib/libc/stdio/fputs.3,v 1.14 2007/04/19 14:01:04 phk Exp $
 .\"
-.Dd February 22, 2018
+.Dd November 20, 2020
 .Dt FPUTS 3
 .Os
 .Sh NAME
 .Nm fputs ,
+.Nm fputs_unlocked ,
 .Nm puts
 .Nd output a line to a stream
 .Sh LIBRARY
@@ -46,6 +47,8 @@
 .Ft int
 .Fn fputs "const char * restrict str" "FILE * restrict stream"
 .Ft int
+.Fn fputs_unlocked "const char *str" "FILE *stream"
+.Ft int
 .Fn puts "const char *str"
 .Sh DESCRIPTION
 The function
@@ -58,6 +61,16 @@ to the stream pointed to by
 .\" .Dv NUL
 .\" character is not written.
 .Pp
+The
+.Fn fputs_unlocked
+function is equivalent to
+.Fn fputs ,
+except that the caller is responsible for locking the stream with
+.Xr flockfile 3
+before calling it.
+This function may be used to avoid the overhead of locking the stream and to
+prevent races when multiple threads are operating on the same stream.
+.Pp
 The function
 .Fn puts
 writes the string
diff --git a/lib/libc/stdio/fputs.c b/lib/libc/stdio/fputs.c
index b26a982421..43b66810d9 100644
--- a/lib/libc/stdio/fputs.c
+++ b/lib/libc/stdio/fputs.c
@@ -44,7 +44,7 @@
  * Write the given string to the given file.
  */
 int
-fputs(const char * __restrict s, FILE * __restrict fp)
+fputs_unlocked(const char * __restrict s, FILE * __restrict fp)
 {
 	int retval;
 	struct __suio uio;
@@ -54,9 +54,18 @@ fputs(const char * __restrict s, FILE * __restrict fp)
 	iov.iov_len = uio.uio_resid = strlen(s);
 	uio.uio_iov = &iov;
 	uio.uio_iovcnt = 1;
-	FLOCKFILE(fp);
 	ORIENT(fp, -1);
 	retval = __sfvwrite(fp, &uio);
+	return (retval);
+}
+
+int
+fputs(const char * __restrict s, FILE * __restrict fp)
+{
+	int retval;
+
+	FLOCKFILE(fp);
+	retval = fputs_unlocked(s, fp);
 	FUNLOCKFILE(fp);
 	return (retval);
 }
diff --git a/lib/libc/stdio/fread.3 b/lib/libc/stdio/fread.3
index c41c7f5bb3..b356668eb3 100644
--- a/lib/libc/stdio/fread.3
+++ b/lib/libc/stdio/fread.3
@@ -31,14 +31,15 @@
 .\"
 .\"     @(#)fread.3	8.2 (Berkeley) 3/8/94
 .\" $FreeBSD: src/lib/libc/stdio/fread.3,v 1.10 2007/01/09 00:28:06 imp Exp $
-.\" $DragonFly: src/lib/libc/stdio/fread.3,v 1.2 2003/06/17 04:26:46 dillon Exp $
 .\"
-.Dd March 8, 1994
+.Dd November 20, 2020
 .Dt FREAD 3
 .Os
 .Sh NAME
 .Nm fread ,
-.Nm fwrite
+.Nm fread_unlocked ,
+.Nm fwrite ,
+.Nm fwrite_unlocked
 .Nd binary stream input/output
 .Sh LIBRARY
 .Lb libc
@@ -47,7 +48,11 @@
 .Ft size_t
 .Fn fread "void * restrict ptr" "size_t size" "size_t nmemb" "FILE * restrict stream"
 .Ft size_t
+.Fn fread_unlocked "void * restrict ptr" "size_t size" "size_t nmemb" "FILE * restrict stream"
+.Ft size_t
 .Fn fwrite "const void * restrict ptr" "size_t size" "size_t nmemb" "FILE * restrict stream"
+.Ft size_t
+.Fn fwrite_unlocked "const void * restrict ptr" "size_t size" "size_t nmemb" "FILE * restrict stream"
 .Sh DESCRIPTION
 The function
 .Fn fread
@@ -70,6 +75,21 @@ bytes long, to the stream pointed to by
 .Fa stream ,
 obtaining them from the location given by
 .Fa ptr .
+.Pp
+The
+.Fn fread_unlocked
+and
+.Fn fwrite_unlocked
+functions are equivalent to
+.Fn fread
+and
+.Fn fwrite
+respectively, except that the caller is responsible for locking the stream
+with
+.Xr flockfile 3
+before calling them.
+These functions may be used to avoid the overhead of locking the stream
+and to prevent races when multiple threads are operating on the same stream.
 .Sh RETURN VALUES
 The functions
 .Fn fread
diff --git a/lib/libc/stdio/fread.c b/lib/libc/stdio/fread.c
index 56f86a1796..6381c24dbe 100644
--- a/lib/libc/stdio/fread.c
+++ b/lib/libc/stdio/fread.c
@@ -91,3 +91,5 @@ __fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp)
 	fp->pub._p += resid;
 	return (count);
 }
+
+__weak_reference(__fread, fread_unlocked);
diff --git a/lib/libc/stdio/fwrite.c b/lib/libc/stdio/fwrite.c
index cb8502e11f..49ccc0db87 100644
--- a/lib/libc/stdio/fwrite.c
+++ b/lib/libc/stdio/fwrite.c
@@ -44,8 +44,8 @@
  * Return the number of whole objects written.
  */
 size_t
-fwrite(const void * __restrict buf, size_t size, size_t count,
-       FILE * __restrict fp)
+fwrite_unlocked(const void * __restrict buf, size_t size, size_t count,
+    FILE * __restrict fp)
 {
 	size_t n;
 	struct __suio uio;
@@ -56,7 +56,6 @@ fwrite(const void * __restrict buf, size_t size, size_t count,
 	uio.uio_iov = &iov;
 	uio.uio_iovcnt = 1;
 
-	FLOCKFILE(fp);
 	ORIENT(fp, -1);
 	/*
 	 * The usual case is success (__sfvwrite returns 0);
@@ -65,6 +64,17 @@ fwrite(const void * __restrict buf, size_t size, size_t count,
 	 */
 	if (__sfvwrite(fp, &uio) != 0)
 	    count = (n - uio.uio_resid) / size;
-	FUNLOCKFILE(fp);
 	return (count);
 }
+
+size_t
+fwrite(const void * __restrict buf, size_t size, size_t count,
+    FILE * __restrict fp)
+{
+	size_t n;
+
+	FLOCKFILE(fp);
+	n = fwrite_unlocked(buf, size, count, fp);
+	FUNLOCKFILE(fp);
+	return (n);
+}
diff --git a/lib/libc/stdio/putc.3 b/lib/libc/stdio/putc.3
index 0d2c8b4e34..1d1f439240 100644
--- a/lib/libc/stdio/putc.3
+++ b/lib/libc/stdio/putc.3
@@ -31,13 +31,13 @@
 .\"
 .\"     @(#)putc.3	8.1 (Berkeley) 6/4/93
 .\" $FreeBSD: src/lib/libc/stdio/putc.3,v 1.16 2007/01/09 00:28:07 imp Exp $
-.\" $DragonFly: src/lib/libc/stdio/putc.3,v 1.2 2003/06/17 04:26:46 dillon Exp $
 .\"
-.Dd March 22, 2009
+.Dd November 20, 2020
 .Dt PUTC 3
 .Os
 .Sh NAME
 .Nm fputc ,
+.Nm fputc_unlocked ,
 .Nm putc ,
 .Nm putc_unlocked ,
 .Nm putchar ,
@@ -51,6 +51,8 @@
 .Ft int
 .Fn fputc "int c" "FILE *stream"
 .Ft int
+.Fn fputc_unlocked "int c" "FILE *stream"
+.Ft int
 .Fn putc "int c" "FILE *stream"
 .Ft int
 .Fn putc_unlocked "int c" "FILE *stream"
@@ -98,11 +100,13 @@ to the named output
 .Fa stream .
 .Pp
 The
-.Fn putc_unlocked
+.Fn fputc_unlocked ,
+.Fn putc_unlocked ,
 and
 .Fn putchar_unlocked
 functions are equivalent to
-.Fn putc
+.Fn fputc ,
+.Fn putc ,
 and
 .Fn putchar
 respectively,
diff --git a/lib/libc/stdio/putc.c b/lib/libc/stdio/putc.c
index c5491a7cfc..8448991fc1 100644
--- a/lib/libc/stdio/putc.c
+++ b/lib/libc/stdio/putc.c
@@ -31,7 +31,6 @@
  *
  * @(#)putc.c	8.1 (Berkeley) 6/4/93
  * $FreeBSD: src/lib/libc/stdio/putc.c,v 1.16 2008/05/05 16:03:52 jhb Exp $
- * $DragonFly: src/lib/libc/stdio/putc.c,v 1.7 2005/11/20 11:07:30 swildner Exp $
  */
 
 #include "namespace.h"
diff --git a/sys/sys/param.h b/sys/sys/param.h
index f678f01e1a..dc57835440 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -217,9 +217,10 @@
  * 500800 - 5.8 release
  * 500900 - 5.9 development
  * 500901 - fparseln() was moved from libutil to libc
+ * 500902 - unlocked flavors of fflush(), fputc(), fputs(), fread(), fwrite()
  */
 #undef __DragonFly_version
-#define __DragonFly_version 500901	/* propagated to newvers */
+#define __DragonFly_version 500902	/* propagated to newvers */
 
 #include <sys/_null.h>
 
-- 
2.28.0

