bool ThreadPoolWorker::ThreadFunction(void *port) { #if defined(CAT_OS_WINDOWS) DWORD bytes; void *key = 0; AsyncBuffer *buffer = 0; int error; // Initialize ThreadPool Local Storage (CSPRNG, math library) ThreadPoolLocalStorage tls; if (!tls.Valid()) { FATAL("ThreadPool") << "Unable to initialize thread local storage objects"; return false; } for (;;) { error = GetQueuedCompletionStatus((HANDLE)port, &bytes, (PULONG_PTR)&key, (OVERLAPPED**)&buffer, INFINITE) ? 0 : GetLastError(); // Terminate thread when we receive a zeroed completion packet if (!bytes && !key && !buffer) return true; // If completion object is NOT specified, ThreadRefObject *obj = reinterpret_cast<ThreadRefObject*>( key ); if (!obj) { // Release memory for overlapped object buffer->Release(); } else { // If completion object callback returns TRUE, if (buffer->Call(&tls, error, buffer, bytes)) { // Release memory for overlapped object buffer->Release(); } // Release reference held on completion object obj->ReleaseRef(); } } #else // CAT_OS_WINDOWS #error TODO #endif // CAT_OS_WINDOWS return true; }
bool ThreadPoolWorker::ThreadFunction(void *port) { #if defined(CAT_OS_WINDOWS) DWORD bytes; void *key = 0; AsyncBuffer *buffer = 0; int error; // Initialize ThreadPool Local Storage (CSPRNG, math library) ThreadPoolLocalStorage tls; if (!tls.Valid()) { FATAL("ThreadPool") << "Unable to initialize thread local storage objects"; return false; } for (;;) { error = GetQueuedCompletionStatus((HANDLE)port, &bytes, (PULONG_PTR)&key, (OVERLAPPED**)&buffer, INFINITE) ? 0 : GetLastError(); // Terminate thread when we receive a zeroed completion packet if (!bytes && !key && !buffer) return true; // If completion object is NOT specified, ThreadRefObject *obj = reinterpret_cast<ThreadRefObject*>( key ); if (!obj) { // Release memory for overlapped object buffer->Release(); } else { // If completion object callback returns TRUE, if (buffer->Call(&tls, error, buffer, bytes)) { // Release memory for overlapped object buffer->Release(); } // Release reference held on completion object obj->ReleaseRef(); } } #else epoll_event ev_buf[EPOLL_QUEUE_LEN]; for (;;) { int n; for (;;) { n = epoll_wait(_port, ev_buf, EPOLL_QUEUE_LEN, -1); if (!n == -1 && errno == EINTR)) { FATAL("ThreadPool") << "epoll_wait failure"; break; } } if (!n) { FATAL("ThreadPool") << "epoll_wait timeout expired"; continue; } for (int ii = 0; ii < n; ++ii) { poll_entry_t *pe = (poll_entry_t*)ev_buf[ii].data.ptr; if (pe->fd == INVALID_SOCKET) continue; if (ev_buf [i].events & (EPOLLERR | EPOLLHUP)) pe->events->in_event (); if (pe->fd == INVALID_SOCKET) continue; if (ev_buf [i].events & EPOLLOUT) pe->events->out_event (); if (pe->fd == INVALID_SOCKET) continue; if (ev_buf [i].events & EPOLLIN) pe->events->in_event (); } } #endif return true; }
bool Client::ThreadFunction(void *) { ThreadPoolLocalStorage tls; if (!tls.Valid()) { WARN("Client") << "Unable to create thread pool local storage"; return false; } u32 start_time = Clock::msec_fast(); u32 first_hello_post = start_time; u32 last_hello_post = start_time; u32 hello_post_interval = INITIAL_HELLO_POST_INTERVAL; // While still not connected, while (!_connected) { // Wait for quit signal if (_kill_flag.Wait(HANDSHAKE_TICK_RATE)) return false; // If now connected, break out if (_connected) break; u32 now = Clock::msec_fast(); // If connection timed out, if (now - first_hello_post >= CONNECT_TIMEOUT) { // NOTE: Connection can complete before or after OnConnectFail() WARN("Client") << "Unable to connect: Timeout"; Close(); return false; } // If time to repost hello packet, if (now - last_hello_post >= hello_post_interval) { if (!PostHello()) { WARN("Client") << "Unable to connect: Post failure"; Close(); return false; } last_hello_post = now; hello_post_interval *= 2; } OnTick(&tls, now); } // Begin MTU probing after connection completes u32 overhead = (Is6() ? IPV6_HEADER_BYTES : IPV4_HEADER_BYTES) + UDP_HEADER_BYTES + AuthenticatedEncryption::OVERHEAD_BYTES; u32 mtu_discovery_time = Clock::msec(); int mtu_discovery_attempts = 2; if (!DontFragment()) { WARN("Client") << "Unable to detect MTU: Unable to set DF bit"; mtu_discovery_attempts = 0; } else if (!PostMTUProbe(&tls, MAXIMUM_MTU - overhead) || !PostMTUProbe(&tls, MEDIUM_MTU - overhead)) { WARN("Client") << "Unable to detect MTU: First probe post failure"; } // Time synchronization begins right away u32 next_sync_time = Clock::msec(); u32 sync_attempts = 0; // Set last receive time to avoid disconnecting due to timeout too soon _last_recv_tsc = next_sync_time; // While waiting for quit signal, while (!_kill_flag.Wait(Transport::TICK_RATE)) { u32 now = Clock::msec(); TickTransport(&tls, now); // If it is time for time synch, if ((s32)(now - next_sync_time) >= 0) { PostTimePing(); // Increase synch interval after the first 8 data points if (sync_attempts >= 8) next_sync_time = now + 20000; // 20 seconds from now else { next_sync_time = now + 5000; // 5 seconds from now ++sync_attempts; } } // If MTU discovery attempts continue, if (mtu_discovery_attempts > 0) { // If it is time to reprobe the MTU, if (now - mtu_discovery_time >= MTU_PROBE_INTERVAL) { // If payload bytes already maxed out, if (_max_payload_bytes >= MAXIMUM_MTU - overhead) { // Stop posting probes mtu_discovery_attempts = 0; // On final attempt set DF=0 DontFragment(false); } else { // If not on final attempt, if (mtu_discovery_attempts > 1) { // Post probes if (!PostMTUProbe(&tls, MAXIMUM_MTU - overhead) || !PostMTUProbe(&tls, MEDIUM_MTU - overhead)) { WARN("Client") << "Unable to detect MTU: Probe post failure"; } mtu_discovery_time = now; --mtu_discovery_attempts; } else { // Stop posting probes mtu_discovery_attempts = 0; // On final attempt set DF=0 DontFragment(false); } } } } // If no packets have been received, if ((s32)(now - _last_recv_tsc) >= TIMEOUT_DISCONNECT) { Disconnect(); return true; } // Tick subclass OnTick(&tls, now); // Send a keep-alive after the silence limit expires if ((s32)(now - _last_send_mstsc) >= SILENCE_LIMIT) { PostTimePing(); next_sync_time = now + 20000; // 20 seconds from now } } return true; }