Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
ConnectionOrientedStreamSocket.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#if qStroika_Foundation_Common_Platform_POSIX
7#include <fcntl.h>
8#include <netinet/in.h>
9#include <netinet/tcp.h>
10#include <sys/socket.h>
11#include <sys/types.h>
12#include <unistd.h>
13#endif
14
18#include "Stroika/Foundation/IO/Network/ConnectionOrientedStreamSocket.h"
19#include "Stroika/Foundation/IO/Network/Socket-Private_.h"
22
23// Comment this in to turn on aggressive noisy DbgTrace in this module
24//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
25
26using std::byte;
27
28using namespace Stroika::Foundation::IO::Network::PRIVATE_;
29
30using namespace ConnectionOrientedStreamSocket;
31
33
34namespace {
35 struct Rep_ : BackSocketImpl_<ConnectionOrientedStreamSocket::_IRep> {
36 using inherited = BackSocketImpl_<ConnectionOrientedStreamSocket::_IRep>;
38 : inherited{sd}
39 {
40#if USE_NOISY_TRACE_IN_THIS_MODULE_
41 DbgTrace ("Constructed BackSocketImpl_<ConnectionOrientedStreamSocket>::Rep_ with sd={:x}"_f, (int)sd);
42#endif
43 }
44 Rep_ () = delete;
45 Rep_ (const Rep_&) = delete;
46 ~Rep_ ()
47 {
48 // need DTOR cuz one in base class wont call this override of Close
49 if (fSD_ != kINVALID_NATIVE_HANDLE_) {
50 Close ();
51 }
52 }
53 virtual void Close () override
54 {
55 AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized};
56 if (fSD_ != kINVALID_NATIVE_HANDLE_ and fAutomaticTCPDisconnectOnClose_) {
57 Shutdown (Socket::ShutdownTarget::eWrites);
58 Time::TimePointSeconds timeOutAt = Time::GetTickCount () + *fAutomaticTCPDisconnectOnClose_;
59 Execution::WaitForIOReady ioReady{fSD_};
60 try {
61 again:
62 (void)ioReady.WaitUntil (timeOutAt);
64#if qStroika_Foundation_Common_Platform_POSIX
65 int nb = ::read (fSD_, data, std::size (data));
66#elif qStroika_Foundation_Common_Platform_Windows
67 int flags = 0;
68 int nb = ::recv (fSD_, data, (int)std::size (data), flags);
69#endif
70 if (nb > 0) {
71 DbgTrace ("Warning: {} unread bytes to be read on socket when it was closed."_f,
72 nb); // SHOULD READ ZERO AFTER SHUTDOWN to indicate client other side of connection handled the close
73 goto again;
74 }
75 }
76 catch (...) {
77#if USE_NOISY_TRACE_IN_THIS_MODULE_
78 DbgTrace ("timeout closing down socket - not serious - just means client didn't send close ACK quickly enough"_f);
79#endif
80 }
81 }
82 inherited::Close ();
83 }
84 nonvirtual void Connect_Sync_ (const SocketAddress& sockAddr) const
85 {
86 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
87 sockaddr_storage useSockAddr = sockAddr.As<sockaddr_storage> ();
88#if qStroika_Foundation_Common_Platform_POSIX
89 Handle_ErrNoResultInterruption ([&] () -> int { return ::connect (fSD_, (sockaddr*)&useSockAddr, sockAddr.GetRequiredSize ()); });
90#elif qStroika_Foundation_Common_Platform_Windows
91 ThrowWSASystemErrorIfSOCKET_ERROR (::connect (fSD_, (sockaddr*)&useSockAddr, static_cast<int> (sockAddr.GetRequiredSize ())));
92#else
94#endif
95 }
96 nonvirtual void Connect_AsyncWTimeout_ (const SocketAddress& sockAddr, const Time::Duration& timeout) const
97 {
98 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
99 sockaddr_storage useSockAddr = sockAddr.As<sockaddr_storage> ();
100#if qStroika_Foundation_Common_Platform_POSIX
101 // http://developerweb.net/viewtopic.php?id=3196.
102 // and see https://stackoverflow.com/questions/4181784/how-to-set-socket-timeout-in-c-when-making-multiple-connections/4182564#4182564 for why not using SO_RCVTIMEO/SO_SNDTIMEO
103 long savedFlags{};
104 ThrowPOSIXErrNoIfNegative (savedFlags = ::fcntl (fSD_, F_GETFL, nullptr));
105 ThrowPOSIXErrNoIfNegative (::fcntl (fSD_, F_SETFL, savedFlags | O_NONBLOCK)); // non-blocking mode for select
106 [[maybe_unused]] auto&& cleanup = Finally ([this, savedFlags] () noexcept {
107 // Set to blocking mode again...
108 if (::fcntl (fSD_, F_SETFL, savedFlags) < 0) {
109 AssertNotReached (); // cannot throw here
110 }
111 });
112 while (::connect (fSD_, (sockaddr*)&useSockAddr, sockAddr.GetRequiredSize ()) < 0) {
113 switch (errno) {
114 case EINTR:
115 break; // ignore - try again
116 case EINPROGRESS: {
118 FD_ZERO (&myset);
119 FD_SET (fSD_, &myset);
120 timeval time_out = timeout.As<timeval> ();
121 Handle_ErrNoResultInterruption ([&] () -> int {
122 auto r = ::select (fSD_ + 1, NULL, &myset, nullptr, &time_out);
123 if (r == 0) {
124 // https://man7.org/linux/man-pages/man2/select.2.html - "The return value may be zero if the timeout expired before any file descriptors became ready"
125 Execution::Throw (Execution::TimeOutException::kThe);
126 }
127 return r;
128 });
129 // Check the errno value returned...
130 if (auto err = getsockopt<int> (SOL_SOCKET, SO_ERROR)) {
131 Execution::ThrowSystemErrNo (err);
132 }
133 return; // else must have succeeded
134 } break;
135 default: {
136 Execution::ThrowSystemErrNo ();
137 } break;
138 }
139 }
140#elif qStroika_Foundation_Common_Platform_Windows
141 // https://stackoverflow.com/questions/46045434/winsock-c-connect-timeout
142 {
143 u_long block = 1;
144 if (::ioctlsocket (fSD_, FIONBIO, &block) == SOCKET_ERROR) {
145 Execution::ThrowSystemErrNo (::WSAGetLastError ());
146 }
147 }
148 [[maybe_unused]] auto&& cleanup = Finally ([this] () noexcept {
149 u_long block = 0; // should have saved old value, but not clear how to read?
150 if (::ioctlsocket (fSD_, FIONBIO, &block) == SOCKET_ERROR) {
151 //Execution::ThrowSystemErrNo (::WSAGetLastError ());
152 Assert (false); //FAILURE SETTING BACK TO NOT BLOCKING - SERIOUS
153 }
154 });
155 if (::connect (fSD_, (sockaddr*)&useSockAddr, static_cast<int> (sockAddr.GetRequiredSize ())) == SOCKET_ERROR) {
156 if (::WSAGetLastError () != WSAEWOULDBLOCK) {
157 Execution::ThrowSystemErrNo (::WSAGetLastError ()); // connection failed
158 }
159 // connection pending
161 FD_ZERO (&setW);
162 FD_SET (fSD_, &setW);
164 FD_ZERO (&setE);
165 FD_SET (fSD_, &setE);
166 timeval time_out = timeout.As<timeval> ();
167 int ret = ::select (0, NULL, &setW, &setE, &time_out);
168 if (ret <= 0) {
169 // select() failed or connection timed out
170 if (ret == 0) {
171 WSASetLastError (WSAETIMEDOUT);
172 }
173 Execution::ThrowSystemErrNo (::WSAGetLastError ()); // connection failed
174 }
175 // Check the errno value returned...
176 if (auto err = getsockopt<int> (SOL_SOCKET, SO_ERROR)) {
177 Execution::ThrowSystemErrNo (err);
178 }
179 // else if got here >0 so succeeded with connection
180 }
181#else
183#endif
184 }
185 virtual void Connect (const SocketAddress& sockAddr, const optional<Time::Duration>& timeout) const override
186 {
187 Debug::TraceContextBumper ctx{"ConnectionOrientedStreamSocket_IMPL_::Connect", "sockAddr={}, timeout={}"_f, sockAddr, timeout};
188 if (timeout) {
189 Connect_AsyncWTimeout_ (sockAddr, *timeout);
190 }
191 else {
192 Connect_Sync_ (sockAddr);
193 }
194 }
195 virtual span<byte> Read (span<byte> into) const override
196 {
197 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
198
199#if qStroika_Foundation_Debug_AssertionsChecked
200 Assert (fCurrentPendingReadsCount++ == 0);
201 [[maybe_unused]] auto&& cleanup = Finally ([this] () noexcept { Assert (--fCurrentPendingReadsCount == 0); });
202#endif
203 if (fReadEOF_) {
204 return span<byte>{}; // EOF
205 }
206
207#if qStroika_Foundation_Common_Platform_POSIX
208 auto result = into.subspan (
209 0, Handle_ErrNoResultInterruption ([this, &into] () -> int { return ::read (fSD_, into.data (), into.size ()); }));
210#elif qStroika_Foundation_Common_Platform_Windows
211 int flags = 0;
212 int nBytesToRead = static_cast<int> (min<size_t> (into.size (), numeric_limits<int>::max ()));
213 auto result = into.subspan (0, static_cast<size_t> (ThrowWSASystemErrorIfSOCKET_ERROR (
214 ::recv (fSD_, reinterpret_cast<char*> (into.data ()), nBytesToRead, flags))));
215#else
217 span<byte> result{};
218#endif
219 if (result.empty ()) {
220 fReadEOF_ = true;
221 }
222 return result;
223 }
224 virtual optional<span<byte>> ReadNonBlocking (span<byte> into) const override
225 {
226 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
227 if (AvailableToRead ().has_value ()) {
228 return Read (into);
229 }
230 return nullopt;
231 }
232 virtual optional<size_t> AvailableToRead () const override
233 {
234 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
235#if qStroika_Foundation_Debug_AssertionsChecked
236 Assert (fCurrentPendingReadsCount++ == 0);
237 [[maybe_unused]] auto&& cleanup = Finally ([this] () noexcept { Assert (--fCurrentPendingReadsCount == 0); });
238#endif
239 if (fReadEOF_) {
240 return 0;
241 }
242#if qStroika_Foundation_Common_Platform_POSIX or qStroika_Foundation_Common_Platform_Windows
243 {
245 FD_ZERO (&input);
246 FD_SET (fSD_, &input);
247 struct timeval timeout{};
248 if (::select (static_cast<int> (fSD_) + 1, &input, NULL, NULL, &timeout) == 1) {
249 // don't know how much, but doesn't matter, since read allows returning just one byte if thats all thats available
250 // But MUST check if is EOF or real data available
251 char buf[1024];
252#if qStroika_Foundation_Common_Platform_POSIX
253 int tmp = Handle_ErrNoResultInterruption ([&] () -> int { return ::recv (fSD_, buf, std::size (buf), MSG_PEEK); });
254#elif qStroika_Foundation_Common_Platform_Windows
255 int tmp = ThrowWSASystemErrorIfSOCKET_ERROR (::recv (fSD_, buf, static_cast<int> (std::size (buf)), MSG_PEEK));
256#else
258#endif
259 return tmp;
260 }
261 else {
262 return {};
263 }
264 }
265#else
267 return {};
268#endif
269 }
270 virtual void Write (span<const byte> data) const override
271 {
272 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
273#if USE_NOISY_TRACE_IN_THIS_MODULE_
275 Stroika_Foundation_Debug_OptionalizeTraceArgs ("IO::Network::Socket...rep...::Write", "lwn={}"_f, data.size ())};
276#endif
277#if qStroika_Foundation_Common_Platform_POSIX
278 /*
279 * https://linux.die.net/man/2/write says "writes up to count bytes". So handle case where we get partial writes.
280 * Actually, for most of the cases called out, we cannot really continue anyhow, so this maybe pointless, but the
281 * docs aren't fully clear, so play it safe --LGP 2017-04-13
282 */
283 BreakWriteIntoParts_<byte> (data, numeric_limits<int>::max (), [this] (span<const byte> data) -> size_t {
284 Assert (data.size () < numeric_limits<int>::max ());
285 ssize_t n =
286 Handle_ErrNoResultInterruption ([this, &data] () -> ssize_t { return ::write (fSD_, data.data (), data.size ()); });
288 Assert (0 <= n and n <= data.size ());
289 return static_cast<size_t> (n);
290 });
291#elif qStroika_Foundation_Common_Platform_Windows
292 /*
293 * Note sure what the best way is here, but with WinSock, you cannot use write() directly. Sockets are not
294 * file descriptors in windows implementation.
295 * WONT WORK:
296 * int n = ::_write (fSD_, start, end - start);
297 */
298 size_t maxSendAtATime = getsockopt<unsigned int> (SOL_SOCKET, SO_MAX_MSG_SIZE);
299 BreakWriteIntoParts_<byte> (data, maxSendAtATime, [this, maxSendAtATime] (span<const byte> data) -> size_t {
300 Require (data.size () <= maxSendAtATime);
301 Assert (data.size () < static_cast<size_t> (numeric_limits<int>::max ()));
302 int len = static_cast<int> (data.size ());
303 int flags = 0;
304 int n = ThrowWSASystemErrorIfSOCKET_ERROR (::send (fSD_, reinterpret_cast<const char*> (data.data ()), len, flags));
305 Assert (0 <= n and static_cast<size_t> (n) <= data.size ());
306 return static_cast<size_t> (n);
307 });
308#else
310#endif
311 }
312 virtual optional<IO::Network::SocketAddress> GetPeerAddress () const override
313 {
314 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
315 struct sockaddr_storage radr;
316 socklen_t len = sizeof (radr);
317 if (::getpeername (static_cast<int> (fSD_), (struct sockaddr*)&radr, &len) == 0) {
319 return sa;
320 }
321 return nullopt;
322 }
323 virtual optional<Time::DurationSeconds> GetAutomaticTCPDisconnectOnClose () const override
324 {
325 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
326 return fAutomaticTCPDisconnectOnClose_;
327 }
328 virtual void SetAutomaticTCPDisconnectOnClose (const optional<Time::DurationSeconds>& waitFor) override
329 {
330 AssertExternallySynchronizedMutex::WriteContext declareContext{this->fThisAssertExternallySynchronized};
331 fAutomaticTCPDisconnectOnClose_ = waitFor;
332 }
333 virtual KeepAliveOptions GetKeepAlives () const override
334 {
335 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
336 KeepAliveOptions result;
337 result.fEnabled = !!getsockopt<int> (SOL_SOCKET, SO_KEEPALIVE);
338#if qStroika_Foundation_Common_Platform_Linux
339 // Only available if linux >= 2.4
340 result.fMaxProbesSentBeforeDrop = getsockopt<int> (SOL_TCP, TCP_KEEPCNT);
341 result.fTimeIdleBeforeSendingKeepalives = Time::DurationSeconds{getsockopt<int> (SOL_TCP, TCP_KEEPIDLE)};
342 result.fTimeBetweenIndividualKeepaliveProbes = Time::DurationSeconds{getsockopt<int> (SOL_TCP, TCP_KEEPINTVL)};
343#elif qStroika_Foundation_Common_Platform_Windows
344// WSAIoctl (..., SIO_KEEPALIVE_VALS) can be used to set some of these values, but I can find no way
345// to fetch them --LGP 2017-02-27
346#endif
347 return result;
348 }
349 virtual void SetKeepAlives (const KeepAliveOptions& keepAliveOptions) override
350 {
351 AssertExternallySynchronizedMutex::WriteContext declareContext{this->fThisAssertExternallySynchronized};
352 setsockopt<int> (SOL_SOCKET, SO_KEEPALIVE, keepAliveOptions.fEnabled);
353#if qStroika_Foundation_Common_Platform_Linux
354 // Only available if linux >= 2.4
355 if (keepAliveOptions.fMaxProbesSentBeforeDrop) {
356 setsockopt<int> (SOL_TCP, TCP_KEEPCNT, *keepAliveOptions.fMaxProbesSentBeforeDrop);
357 }
358 if (keepAliveOptions.fTimeIdleBeforeSendingKeepalives) {
359 setsockopt<int> (SOL_TCP, TCP_KEEPIDLE, static_cast<int> (keepAliveOptions.fTimeIdleBeforeSendingKeepalives->count ()));
360 }
361 if (keepAliveOptions.fTimeBetweenIndividualKeepaliveProbes) {
362 setsockopt<int> (SOL_TCP, TCP_KEEPINTVL, static_cast<int> (keepAliveOptions.fTimeBetweenIndividualKeepaliveProbes->count ()));
363 }
364#elif qStroika_Foundation_Common_Platform_Windows
365 // windows only allows setting these two, and both at the same time
366 if (keepAliveOptions.fEnabled and
367 (keepAliveOptions.fTimeIdleBeforeSendingKeepalives or keepAliveOptions.fTimeBetweenIndividualKeepaliveProbes)) {
368 tcp_keepalive alive{keepAliveOptions.fEnabled};
369 // from https://msdn.microsoft.com/en-us/library/windows/desktop/dd877220(v=vs.85).aspx - "The default settings when a TCP socket is initialized sets the keep-alive timeout to 2 hours and the keep-alive interval to 1 second"
370 alive.keepalivetime = Math::Round<ULONG> (keepAliveOptions.fTimeIdleBeforeSendingKeepalives.value_or (2 * 60 * 60s).count () * 1000.0);
371 alive.keepaliveinterval = Math::Round<ULONG> (keepAliveOptions.fTimeBetweenIndividualKeepaliveProbes.value_or (1s).count () * 1000.0);
372 DWORD dwBytesRet{};
373 if (::WSAIoctl (fSD_, SIO_KEEPALIVE_VALS, &alive, sizeof (alive), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR) {
374 Execution::ThrowSystemErrNo (::WSAGetLastError ());
375 }
376 }
377#endif
378 }
379 virtual bool GetTCPNoDelay () const override
380 {
381 AssertExternallySynchronizedMutex::ReadContext declareContext{this->fThisAssertExternallySynchronized};
382 return static_cast<bool> (getsockopt<int> (IPPROTO_TCP, TCP_NODELAY));
383 }
384 virtual void SetTCPNoDelay (bool noDelay) override
385 {
386 AssertExternallySynchronizedMutex::WriteContext declareContext{this->fThisAssertExternallySynchronized};
387 setsockopt<int> (IPPROTO_TCP, TCP_NODELAY, noDelay);
388 }
389 optional<Time::DurationSeconds> fAutomaticTCPDisconnectOnClose_;
390 mutable bool fReadEOF_{false};
391#if qStroika_Foundation_Debug_AssertionsChecked
392 mutable atomic<int> fCurrentPendingReadsCount{};
393#endif
394 };
395}
396
397/*
398 ********************************************************************************
399 ********** Network::ConnectionOrientedStreamSocket::KeepAliveOptions ***********
400 ********************************************************************************
401 */
402Characters::String Network::ConnectionOrientedStreamSocket::KeepAliveOptions::ToString () const
403{
405 sb << "{"sv;
406 sb << "Enabled: "sv << fEnabled;
407#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_Platform_Windows
408 if (fMaxProbesSentBeforeDrop) {
409 sb << ", Max-Probes-Sent-Before-Drop: "sv << fMaxProbesSentBeforeDrop;
410 }
411 if (fTimeIdleBeforeSendingKeepalives) {
412 sb << ", Time-Idle-Before-Sending-Keepalives: "sv << fTimeIdleBeforeSendingKeepalives;
413 }
414 if (fTimeBetweenIndividualKeepaliveProbes) {
415 sb << ", Time-Between-Individual-Keepalive-Probes: "sv << fTimeBetweenIndividualKeepaliveProbes;
416 }
417#endif
418 sb << "}"sv;
419 return sb;
420}
421
422/*
423 ********************************************************************************
424 ******************** ConnectionOrientedStreamSocket ****************************
425 ********************************************************************************
426 */
427ConnectionOrientedStreamSocket::Ptr ConnectionOrientedStreamSocket::New (SocketAddress::FamilyType family, Type socketKind, const optional<IPPROTO>& protocol)
428{
429 return Ptr{Memory::MakeSharedPtr<Rep_> (_Protected::mkLowLevelSocket_ (family, socketKind, protocol))};
430}
431
432ConnectionOrientedStreamSocket::Ptr ConnectionOrientedStreamSocket::Attach (PlatformNativeHandle sd)
433{
434 return Ptr{Memory::MakeSharedPtr<Rep_> (sd)};
435}
436
437#include "ConnectionOrientedMasterSocket.h" //tmphack for kLowLevelSocketPairWorks_==false
438auto ConnectionOrientedStreamSocket::NewPair (SocketAddress::FamilyType family, Type socketKind, const optional<IPPROTO>& protocol) -> tuple<Ptr, Ptr>
439{
440 constexpr bool kLowLevelSocketPairWorks_{true}; // for now only works on windows
441 if constexpr (kLowLevelSocketPairWorks_) {
442 auto sp = _Protected::mkLowLevelSocketPair_ (family, socketKind, protocol);
443 return make_tuple (Attach (get<0> (sp)), Attach (get<1> (sp)));
444 }
445 else {
446 // Create a Listening master socket, bind it, and get it listening
447 // Just needed temporarily to create the socketpair, then it can be closed when it goes out of scope
448 auto connectionOrientedMaster = ConnectionOrientedMasterSocket::New (family, socketKind, protocol);
449 connectionOrientedMaster.Bind (SocketAddress{LocalHost (family)});
450 connectionOrientedMaster.Listen (1);
451
452 // now make a NEW socket, with the bound address and connect;
453 auto one = ConnectionOrientedStreamSocket::NewConnection (*connectionOrientedMaster.GetLocalAddress ());
454 auto two = connectionOrientedMaster.Accept ();
455 return make_tuple (one, two);
456 }
457}
458
459/*
460 ********************************************************************************
461 ******************** ConnectionOrientedStreamSocket::Ptr ***********************
462 ********************************************************************************
463 */
465{
466 linger lr = getsockopt<linger> (SOL_SOCKET, SO_LINGER);
467 return lr.l_onoff ? lr.l_linger : optional<int>{};
468}
469
470void ConnectionOrientedStreamSocket::Ptr::SetLinger (const optional<int>& linger) const
471{
472 ::linger so_linger{};
473 if (linger) {
474 so_linger.l_onoff = true;
475 so_linger.l_linger = static_cast<u_short> (*linger);
476 }
477 setsockopt<::linger> (SOL_SOCKET, SO_LINGER, so_linger);
478}
#define AssertNotImplemented()
Definition Assertions.h:402
#define AssertNotReached()
Definition Assertions.h:356
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
#define qStroika_ATTRIBUTE_INDETERMINATE
qStroika_ATTRIBUTE_INDETERMINATE is used where you would use a C++ attribute for a variable that is i...
Definition StdCompat.h:395
#define DbgTrace
Definition Trace.h:317
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:278
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
auto Handle_ErrNoResultInterruption(CALL call) -> decltype(call())
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from th...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode)
Ptr New(SocketAddress::FamilyType family, Type socketKind, const optional< IPPROTO > &protocol={})
constexpr InternetAddress LocalHost(SocketAddress::FamilyType fm)
return V4::kLocalhost or V6::kLocalhost depending on argument address family