Implementation:ClickHouse ClickHouse Poco Socket Impl
base/poco/Net/src/Socket.cpp:1-391
ClickHouse_ClickHouse
ClickHouse_ClickHouse_Socket_Abstraction
| Source File | base/poco/Net/src/Socket.cpp |
|---|---|
| Lines of Code | 391 |
| Principle | Principle:ClickHouse_ClickHouse_Socket_Abstraction |
| Domain | Networking, Sockets |
| Language | C++ |
| Last Updated | 2026-02-08 00:00 GMT |
Purpose
Implements the `Socket` base class and its static `select` method. The `Socket` class is a lightweight handle that wraps a reference-counted `SocketImpl` pointer, providing value-semantic copy/assignment with shared underlying socket resources. The `select` method implements the classic BSD socket readiness check across three lists (read, write, except), using the best available platform mechanism (epoll, poll, or select).
Code Reference
Socket Handle (Value Semantics with Reference Counting)
Socket::Socket():
_pImpl(new StreamSocketImpl)
{
}
Socket::Socket(SocketImpl* pImpl):
_pImpl(pImpl)
{
poco_check_ptr(_pImpl);
}
Socket::Socket(const Socket& socket):
_pImpl(socket._pImpl)
{
poco_check_ptr(_pImpl);
_pImpl->duplicate();
}
Socket& Socket::operator = (const Socket& socket)
{
if (&socket != this)
{
if (_pImpl) _pImpl->release();
_pImpl = socket._pImpl;
if (_pImpl) _pImpl->duplicate();
}
return *this;
}
Socket::~Socket()
{
_pImpl->release();
}
Static select Method (Epoll Backend)
int Socket::select(SocketList& readList, SocketList& writeList,
SocketList& exceptList, const Poco::Timespan& timeout)
{
#if defined(POCO_HAVE_FD_EPOLL)
int epollSize = readList.size() + writeList.size() + exceptList.size();
if (epollSize == 0) return 0;
// Build epoll_event array, merging duplicate fds
std::vector<epoll_event> eventsIn(epollSize);
struct epoll_event* eventLast = eventsIn.data();
for (auto it = readList.begin(); it != readList.end(); ++it)
{
// Find existing entry or create new one
// Set EPOLLIN flag
}
// Similar for writeList (EPOLLOUT) and exceptList (EPOLLERR)
int epollfd = epoll_create(1);
// Add all events via epoll_ctl
// epoll_wait with EINTR retry loop
::close(epollfd);
// Build ready lists from returned events
std::swap(readList, readyReadList);
std::swap(writeList, readyWriteList);
std::swap(exceptList, readyExceptList);
return readList.size() + writeList.size() + exceptList.size();
#endif
}
I/O Contract
| Input | Output | Side Effects |
|---|---|---|
| `Socket()` default constructor | A Socket wrapping a new `StreamSocketImpl` | Allocates a new socket implementation |
| `Socket(const Socket& other)` copy constructor | A new Socket handle sharing the same `SocketImpl` | Increments the `SocketImpl` reference count via `duplicate` |
| `operator=(const Socket& other)` | Reference to `*this` | Releases old impl (may close socket if refcount reaches 0), duplicates new impl |
| `~Socket()` destructor | None | Releases the `SocketImpl` reference; if last reference, the socket is closed |
| `select(readList, writeList, exceptList, timeout)` | Number of ready sockets | Modifies input lists in-place: each list is replaced with only the sockets that are ready. Creates and destroys a temporary epoll fd (Linux) or pollfd array (BSD). |
Usage Examples
// Copy semantics -- both sockets share the same underlying fd
Poco::Net::StreamSocket s1;
Poco::Net::Socket s2 = s1; // reference count incremented
// Static select for readiness checking
Socket::SocketList readList, writeList, exceptList;
readList.push_back(serverSocket);
writeList.push_back(clientSocket);
Poco::Timespan timeout(5, 0); // 5 seconds
int ready = Socket::select(readList, writeList, exceptList, timeout);
// After select, readList contains only readable sockets
for (auto& sock : readList)
{
// Handle readable socket
}
Internal Details
Platform-Specific select Implementation
The `select` static method has three code paths:
- Epoll (Linux, `POCO_HAVE_FD_EPOLL`): Creates a temporary epoll instance, registers all sockets by scanning the three lists and merging duplicate file descriptors into combined event masks. After `epoll_wait`, maps events back to Socket objects using `data.ptr`. The epoll fd is closed after each call.
- Poll (BSD, `POCO_HAVE_FD_POLL`): Allocates a `pollfd` array, merging sockets appearing in multiple lists to avoid duplicate entries. After `::poll`, scans results and populates ready lists by matching file descriptors back to the original socket objects.
- Select (fallback): Uses classic `fd_set` with `FD_ZERO`/`FD_SET`/`FD_ISSET`. Limited by `FD_SETSIZE`. After `::select`, scans each original list and checks `FD_ISSET` to build the ready lists.
EINTR Retry with Timeout Adjustment
All three backends handle `EINTR` by measuring elapsed wall-clock time and subtracting it from the remaining timeout, then retrying the system call.
Reference Counting
`Socket` objects are copyable handles. The underlying `SocketImpl` (which inherits from `Poco::RefCountedObject`) is shared and automatically closed when the last `Socket` referencing it is destroyed.