/src/kea/src/lib/util/watch_socket.h
Line | Count | Source |
1 | | // Copyright (C) 2014-2024 Internet Systems Consortium, Inc. ("ISC") |
2 | | // |
3 | | // This Source Code Form is subject to the terms of the Mozilla Public |
4 | | // License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
6 | | |
7 | | #ifndef WATCH_SOCKET_H |
8 | | #define WATCH_SOCKET_H |
9 | | |
10 | | /// @file watch_socket.h Defines the class, WatchSocket. |
11 | | |
12 | | #include <exceptions/exceptions.h> |
13 | | #include <boost/noncopyable.hpp> |
14 | | #include <boost/shared_ptr.hpp> |
15 | | |
16 | | #include <stdint.h> |
17 | | #include <string> |
18 | | |
19 | | namespace isc { |
20 | | namespace util { |
21 | | |
22 | | /// @brief Exception thrown if an error occurs during IO source open. |
23 | | class WatchSocketError : public isc::Exception { |
24 | | public: |
25 | | WatchSocketError(const char* file, size_t line, const char* what) : |
26 | 0 | isc::Exception(file, line, what) { } |
27 | | }; |
28 | | |
29 | | /// @brief Provides an IO "ready" semaphore for use with select() or poll() |
30 | | /// WatchSocket exposes a single open file descriptor, the "select-fd" which |
31 | | /// can be marked as being ready to read (i.e. !EWOULDBLOCK) and cleared |
32 | | /// (i.e. EWOULDBLOCK). The select-fd can be used with select(), poll(), or |
33 | | /// their variants alongside other file descriptors. |
34 | | /// |
35 | | /// Internally, WatchSocket uses a pipe. The select-fd is the "read" end of |
36 | | /// pipe. To mark the socket as ready to read, an integer marker is written |
37 | | /// to the pipe. To clear the socket, the marker is read from the pipe. Note |
38 | | /// that WatchSocket will only write the marker if it is not already marked. |
39 | | /// This prevents the socket's pipe from filling endlessly. |
40 | | /// |
41 | | /// @warning Because the read or "sink" side of the pipe is used as the select_fd, |
42 | | /// it is possible for that fd to be interfered with, albeit only from within the |
43 | | /// process space which owns it. Performing operations that may alter the fd's state |
44 | | /// such as close, read, or altering behavior flags with fcntl or ioctl can have |
45 | | /// unpredictable results. It is intended strictly use with functions such as select() |
46 | | /// poll() or their variants. |
47 | | class WatchSocket : public boost::noncopyable { |
48 | | public: |
49 | | /// @brief Value used to signify an invalid descriptor. |
50 | | static const int SOCKET_NOT_VALID = -1; |
51 | | /// @brief Value written to the source when marking the socket as ready. |
52 | | /// The value itself is arbitrarily chosen as one that is unlikely to occur |
53 | | /// otherwise and easy to debug. |
54 | | static const uint32_t MARKER = 0xDEADBEEF; |
55 | | |
56 | | /// @brief Constructor |
57 | | /// |
58 | | /// Constructs an instance of the WatchSocket in the cleared (EWOULDBLOCK) |
59 | | /// state. |
60 | | WatchSocket(); |
61 | | |
62 | | /// @brief Destructor |
63 | | /// |
64 | | /// Closes all internal resources, including the select-fd. |
65 | | virtual ~WatchSocket(); |
66 | | |
67 | | /// @brief Marks the select-fd as ready to read. |
68 | | /// |
69 | | /// Marks the socket as ready to read, if is not already so marked. |
70 | | /// If an error occurs, closeSocket is called. This will force any further |
71 | | /// use of the select_fd to fail rather than show the fd as READY. Such |
72 | | /// an error is almost surely a programmatic error which has corrupted the |
73 | | /// select_fd. |
74 | | /// |
75 | | /// @throw WatchSocketError if an error occurs marking the socket. |
76 | | void markReady(); |
77 | | |
78 | | /// @brief Returns true the if socket is marked as ready. |
79 | | /// |
80 | | /// This method uses a non-blocking call to select() to test read state of the |
81 | | /// select_fd. Rather than track what the status "should be" it tests the status. |
82 | | /// This should eliminate conditions where the select-fd appear to be perpetually |
83 | | /// ready. |
84 | | /// @return Returns true if select_fd is not SOCKET_NOT_VALID and select() reports it |
85 | | /// as !EWOULDBLOCK, otherwise it returns false. |
86 | | /// This method is guaranteed NOT to throw. |
87 | | bool isReady(); |
88 | | |
89 | | /// @brief Clears the socket's ready to read marker. |
90 | | /// |
91 | | /// Clears the socket if it is currently marked as ready to read. |
92 | | /// If an error occurs, closeSocket is called. This will force any further |
93 | | /// use of the select_fd to fail rather than show the fd as READY. Such |
94 | | /// an error is almost surely a programmatic error which has corrupted the |
95 | | /// select_fd. |
96 | | /// |
97 | | /// @throw WatchSocketError if an error occurs clearing the socket |
98 | | /// marker. |
99 | | void clearReady(); |
100 | | |
101 | | /// @brief Returns the file descriptor to use to monitor the socket. |
102 | | /// |
103 | | /// @note Using this file descriptor as anything other than an argument |
104 | | /// to select() or similar methods can have unpredictable results. |
105 | | /// |
106 | | /// @return The file descriptor associated with read end of the socket's |
107 | | /// pipe. |
108 | | int getSelectFd(); |
109 | | |
110 | | /// @brief Closes the descriptors associated with the socket. |
111 | | /// |
112 | | /// This method is used to close the socket and capture errors that |
113 | | /// may occur during this operation. |
114 | | /// |
115 | | /// @param [out] error_string Holds the error string if closing |
116 | | /// the socket failed. It will hold empty string otherwise. |
117 | | /// |
118 | | /// @return true if the operation was successful, false otherwise. |
119 | | bool closeSocket(std::string& error_string); |
120 | | |
121 | | private: |
122 | | |
123 | | /// @brief Closes the descriptors associated with the socket. |
124 | | /// |
125 | | /// This method is called by the class destructor and it ignores |
126 | | /// any errors that may occur while closing the sockets. |
127 | | void closeSocket(); |
128 | | |
129 | | /// @brief The end of the pipe to which the marker is written |
130 | | int source_; |
131 | | |
132 | | /// @brief The end of the pipe from which the marker is read. |
133 | | /// This is the value returned as the select-fd. |
134 | | int sink_; |
135 | | }; |
136 | | |
137 | | /// @brief Defines a smart pointer to an instance of a WatchSocket. |
138 | | typedef boost::shared_ptr<WatchSocket> WatchSocketPtr; |
139 | | |
140 | | } // namespace isc::util |
141 | | } // namespace isc |
142 | | |
143 | | #endif |