[libc] Add GNU extention functions fread_unlocked and fwrite_unlocked.
authorSiva Chandra Reddy <sivachandra@google.com>
Tue, 19 Apr 2022 07:36:43 +0000 (07:36 +0000)
committerSiva Chandra Reddy <sivachandra@google.com>
Wed, 20 Apr 2022 15:39:27 +0000 (15:39 +0000)
POSIX locking and unlocking functions flockfile and funlockfile have
also been added. The locking is not recursive yet. A future patch will
make the underlying lock a recursive lock.

Reviewed By: lntue

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

18 files changed:
libc/config/linux/x86_64/entrypoints.txt
libc/spec/gnu_ext.td
libc/spec/posix.td
libc/spec/spec.td
libc/spec/stdc.td
libc/src/__support/File/file.cpp
libc/src/__support/File/file.h
libc/src/stdio/CMakeLists.txt
libc/src/stdio/flockfile.cpp [new file with mode: 0644]
libc/src/stdio/flockfile.h [new file with mode: 0644]
libc/src/stdio/fread_unlocked.cpp [new file with mode: 0644]
libc/src/stdio/fread_unlocked.h [new file with mode: 0644]
libc/src/stdio/funlockfile.cpp [new file with mode: 0644]
libc/src/stdio/funlockfile.h [new file with mode: 0644]
libc/src/stdio/fwrite_unlocked.cpp [new file with mode: 0644]
libc/src/stdio/fwrite_unlocked.h [new file with mode: 0644]
libc/test/src/stdio/CMakeLists.txt
libc/test/src/stdio/unlocked_fileop_test.cpp [new file with mode: 0644]

index 5fc5d19..c7af459 100644 (file)
@@ -252,10 +252,14 @@ if(LLVM_LIBC_FULL_BUILD)
 
     # stdio.h entrypoints
     libc.src.stdio.fclose
+    libc.src.stdio.flockfile
     libc.src.stdio.fopen
     libc.src.stdio.fread
+    libc.src.stdio.fread_unlocked
     libc.src.stdio.fseek
+    libc.src.stdio.funlockfile
     libc.src.stdio.fwrite
+    libc.src.stdio.fwrite_unlocked
 
     # signal.h entrypoints
     # TODO: Enable signal.h entrypoints after fixing signal.h
index 99b78de..14ede61 100644 (file)
@@ -65,10 +65,36 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> {
       ]
   >;
 
+  HeaderSpec StdIO = HeaderSpec<
+      "stdio.h",
+      [], // Macros
+      [], // Types
+      [], // Enumerations
+      [
+          FunctionSpec<
+              "fread_unlocked",
+              RetValSpec<SizeTType>,
+              [ArgSpec<VoidRestrictedPtr>,
+               ArgSpec<SizeTType>,
+               ArgSpec<SizeTType>,
+               ArgSpec<FILERestrictedPtr>]
+          >,
+          FunctionSpec<
+              "fwrite_unlocked",
+              RetValSpec<SizeTType>,
+              [ArgSpec<ConstVoidRestrictedPtr>,
+               ArgSpec<SizeTType>,
+               ArgSpec<SizeTType>,
+               ArgSpec<FILERestrictedPtr>]
+          >,
+      ]
+   >;
+
   let Headers = [
     CType,
     FEnv,
-    Math, 
+    Math,
+    StdIO,
     String,
   ];
 }
index 13b4ed4..fbb6a78 100644 (file)
@@ -502,12 +502,32 @@ def POSIX : StandardSpec<"POSIX"> {
     ]
   >;
 
