diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -252,10 +252,14 @@ # 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 diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -65,10 +65,36 @@ ] >; + HeaderSpec StdIO = HeaderSpec< + "stdio.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "fread_unlocked", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] + >, + FunctionSpec< + "fwrite_unlocked", + RetValSpec, + [ArgSpec, + ArgSpec, + ArgSpec, + ArgSpec] + >, + ] + >; + let Headers = [ CType, FEnv, - Math, + Math, + StdIO, String, ]; } diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -502,12 +502,32 @@ ] >; + HeaderSpec StdIO = HeaderSpec< + "stdio.h", + [], // Macros + [], // Types + [], // Enumerations + [ + FunctionSpec< + "flockfile", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "funlockfile", + RetValSpec, + [ArgSpec] + >, + ] + >; + let Headers = [ CType, Errno, FCntl, PThread, Signal, + StdIO, StdLib, SysMMan, SysStat, diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -103,6 +103,10 @@ def AtexitHandlerT : NamedType<"__atexithandler_t">; +def FILE : NamedType<"FILE">; +def FILEPtr : PtrType; +def FILERestrictedPtr : RestrictedPtrType; + //added because __assert_fail needs it. def UnsignedType : NamedType<"unsigned">; diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -1,8 +1,5 @@ def StdC : StandardSpec<"stdc"> { - NamedType FILE = NamedType<"FILE">; - PtrType FILEPtr = PtrType; - RestrictedPtrType FILERestrictedPtr = RestrictedPtrType; NamedType StructTmType = NamedType<"struct tm">; PtrType StructTmPtr = PtrType; PtrType TimeTTypePtr = PtrType; diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -145,11 +145,27 @@ 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); diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp --- a/libc/src/__support/File/file.cpp +++ b/libc/src/__support/File/file.cpp @@ -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 @@ 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; diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -24,6 +24,42 @@ libc.src.__support.File.platform_file ) +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 @@ -36,6 +72,18 @@ libc.src.__support.File.platform_file ) +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 diff --git a/libc/src/stdio/flockfile.h b/libc/src/stdio/flockfile.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/flockfile.h @@ -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 + +namespace __llvm_libc { + +void flockfile(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FLOCKFILE_H diff --git a/libc/src/stdio/flockfile.cpp b/libc/src/stdio/flockfile.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/flockfile.cpp @@ -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 + +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/fread_unlocked.h b/libc/src/stdio/fread_unlocked.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fread_unlocked.h @@ -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 + +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/fread_unlocked.cpp b/libc/src/stdio/fread_unlocked.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fread_unlocked.cpp @@ -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 + +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/funlockfile.h b/libc/src/stdio/funlockfile.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/funlockfile.h @@ -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 + +namespace __llvm_libc { + +void funlockfile(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FUNLOCKFILE_H diff --git a/libc/src/stdio/funlockfile.cpp b/libc/src/stdio/funlockfile.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/funlockfile.cpp @@ -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 + +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/fwrite_unlocked.h b/libc/src/stdio/fwrite_unlocked.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fwrite_unlocked.h @@ -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 + +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 diff --git a/libc/src/stdio/fwrite_unlocked.cpp b/libc/src/stdio/fwrite_unlocked.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/fwrite_unlocked.cpp @@ -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 + +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/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -14,6 +14,21 @@ 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 --- /dev/null +++ b/libc/test/src/stdio/unlocked_fileop_test.cpp @@ -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 + +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); +}