diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index b4ba548d211146a66218f575cdc52fdaec5dd8b9..1167c668ab0fcae6f3bb85690b63d2e5eefefa5a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required (VERSION 2.6) set (COMMON_LIB_SRC_FILES + SmartFd.cpp Timer.cpp Utils.cpp exception/Backtrace.cpp @@ -20,6 +21,7 @@ target_link_libraries (ctacommon uuid) set (COMMON_UNIT_TESTS_LIB_SRC_FILES + SmartFdTest.cpp UtilsTest.cpp) add_library (ctacommonunittests SHARED diff --git a/common/SmartFd.cpp b/common/SmartFd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b58df0bac7f3dbbea173d27750f57ce366c287a --- /dev/null +++ b/common/SmartFd.cpp @@ -0,0 +1,111 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "common/exception/Exception.hpp" +#include "common/SmartFd.hpp" + +#include <errno.h> +#include <unistd.h> + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +cta::SmartFd::SmartFd() throw(): + m_fd(-1), m_closedCallback(NULL) { +} + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +cta::SmartFd::SmartFd(const int fd) throw(): + m_fd(fd), m_closedCallback(NULL) { +} + +//----------------------------------------------------------------------------- +// setClosedCallback +//----------------------------------------------------------------------------- +void cta::SmartFd::setClosedCallback(ClosedCallback closedCallback) + throw() { + m_closedCallback = closedCallback; +} + +//----------------------------------------------------------------------------- +// reset +//----------------------------------------------------------------------------- +void cta::SmartFd::reset(const int fd = -1) throw() { + // If the new file descriptor is not the one already owned + if(fd != m_fd) { + + // If this SmartFd still owns a file descriptor, then close it + if(m_fd >= 0) { + close(m_fd); + if(m_closedCallback) { + try { + (*m_closedCallback)(m_fd); + } catch(...) { + // Ignore any exception thrown my the m_closedCallback function + // because this reset function maybe called by the destructor of + // SmartFd + } + } + } + + // Take ownership of the new file descriptor + m_fd = fd; + } +} + +//----------------------------------------------------------------------------- +// SmartFd assignment operator +//----------------------------------------------------------------------------- +cta::SmartFd &cta::SmartFd::operator=(SmartFd& obj) { + reset(obj.release()); + return *this; +} + +//----------------------------------------------------------------------------- +// destructor +//----------------------------------------------------------------------------- +cta::SmartFd::~SmartFd() { + reset(); +} + +//----------------------------------------------------------------------------- +// get +//----------------------------------------------------------------------------- +int cta::SmartFd::get() const throw() { + return m_fd; +} + +//----------------------------------------------------------------------------- +// release +//----------------------------------------------------------------------------- +int cta::SmartFd::release() { + // If this SmartFd does not own a file descriptor + if(m_fd < 0) { + throw exception::Exception("Smart file-descriptor does not own a" + " file-descriptor"); + } + + const int tmpFd = m_fd; + + // A negative number indicates this SmartFd does not own a file descriptor + m_fd = -1; + + return tmpFd; +} diff --git a/common/SmartFd.hpp b/common/SmartFd.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ff67f4e406ff39e189977f6fecefff9aad959793 --- /dev/null +++ b/common/SmartFd.hpp @@ -0,0 +1,144 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +namespace cta { + +/** + * A smart file descriptor that owns a basic file descriptor. When the smart + * file descriptor goes out of scope, it will close the file descriptor it + * owns. + */ +class SmartFd { +public: + + /** + * A pointer to a callback function that will called by the Smart + * immediately after the SmartFd has closed the file-descriptor it owns. + * + * Please note that any exception thrown by this function will be ignored + * because this function maybe called by the destructor of SmartFd. + * + * @param closedFd The value of the file descriptor that was closed. + */ + typedef void (*ClosedCallback)(int closedFd); + + /** + * Constructor. + * + */ + SmartFd() throw(); + + /** + * Constructor. + * + * @param fd The file descriptor to be owned by the smart file + * descriptor. + */ + SmartFd(const int fd) throw(); + + /** + * Sets the function to be called back by the SmartFd immediately after + * the SmartFd has closed the file-descriptor it owns. + * + * Setting the callback function to NULL means that no function will be + * called. + * + * Please note any exception thrown by the callback function will be + * ignored because the callback function maybe called by the destructor of + * SmartFd. + * + * @param closedCallback This function will be called immediately after + * the SmartFd has closed the file-descriptor it owns. + * Please note that any exception thrown by this + * function will be ignored because this function + * maybe called by the destructor of SmartFd. + */ + void setClosedCallback(ClosedCallback closedCallback) throw(); + + /** + * Take ownership of the specified file descriptor, closing the previously + * owned file descriptor if there is one and it is not the same as the one + * specified. + * + * @param fd The file descriptor to be owned, defaults to -1 if not + * specified, where a negative number means this SmartFd does not + * own a file descriptor. + */ + void reset(const int fd) throw(); + + /** + * SmartFd assignment operator. + * + * This function does the following: + * <ul> + * <li> Calls release on the previous owner (obj); + * <li> Closes the file descriptor of this object if it already owns one. + * <li> Makes this object the owner of the file descriptor released from + * the previous owner (obj). + * </ul> + */ + SmartFd &operator=(SmartFd& obj) ; + + /** + * Destructor. + * + * Closes the owned file descriptor if there is one. + */ + ~SmartFd(); + + /** + * Returns the owned file descriptor or a negative number if this SmartFd + * does not own a file descriptor. + * + * @return The owned file desccriptor. + */ + int get() const throw(); + + /** + * Releases the owned file descriptor. + * + * @return The released file descriptor. + */ + int release() ; + +private: + + /** + * The owned file descriptor. A negative value means this SmartFd does not + * own a file descriptor.. + */ + int m_fd; + + /** + * The function to be called immediately after the SmartFd has closed its + * file-descriptor. A value of null means no function will be called. + */ + ClosedCallback m_closedCallback; + + /** + * Private copy-constructor to prevent users from trying to create a new + * copy of an object of this class. + * Not implemented so that it cannot be called + */ + SmartFd(const SmartFd &obj) throw(); + +}; // class SmartFd + +} // namespace cta diff --git a/common/SmartFdTest.cpp b/common/SmartFdTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08126870b8d52ad134f1ce43b6f94897124120bb --- /dev/null +++ b/common/SmartFdTest.cpp @@ -0,0 +1,68 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "common/SmartFd.hpp" + +#include <gtest/gtest.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <fcntl.h> + +namespace unitTests { + +class castor_utils_SmartFdTest : public ::testing::Test { +protected: + static int s_fd; + static bool s_closedCallbackWasCalled; + + virtual void SetUp() { + s_fd = -1; + s_closedCallbackWasCalled = false; + } + + virtual void TearDown() { + } + + static void closedCallback(int closedfd) { + s_fd = closedfd; + s_closedCallbackWasCalled = true; + } +}; + +int castor_utils_SmartFdTest::s_fd = -1; +bool castor_utils_SmartFdTest::s_closedCallbackWasCalled = false; + +TEST_F(castor_utils_SmartFdTest, testClosedCallback) { + using namespace cta; + + ASSERT_EQ(-1, s_fd); + ASSERT_EQ(false, s_closedCallbackWasCalled); + + int fd = socket(PF_LOCAL, SOCK_STREAM, 0); + ASSERT_NE(-1, fd); + + { + SmartFd sfd(fd); + sfd.setClosedCallback(closedCallback); + } + ASSERT_EQ(fd, s_fd); + ASSERT_EQ(true, s_closedCallbackWasCalled); +} + +} // namespace unitTests