+  HeaderSpec StdIO = HeaderSpec<
+      "stdio.h",
+      [], // Macros
+      [], // Types
+      [], // Enumerations
+      [
+          FunctionSpec<
+              "flockfile",
+              RetValSpec<VoidType>,
+              [ArgSpec<FILEPtr>]
+          >,
+          FunctionSpec<
+              "funlockfile",
+              RetValSpec<VoidType>,
+              [ArgSpec<FILEPtr>]
+          >,
+      ]
+  >;
+
   let Headers = [
     CType,
     Errno,
     FCntl,
     PThread,
     Signal,
+    StdIO,
     StdLib,
     SysMMan,
     SysStat,
index 5c947e4..1989007 100644 (file)
@@ -103,6 +103,10 @@ def QSortCompareT : NamedType<"__qsortcompare_t">;
 
 def AtexitHandlerT : NamedType<"__atexithandler_t">;
 
+def FILE : NamedType<"FILE">;
+def FILEPtr : PtrType<FILE>;
+def FILERestrictedPtr : RestrictedPtrType<FILE>;
+
 //added because __assert_fail needs it.
 def UnsignedType : NamedType<"unsigned">;
 
index 7f57a2d..10a980b 100644 (file)
@@ -1,8 +1,5 @@
 def StdC : StandardSpec<"stdc"> {
 
-  NamedType FILE = NamedType<"FILE">;
-  PtrType FILEPtr = PtrType<FILE>;
-  RestrictedPtrType FILERestrictedPtr = RestrictedPtrType<FILE>;
   NamedType StructTmType = NamedType<"struct tm">;
   PtrType StructTmPtr = PtrType<StructTmType>;
   PtrType TimeTTypePtr = PtrType<TimeTType>;
index 3f2928c..95f464d 100644 (file)
@@ -16,9 +16,7 @@
 
 namespace __llvm_libc {
 
-size_t File::write(const void *data, size_t len) {
-  FileLock lock(this);
-
+size_t File::write_unlocked(const void *data, size_t len) {
   if (!write_allowed()) {
     errno = EBADF;
     err = true;
@@ -76,9 +74,7 @@ size_t File::write(const void *data, size_t len) {
   return len;
 }
 
-size_t File::read(void *data, size_t len) {
-  FileLock lock(this);
-
+size_t File::read_unlocked(void *data, size_t len) {
   if (!read_allowed()) {
     errno = EBADF;
     err = true;
index 26849ad..fdc0d45 100644 (file)
@@ -145,11 +145,27 @@ public:
     f->eof = f->err = false;
   }
 
-  // Buffered write of |len| bytes from |data|.
-  size_t write(const void *data, size_t len);
+  // Buffered write of |len| bytes from |data| without the file lock.
+  size_t write_unlocked(const void *data, size_t len);
+
+  // Buffered write of |len| bytes from |data| under the file lock.
+  size_t write(const void *data, size_t len) {
+    lock();
+    size_t ret = write_unlocked(data, len);
+    unlock();
+    return ret;
+  }
+
+  // Buffered read of |len| bytes into |data| without the file lock.
+  size_t read_unlocked(void *data, size_t len);
 
-  // Buffered read of |len| bytes into |data|.
-  size_t read(void *data, size_t len);
+  // Buffered read of |len| bytes into |data| under the file lock.
+  size_t read(void *data, size_t len) {
+    lock();
+    size_t ret = read_unlocked(data, len);
+    unlock();
+    return ret;
+  }
 
   int seek(long offset, int whence);
 
index 19a3cd1..4932442 100644 (file)
@@ -25,6 +25,42 @@ add_entrypoint_object(
 )
 
 add_entrypoint_object(
+  flockfile
+  SRCS
+    flockfile.cpp
+  HDRS
+    flockfile.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  funlockfile
+  SRCS
+    funlockfile.cpp
+  HDRS
+    funlockfile.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
+  fread_unlocked
+  SRCS
+    fread_unlocked.cpp
+  HDRS
+    fread_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
   fread
   SRCS
     fread.cpp
@@ -37,6 +73,18 @@ add_entrypoint_object(
 )
 
 add_entrypoint_object(
+  fwrite_unlocked
+  SRCS
+    fwrite_unlocked.cpp
+  HDRS
+    fwrite_unlocked.h
+  DEPENDS
+    libc.include.stdio
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+)
+
+add_entrypoint_object(
   fwrite
   SRCS
     fwrite.cpp
diff --git a/libc/src/stdio/flockfile.cpp b/libc/src/stdio/flockfile.cpp
new file mode 100644 (file)
index 0000000..3b13a6f
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation of flockfile ---------------------------------------===//
+//
+// 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 "src/stdio/flockfile.h"
+#include "src/__support/File/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(void, flockfile, (::FILE * stream)) {
+  reinterpret_cast<__llvm_libc::File *>(stream)->lock();
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/flockfile.h b/libc/src/stdio/flockfile.h
new file mode 100644 (file)
index 0000000..961ad8f
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation header of flockfile ----------------------*- 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_STDIO_FLOCKFILE_H
+#define LLVM_LIBC_SRC_STDIO_FLOCKFILE_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+void flockfile(::FILE *stream);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FLOCKFILE_H
diff --git a/libc/src/stdio/fread_unlocked.cpp b/libc/src/stdio/fread_unlocked.cpp
new file mode 100644 (file)
index 0000000..162cc4e
--- /dev/null
@@ -0,0 +1,23 @@
+//===-- Implementation of fread_unlocked ----------------------------------===//
+//
+// 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 "src/stdio/fread_unlocked.h"
+#include "src/__support/File/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(size_t, fread_unlocked,
+                   (void *__restrict buffer, size_t size, size_t nmemb,
+                    ::FILE *stream)) {
+  return reinterpret_cast<__llvm_libc::File *>(stream)->read_unlocked(
+      buffer, size * nmemb);
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/fread_unlocked.h b/libc/src/stdio/fread_unlocked.h
new file mode 100644 (file)
index 0000000..5c965b0
--- /dev/null
@@ -0,0 +1,21 @@
+//===-- Implementation header of fread_unlocked -----------------*- 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_STDIO_FREAD_UNLOCKED_H
+#define LLVM_LIBC_SRC_STDIO_FREAD_UNLOCKED_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+size_t fread_unlocked(void *__restrict buffer, size_t size, size_t nmemb,
+                      ::FILE *stream);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FREAD_UNLOCKED_H
diff --git a/libc/src/stdio/funlockfile.cpp b/libc/src/stdio/funlockfile.cpp
new file mode 100644 (file)
index 0000000..6c7dbee
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation of funlockfile -------------------------------------===//
+//
+// 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 "src/stdio/funlockfile.h"
+#include "src/__support/File/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(void, funlockfile, (::FILE * stream)) {
+  reinterpret_cast<__llvm_libc::File *>(stream)->unlock();
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/funlockfile.h b/libc/src/stdio/funlockfile.h
new file mode 100644 (file)
index 0000000..cb04b7e
--- /dev/null
@@ -0,0 +1,20 @@
+//===-- Implementation header of funlockfile --------------------*- 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_STDIO_FUNLOCKFILE_H
+#define LLVM_LIBC_SRC_STDIO_FUNLOCKFILE_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+void funlockfile(::FILE *stream);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FUNLOCKFILE_H
diff --git a/libc/src/stdio/fwrite_unlocked.cpp b/libc/src/stdio/fwrite_unlocked.cpp
new file mode 100644 (file)
index 0000000..085b775
--- /dev/null
@@ -0,0 +1,23 @@
+//===-- Implementation of fwrite_unlocked ---------------------------------===//
+//
+// 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 "src/stdio/fwrite_unlocked.h"
+#include "src/__support/File/file.h"
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(size_t, fwrite_unlocked,
+                   (const void *__restrict buffer, size_t size, size_t nmemb,
+                    ::FILE *stream)) {
+  return reinterpret_cast<__llvm_libc::File *>(stream)->write_unlocked(
+      buffer, size * nmemb);
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/stdio/fwrite_unlocked.h b/libc/src/stdio/fwrite_unlocked.h
new file mode 100644 (file)
index 0000000..33bcc21
--- /dev/null
@@ -0,0 +1,21 @@
+//===-- Implementation header of fwrite_unlocked ----------------*- 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_STDIO_FWRITE_UNLOCKED_H
+#define LLVM_LIBC_SRC_STDIO_FWRITE_UNLOCKED_H
+
+#include <stdio.h>
+
+namespace __llvm_libc {
+
+size_t fwrite_unlocked(const void *__restrict ptr, size_t size, size_t nmemb,
+                       ::FILE *__restrict stream);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_FWRITE_UNLOCKED_H
index 4571759..6a955ba 100644 (file)
@@ -14,6 +14,21 @@ add_libc_unittest(
     libc.src.stdio.fwrite
 )
 
+add_libc_unittest(
+  unlocked_fileop_test
+  SUITE
+    libc_stdio_unittests
+  SRCS
+    unlocked_fileop_test.cpp
+  DEPENDS
+    libc.src.stdio.fclose
+    libc.src.stdio.flockfile
+    libc.src.stdio.fopen
+    libc.src.stdio.fread_unlocked
+    libc.src.stdio.funlockfile
+    libc.src.stdio.fwrite_unlocked
+)
+
 add_subdirectory(printf_core)
 
 add_subdirectory(testdata)
diff --git a/libc/test/src/stdio/unlocked_fileop_test.cpp b/libc/test/src/stdio/unlocked_fileop_test.cpp
new file mode 100644 (file)
index 0000000..6e936ef
--- /dev/null
@@ -0,0 +1,44 @@
+//===-- Unittests for file operations like fopen, flcose etc --------------===//
+//
+// 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 "src/stdio/fclose.h"
+#include "src/stdio/flockfile.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fread_unlocked.h"
+#include "src/stdio/funlockfile.h"
+#include "src/stdio/fwrite_unlocked.h"
+#include "utils/UnitTest/Test.h"
+
+#include <stdio.h>
+
+TEST(LlvmLibcFILETest, UnlockedReadAndWrite) {
+  constexpr char FILENAME[] = "testdata/unlocked_read_and_write.test";
+  ::FILE *file = __llvm_libc::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+  constexpr char CONTENT[] = "1234567890987654321";
+  __llvm_libc::flockfile(file);
+  ASSERT_EQ(sizeof(CONTENT) - 1, __llvm_libc::fwrite_unlocked(
+                                     CONTENT, 1, sizeof(CONTENT) - 1, file));
+  __llvm_libc::funlockfile(file);
+  ASSERT_EQ(0, __llvm_libc::fclose(file));
+
+  file = __llvm_libc::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  constexpr size_t READ_SIZE = 5;
+  char data[READ_SIZE * 2 + 1];
+  data[READ_SIZE * 2] = '\0';
+  __llvm_libc::flockfile(file);
+  ASSERT_EQ(__llvm_libc::fread_unlocked(data, 1, READ_SIZE, file), READ_SIZE);
+  ASSERT_EQ(__llvm_libc::fread_unlocked(data + READ_SIZE, 1, READ_SIZE, file),
+            READ_SIZE);
+  __llvm_libc::funlockfile(file);
+  ASSERT_STREQ(data, "1234567890");
+
+  ASSERT_EQ(__llvm_libc::fclose(file), 0);
+}