1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
|