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 @@ -56,16 +56,6 @@ // file position indicator. using SeekFunc = ErrorOr(File *, long, int); using CloseFunc = int(File *); - // CleanupFunc is a function which does the equivalent of this: - // - // void my_file_cleanup(File *f) { - // MyFile *file = reinterpret_cast(f); - // delete file; - // } - // - // Essentially, it a function which calls the delete operator on the - // platform file object to cleanup resources held by it. - using CleanupFunc = void(File *); using ModeFlags = uint32_t; @@ -102,7 +92,6 @@ ReadFunc *platform_read; SeekFunc *platform_seek; CloseFunc *platform_close; - CleanupFunc *platform_cleanup; Mutex mutex; @@ -150,26 +139,6 @@ FileLock(FileLock &&) = delete; }; - // This is private function and is not to be called by the users of - // File and its derived classes. The correct way to close a file is - // to call the File::cleanup function. - int close() { - { - FileLock lock(this); - if (prev_op == FileOp::WRITE && pos > 0) { - auto buf_result = platform_write(this, buf, pos); - if (buf_result.has_error() || buf_result.value < pos) { - err = true; - return buf_result.error; - } - } - int result = platform_close(this); - if (result != 0) - return result; - } - return 0; - } - protected: constexpr bool write_allowed() const { return mode & (static_cast(OpenMode::WRITE) | @@ -200,28 +169,17 @@ // is zero. This way, we will not have to employ the semantics of // the set_buffer method and allocate a buffer. constexpr File(WriteFunc *wf, ReadFunc *rf, SeekFunc *sf, CloseFunc *cf, - CleanupFunc *clf, uint8_t *buffer, size_t buffer_size, - int buffer_mode, bool owned, ModeFlags modeflags) + uint8_t *buffer, size_t buffer_size, int buffer_mode, + bool owned, ModeFlags modeflags) : platform_write(wf), platform_read(rf), platform_seek(sf), - platform_close(cf), platform_cleanup(clf), mutex(false, false, false), - ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), - own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE), - read_limit(0), eof(false), err(false) { + platform_close(cf), mutex(false, false, false), ungetc_buf(0), + buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned), + mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0), + eof(false), err(false) { if constexpr (ENABLE_BUFFER) adjust_buf(); } - // Close |f| and cleanup resources held by it. - // Returns the non-zero error value if an error occurs when closing the - // file. - static int cleanup(File *f) { - int close_result = f->close(); - if (close_result != 0) - return close_result; - f->platform_cleanup(f); - return 0; - } - // Buffered write of |len| bytes from |data| without the file lock. FileIOResult write_unlocked(const void *data, size_t len); @@ -261,6 +219,28 @@ return ungetc_unlocked(c); } + // Does the following: + // 1. If in write mode, Write out any data present in the buffer. + // 2. Call platform_close. + // platform_close is expected to cleanup the complete file object. + int close() { + { + FileLock lock(this); + if (prev_op == FileOp::WRITE && pos > 0) { + auto buf_result = platform_write(this, buf, pos); + if (buf_result.has_error() || buf_result.value < pos) { + err = true; + return buf_result.error; + } + } + } + // Platform close is expected to cleanup the file data structure which + // includes the file mutex. Hence, we call platform_close after releasing + // the file lock. Another thread doing file operations while a thread is + // closing the file is undefined behavior as per POSIX. + return platform_close(this); + } + // Sets the internal buffer to |buffer| with buffering mode |mode|. // |size| is the size of |buffer|. If |size| is non-zero, but |buffer| // is nullptr, then a buffer owned by this file will be allocated. @@ -331,15 +311,6 @@ } }; -// Platform specific file implementations can simply pass a pointer to a -// a specialization of this function as the CleanupFunc argument to the -// File constructor. The template type argument FileType should replaced -// with the type of the platform specific file implementation. -template void cleanup_file(File *f) { - auto *file = reinterpret_cast(f); - delete file; -} - // The implementaiton of this function is provided by the platfrom_file // library. ErrorOr openfile(const char *path, const char *mode); diff --git a/libc/src/__support/File/gpu/file.cpp b/libc/src/__support/File/gpu/file.cpp --- a/libc/src/__support/File/gpu/file.cpp +++ b/libc/src/__support/File/gpu/file.cpp @@ -26,8 +26,8 @@ public: constexpr GPUFile(uintptr_t file, File::ModeFlags modeflags) - : File(&write_func, nullptr, nullptr, nullptr, nullptr, nullptr, 0, - _IONBF, false, modeflags), + : File(&write_func, nullptr, nullptr, nullptr, nullptr, 0, _IONBF, false, + modeflags), file(file) {} uintptr_t get_file() const { return file; } diff --git a/libc/src/__support/File/linux/file.cpp b/libc/src/__support/File/linux/file.cpp --- a/libc/src/__support/File/linux/file.cpp +++ b/libc/src/__support/File/linux/file.cpp @@ -33,9 +33,8 @@ public: constexpr LinuxFile(int file_descriptor, uint8_t *buffer, size_t buffer_size, int buffer_mode, bool owned, File::ModeFlags modeflags) - : File(&write_func, &read_func, &seek_func, &close_func, - &cleanup_file, buffer, buffer_size, buffer_mode, owned, - modeflags), + : File(&write_func, &read_func, &seek_func, &close_func, buffer, + buffer_size, buffer_mode, owned, modeflags), fd(file_descriptor) {} int get_fd() const { return fd; } @@ -87,6 +86,7 @@ if (ret < 0) { return -ret; } + delete lf; return 0; } diff --git a/libc/src/stdio/fclose.cpp b/libc/src/stdio/fclose.cpp --- a/libc/src/stdio/fclose.cpp +++ b/libc/src/stdio/fclose.cpp @@ -15,8 +15,7 @@ namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, fclose, (::FILE * stream)) { - auto *file = reinterpret_cast<__llvm_libc::File *>(stream); - int result = File::cleanup(file); + int result = reinterpret_cast<__llvm_libc::File *>(stream)->close(); if (result != 0) { libc_errno = result; return EOF; diff --git a/libc/src/stdio/fopencookie.cpp b/libc/src/stdio/fopencookie.cpp --- a/libc/src/stdio/fopencookie.cpp +++ b/libc/src/stdio/fopencookie.cpp @@ -31,8 +31,8 @@ CookieFile(void *c, cookie_io_functions_t cops, uint8_t *buffer, size_t bufsize, File::ModeFlags mode) : File(&cookie_write, &cookie_read, &CookieFile::cookie_seek, - &cookie_close, &cleanup_file, buffer, bufsize, - 0 /* default buffering mode */, true /* File owns buffer */, mode), + &cookie_close, buffer, bufsize, 0 /* default buffering mode */, + true /* File owns buffer */, mode), cookie(c), ops(cops) {} }; @@ -69,7 +69,11 @@ auto cookie_file = reinterpret_cast(f); if (cookie_file->ops.close == nullptr) return 0; - return cookie_file->ops.close(cookie_file->cookie); + int retval = cookie_file->ops.close(cookie_file->cookie); + if (retval != 0) + return retval; + delete cookie_file; + return 0; } } // anonymous namespace diff --git a/libc/test/src/__support/File/file_test.cpp b/libc/test/src/__support/File/file_test.cpp --- a/libc/test/src/__support/File/file_test.cpp +++ b/libc/test/src/__support/File/file_test.cpp @@ -32,13 +32,15 @@ static FileIOResult str_write(__llvm_libc::File *f, const void *data, size_t len); static ErrorOr str_seek(__llvm_libc::File *f, long offset, int whence); - static int str_close(__llvm_libc::File *f) { return 0; } + static int str_close(__llvm_libc::File *f) { + delete reinterpret_cast(f); + return 0; + } public: explicit StringFile(char *buffer, size_t buflen, int bufmode, bool owned, ModeFlags modeflags) : __llvm_libc::File(&str_write, &str_read, &str_seek, &str_close, - &__llvm_libc::cleanup_file, reinterpret_cast(buffer), buflen, bufmode, owned, modeflags), pos(0), eof_marker(0), write_append(false) { @@ -145,7 +147,7 @@ EXPECT_TRUE(result.has_error()); } - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, WriteLineBuffered) { @@ -204,8 +206,8 @@ EXPECT_MEM_EQ(src3, dst_line_final); EXPECT_MEM_EQ(src3, dst_full_final); - ASSERT_EQ(File::cleanup(f_line), 0); - ASSERT_EQ(File::cleanup(f_full), 0); + ASSERT_EQ(f_line->close(), 0); + ASSERT_EQ(f_full->close(), 0); } TEST(LlvmLibcFileTest, WriteUnbuffered) { @@ -220,7 +222,7 @@ sizeof(data)); // no buffering means this is written immediately. EXPECT_STREQ(f->get_str(), data); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, ReadOnly) { @@ -273,7 +275,7 @@ EXPECT_TRUE(result.has_error()); } - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, ReadSeekCurAndRead) { @@ -295,7 +297,7 @@ ASSERT_EQ(f->seek(-5, SEEK_CUR).value(), 0); ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1); ASSERT_STREQ(data, "9098"); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, AppendOnly) { @@ -325,7 +327,7 @@ EXPECT_EQ(f->flush(), int(0)); EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content)); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, WriteUpdate) { @@ -345,7 +347,7 @@ ASSERT_EQ(f->read(read_data, sizeof(data)).value, sizeof(data)); EXPECT_STREQ(read_data, data); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, ReadUpdate) { @@ -378,7 +380,7 @@ src2(write_data, sizeof(write_data)); EXPECT_MEM_EQ(src2, dst2); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, AppendUpdate) { @@ -420,7 +422,7 @@ MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE); EXPECT_MEM_EQ(src4, dst4); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, SmallBuffer) { @@ -437,7 +439,7 @@ EXPECT_EQ(f->get_pos(), sizeof(WRITE_DATA)); ASSERT_STREQ(f->get_str(), WRITE_DATA); - ASSERT_EQ(File::cleanup(f), 0); + ASSERT_EQ(f->close(), 0); } TEST(LlvmLibcFileTest, ZeroLengthBuffer) { @@ -459,9 +461,9 @@ ASSERT_STREQ(f_lbf->get_str(), WRITE_DATA); ASSERT_STREQ(f_nbf->get_str(), WRITE_DATA); - ASSERT_EQ(File::cleanup(f_fbf), 0); - ASSERT_EQ(File::cleanup(f_lbf), 0); - ASSERT_EQ(File::cleanup(f_nbf), 0); + ASSERT_EQ(f_fbf->close(), 0); + ASSERT_EQ(f_lbf->close(), 0); + ASSERT_EQ(f_nbf->close(), 0); } TEST(LlvmLibcFileTest, WriteNothing) { @@ -486,7 +488,7 @@ ASSERT_FALSE(f_lbf->error_unlocked()); ASSERT_FALSE(f_nbf->error_unlocked()); - ASSERT_EQ(File::cleanup(f_fbf), 0); - ASSERT_EQ(File::cleanup(f_lbf), 0); - ASSERT_EQ(File::cleanup(f_nbf), 0); + ASSERT_EQ(f_fbf->close(), 0); + ASSERT_EQ(f_lbf->close(), 0); + ASSERT_EQ(f_nbf->close(), 0); } diff --git a/libc/test/src/__support/File/platform_file_test.cpp b/libc/test/src/__support/File/platform_file_test.cpp --- a/libc/test/src/__support/File/platform_file_test.cpp +++ b/libc/test/src/__support/File/platform_file_test.cpp @@ -25,7 +25,7 @@ File *file = openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); @@ -38,7 +38,7 @@ ASSERT_EQ(file->read(data, TEXT_SIZE).value, size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, CreateWriteSeekAndReadBack) { @@ -58,7 +58,7 @@ ASSERT_EQ(file->read(data, TEXT_SIZE).value, size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, CreateAppendCloseAndReadBack) { @@ -66,14 +66,14 @@ File *file = openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "a"); ASSERT_FALSE(file == nullptr); constexpr char APPEND_TEXT[] = " Append Text"; constexpr size_t APPEND_TEXT_SIZE = sizeof(APPEND_TEXT) - 1; ASSERT_EQ(file->write(APPEND_TEXT, APPEND_TEXT_SIZE).value, APPEND_TEXT_SIZE); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); @@ -87,7 +87,7 @@ ASSERT_EQ(file->read(data, READ_SIZE).value, size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, CreateAppendSeekAndReadBack) { @@ -95,7 +95,7 @@ File *file = openfile(FILENAME, "w"); ASSERT_FALSE(file == nullptr); ASSERT_EQ(file->write(TEXT, TEXT_SIZE).value, TEXT_SIZE); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "a+"); ASSERT_FALSE(file == nullptr); @@ -113,7 +113,7 @@ ASSERT_EQ(file->read(data, APPEND_TEXT_SIZE).value, size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, LargeFile) { @@ -131,7 +131,7 @@ for (int i = 0; i < REPEAT; ++i) { ASSERT_EQ(file->write(write_data, DATA_SIZE).value, DATA_SIZE); } - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); @@ -146,7 +146,7 @@ ASSERT_EQ(file->read(data, 1).value, size_t(0)); ASSERT_TRUE(file->iseof()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, ReadSeekCurAndRead) { @@ -156,7 +156,7 @@ constexpr char CONTENT[] = "1234567890987654321"; ASSERT_EQ(sizeof(CONTENT) - 1, file->write(CONTENT, sizeof(CONTENT) - 1).value); - ASSERT_EQ(0, File::cleanup(file)); + ASSERT_EQ(0, file->close()); file = openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); @@ -173,7 +173,7 @@ ASSERT_EQ(file->read(data, READ_SIZE - 1).value, READ_SIZE - 1); ASSERT_STREQ(data, "9098"); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, IncorrectOperation) { @@ -185,21 +185,21 @@ ASSERT_EQ(file->read(data, 1).value, size_t(0)); // Cannot read ASSERT_FALSE(file->iseof()); ASSERT_TRUE(file->error()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "r"); ASSERT_FALSE(file == nullptr); ASSERT_EQ(file->write(data, 1).value, size_t(0)); // Cannot write ASSERT_FALSE(file->iseof()); ASSERT_TRUE(file->error()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); file = openfile(FILENAME, "a"); ASSERT_FALSE(file == nullptr); ASSERT_EQ(file->read(data, 1).value, size_t(0)); // Cannot read ASSERT_FALSE(file->iseof()); ASSERT_TRUE(file->error()); - ASSERT_EQ(File::cleanup(file), 0); + ASSERT_EQ(file->close(), 0); } TEST(LlvmLibcPlatformFileTest, StdOutStdErrSmokeTest) {