diff --git a/mediapipe/framework/formats/BUILD b/mediapipe/framework/formats/BUILD index b5d93530e4..66ca60ac7d 100644 --- a/mediapipe/framework/formats/BUILD +++ b/mediapipe/framework/formats/BUILD @@ -319,6 +319,28 @@ cc_test( ":unique_fd", "//mediapipe/framework/port:gtest_main", "//mediapipe/framework/port:status_matchers", + "//mediapipe/util:fd_test_util", + ], +) + +cc_library( + name = "shared_fd", + hdrs = ["shared_fd.h"], + deps = [ + ":unique_fd", + "@com_google_absl//absl/status:statusor", + ], +) + +cc_test( + name = "shared_fd_test", + srcs = ["shared_fd_test.cc"], + deps = [ + ":shared_fd", + ":unique_fd", + "//mediapipe/framework/port:gtest_main", + "//mediapipe/framework/port:status_matchers", + "//mediapipe/util:fd_test_util", ], ) diff --git a/mediapipe/framework/formats/shared_fd.h b/mediapipe/framework/formats/shared_fd.h new file mode 100644 index 0000000000..1c6ed105b1 --- /dev/null +++ b/mediapipe/framework/formats/shared_fd.h @@ -0,0 +1,66 @@ +#ifndef MEDIAPIPE_FRAMEWORK_FORMATS_SHARED_FD_H_ +#define MEDIAPIPE_FRAMEWORK_FORMATS_SHARED_FD_H_ + +#include +#include +#include + +#include "absl/status/statusor.h" +#include "mediapipe/framework/formats/unique_fd.h" + +namespace mediapipe { + +// Provides a shared ownership for a file descriptor. +// +// File descriptor is closed as soon as last SharedFd is destroyed. +// (Uses `std::shared_ptr` internally and can be used in the same way: copy, +// move, assign/compare with nullptr, use in conditional statements.) +class SharedFd { + public: + // `fd` a valid file descriptor. + explicit SharedFd(UniqueFd fd) + : fd_(std::make_shared(std::move(fd))) {} + + // Constructs empty SharedFd (fd == nullptr evaluates to true) + SharedFd() = default; + + ~SharedFd() = default; + + // Copyable + SharedFd(const SharedFd&) = default; + SharedFd& operator=(const SharedFd&) = default; + + // Moveable + SharedFd(SharedFd&& other) = default; + SharedFd& operator=(SharedFd&& other) = default; + + // Resets this SharedFd object (fd == nullptr will evaluate to true). + SharedFd& operator=(std::nullptr_t other) { + fd_ = other; + return *this; + } + + bool operator==(std::nullptr_t other) const { return fd_ == other; } + bool operator!=(std::nullptr_t other) const { return !operator==(other); }; + + // SharedFd can be used in conditional statements: + // ``` + // if (fd) { + // int raw_fd = fd.Get(); + // } + // ``` + explicit operator bool() const { return operator!=(nullptr); } + + // Gets raw file descriptor for read purposes. + int Get() const { return fd_->Get(); } + + // Duplicates file descriptor. + absl::StatusOr Dup() const { return fd_->Dup(); } + + private: + std::shared_ptr fd_; +}; + +} // namespace mediapipe + +#endif // MEDIAPIPE_FRAMEWORK_FORMATS_SHARED_FD_H_ diff --git a/mediapipe/framework/formats/shared_fd_test.cc b/mediapipe/framework/formats/shared_fd_test.cc new file mode 100644 index 0000000000..0d8d22e6a8 --- /dev/null +++ b/mediapipe/framework/formats/shared_fd_test.cc @@ -0,0 +1,64 @@ +#include "mediapipe/framework/formats/shared_fd.h" + +#include + +#include "mediapipe/framework/formats/unique_fd.h" +#include "mediapipe/framework/port/gtest.h" +#include "mediapipe/framework/port/status_matchers.h" +#include "mediapipe/util/fd_test_util.h" + +namespace mediapipe { +namespace { + +TEST(SharedFdTest, CanCreateFromUniqueFd) { + int raw_fd = GetValidFd(); + { + auto fd = SharedFd(UniqueFd(raw_fd)); + EXPECT_TRUE(IsFdValid(fd.Get())); + } + EXPECT_FALSE(IsFdValid(raw_fd)); +} + +TEST(SharedFdTest, CanCopyAndMoveFd) { + int raw_fd = GetValidFd(); + auto fd = SharedFd(UniqueFd(raw_fd)); + { + SharedFd copied_fd = fd; + EXPECT_TRUE(IsFdValid(copied_fd.Get())); + } + EXPECT_TRUE(IsFdValid(fd.Get())); + + { + SharedFd moved_fd = std::move(fd); + EXPECT_TRUE(IsFdValid(moved_fd.Get())); + } + EXPECT_FALSE(IsFdValid(raw_fd)); +} + +TEST(SharedFdTest, CanBeAssignedAndComparedWithNullptr) { + SharedFd fd; + EXPECT_FALSE(fd); + EXPECT_EQ(fd, nullptr); + + int raw_fd = GetValidFd(); + fd = SharedFd(UniqueFd(raw_fd)); + + EXPECT_NE(fd, nullptr); + EXPECT_TRUE(fd); + + fd = nullptr; + EXPECT_FALSE(IsFdValid(raw_fd)); + EXPECT_EQ(fd, nullptr); + EXPECT_FALSE(fd); +} + +TEST(SharedFdTest, CanDup) { + int raw_fd = GetValidFd(); + auto fd = SharedFd(UniqueFd(GetValidFd())); + MP_ASSERT_OK_AND_ASSIGN(UniqueFd dup_fd, fd.Dup()); + EXPECT_NE(dup_fd.Get(), raw_fd); + EXPECT_TRUE(IsFdValid(dup_fd.Get())); +} + +} // namespace +} // namespace mediapipe diff --git a/mediapipe/framework/formats/unique_fd_test.cc b/mediapipe/framework/formats/unique_fd_test.cc index 7a6f4556d4..a900b53be9 100644 --- a/mediapipe/framework/formats/unique_fd_test.cc +++ b/mediapipe/framework/formats/unique_fd_test.cc @@ -1,23 +1,15 @@ #include "mediapipe/framework/formats/unique_fd.h" -#include -#include - #include #include "mediapipe/framework/port/gtest.h" #include "mediapipe/framework/port/status_matchers.h" +#include "mediapipe/util/fd_test_util.h" namespace mediapipe { namespace { -// Returns a valid system file descriptor. -int GetValidFd() { return dup(STDOUT_FILENO); } - -// Helper function to check if the file descriptor is valid (still open). -int IsFdValid(int fd) { return fcntl(fd, F_GETFD) != -1; } - TEST(UniqueFdTest, ShouldInitializeInvalidFd) { UniqueFd unique_fd; EXPECT_FALSE(unique_fd.IsValid()); diff --git a/mediapipe/util/BUILD b/mediapipe/util/BUILD index 25844a580d..c388ce5529 100644 --- a/mediapipe/util/BUILD +++ b/mediapipe/util/BUILD @@ -97,6 +97,13 @@ cc_library( }), ) +cc_library( + name = "fd_test_util", + testonly = True, + hdrs = ["fd_test_util.h"], + visibility = ["//mediapipe:__subpackages__"], +) + cc_library( name = "header_util", srcs = ["header_util.cc"], diff --git a/mediapipe/util/fd_test_util.h b/mediapipe/util/fd_test_util.h new file mode 100644 index 0000000000..0d2241d91c --- /dev/null +++ b/mediapipe/util/fd_test_util.h @@ -0,0 +1,17 @@ +#ifndef MEDIAPIPE_UTIL_FD_TEST_UTIL_H_ +#define MEDIAPIPE_UTIL_FD_TEST_UTIL_H_ + +#include +#include + +namespace mediapipe { + +// Returns a valid system file descriptor. +inline int GetValidFd() { return dup(STDOUT_FILENO); } + +// Helper function to check if the file descriptor is valid (still open). +inline int IsFdValid(int fd) { return fcntl(fd, F_GETFD) != -1; } + +} // namespace mediapipe + +#endif // MEDIAPIPE_UTIL_FD_TEST_UTIL_H_