summaryrefslogtreecommitdiffstats
path: root/src/lib/util/watch_socket.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/watch_socket.h')
-rw-r--r--src/lib/util/watch_socket.h143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/lib/util/watch_socket.h b/src/lib/util/watch_socket.h
new file mode 100644
index 0000000..e6d65f6
--- /dev/null
+++ b/src/lib/util/watch_socket.h
@@ -0,0 +1,143 @@
+// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef WATCH_SOCKET_H
+#define WATCH_SOCKET_H
+
+/// @file watch_socket.h Defines the class, WatchSocket.
+
+#include <exceptions/exceptions.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace util {
+
+/// @brief Exception thrown if an error occurs during IO source open.
+class WatchSocketError : public isc::Exception {
+public:
+ WatchSocketError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Provides an IO "ready" semaphore for use with select() or poll()
+/// WatchSocket exposes a single open file descriptor, the "select-fd" which
+/// can be marked as being ready to read (i.e. !EWOULDBLOCK) and cleared
+/// (i.e. EWOULDBLOCK). The select-fd can be used with select(), poll(), or
+/// their variants alongside other file descriptors.
+///
+/// Internally, WatchSocket uses a pipe. The select-fd is the "read" end of
+/// pipe. To mark the socket as ready to read, an integer marker is written
+/// to the pipe. To clear the socket, the marker is read from the pipe. Note
+/// that WatchSocket will only write the marker if it is not already marked.
+/// This prevents the socket's pipe from filling endlessly.
+///
+/// @warning Because the read or "sink" side of the pipe is used as the select_fd,
+/// it is possible for that fd to be interfered with, albeit only from within the
+/// process space which owns it. Performing operations that may alter the fd's state
+/// such as close, read, or altering behavior flags with fcntl or ioctl can have
+/// unpredictable results. It is intended strictly use with functions such as select()
+/// poll() or their variants.
+class WatchSocket : public boost::noncopyable {
+public:
+ /// @brief Value used to signify an invalid descriptor.
+ static const int SOCKET_NOT_VALID = -1;
+ /// @brief Value written to the source when marking the socket as ready.
+ /// The value itself is arbitrarily chosen as one that is unlikely to occur
+ /// otherwise and easy to debug.
+ static const uint32_t MARKER = 0xDEADBEEF;
+
+ /// @brief Constructor
+ ///
+ /// Constructs an instance of the WatchSocket in the cleared (EWOULDBLOCK)
+ /// state.
+ WatchSocket();
+
+ /// @brief Destructor
+ ///
+ /// Closes all internal resources, including the select-fd.
+ virtual ~WatchSocket();
+
+ /// @brief Marks the select-fd as ready to read.
+ ///
+ /// Marks the socket as ready to read, if is not already so marked.
+ /// If an error occurs, closeSocket is called. This will force any further
+ /// use of the select_fd to fail rather than show the fd as READY. Such
+ /// an error is almost surely a programmatic error which has corrupted the
+ /// select_fd.
+ ///
+ /// @throw WatchSocketError if an error occurs marking the socket.
+ void markReady();
+
+ /// @brief Returns true the if socket is marked as ready.
+ ///
+ /// This method uses a non-blocking call to select() to test read state of the
+ /// select_fd. Rather than track what the status "should be" it tests the status.
+ /// This should eliminate conditions where the select-fd appear to be perpetually
+ /// ready.
+ /// @return Returns true if select_fd is not SOCKET_NOT_VALID and select() reports it
+ /// as !EWOULDBLOCK, otherwise it returns false.
+ /// This method is guaranteed NOT to throw.
+ bool isReady();
+
+ /// @brief Clears the socket's ready to read marker.
+ ///
+ /// Clears the socket if it is currently marked as ready to read.
+ /// If an error occurs, closeSocket is called. This will force any further
+ /// use of the select_fd to fail rather than show the fd as READY. Such
+ /// an error is almost surely a programmatic error which has corrupted the
+ /// select_fd.
+ ///
+ /// @throw WatchSocketError if an error occurs clearing the socket
+ /// marker.
+ void clearReady();
+
+ /// @brief Returns the file descriptor to use to monitor the socket.
+ ///
+ /// @note Using this file descriptor as anything other than an argument
+ /// to select() or similar methods can have unpredictable results.
+ ///
+ /// @return The file descriptor associated with read end of the socket's
+ /// pipe.
+ int getSelectFd();
+
+ /// @brief Closes the descriptors associated with the socket.
+ ///
+ /// This method is used to close the socket and capture errors that
+ /// may occur during this operation.
+ ///
+ /// @param [out] error_string Holds the error string if closing
+ /// the socket failed. It will hold empty string otherwise.
+ ///
+ /// @return true if the operation was successful, false otherwise.
+ bool closeSocket(std::string& error_string);
+
+private:
+
+ /// @brief Closes the descriptors associated with the socket.
+ ///
+ /// This method is called by the class destructor and it ignores
+ /// any errors that may occur while closing the sockets.
+ void closeSocket();
+
+ /// @brief The end of the pipe to which the marker is written
+ int source_;
+
+ /// @brief The end of the pipe from which the marker is read.
+ /// This is the value returned as the select-fd.
+ int sink_;
+};
+
+/// @brief Defines a smart pointer to an instance of a WatchSocket.
+typedef boost::shared_ptr<WatchSocket> WatchSocketPtr;
+
+} // namespace isc::util
+} // namespace isc
+
+#endif