int pn_selector_select(pn_selector_t *selector, int timeout) { assert(selector); pn_error_clear(selector->error); pn_timestamp_t deadline = 0; pn_timestamp_t now = pn_i_now(); if (timeout) { if (selector->deadlines_head) deadline = selector->deadlines_head->deadline; } if (deadline) { int64_t delta = deadline - now; if (delta < 0) { delta = 0; } if (timeout < 0) timeout = delta; else if (timeout > delta) timeout = delta; } deadline = (timeout >= 0) ? now + timeout : 0; // Process all currently available completions, even if matched events available pni_iocp_drain_completions(selector->iocp); pni_zombie_check(selector->iocp, now); // Loop until an interested event is matched, or until deadline while (true) { if (selector->triggered_list_head) break; if (deadline && deadline <= now) break; pn_timestamp_t completion_deadline = deadline; pn_timestamp_t zd = pni_zombie_deadline(selector->iocp); if (zd) completion_deadline = completion_deadline ? pn_min(zd, completion_deadline) : zd; int completion_timeout = (!completion_deadline) ? -1 : completion_deadline - now; int rv = pni_iocp_wait_one(selector->iocp, completion_timeout, selector->error); if (rv < 0) return pn_error_code(selector->error); now = pn_i_now(); if (zd && zd <= now) { pni_zombie_check(selector->iocp, now); } } selector->current = 0; selector->awoken = now; for (iocpdesc_t *iocpd = selector->deadlines_head; iocpd; iocpd = iocpd->deadlines_next) { if (iocpd->deadline <= now) pni_events_update(iocpd, iocpd->events | PN_EXPIRED); else break; } selector->current_triggered = selector->triggered_list_head; return pn_error_code(selector->error); }
static void drain_zombie_completions(iocp_t *iocp) { // No more pn_selector_select() from App, but zombies still need care and feeding // until their outstanding async actions complete. pni_iocp_drain_completions(iocp); // Discard any that have no pending async IO size_t sz = pn_list_size(iocp->zombie_list); for (size_t idx = 0; idx < sz;) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(iocp->zombie_list, idx); if (!iocpd->ops_in_progress) { pn_list_del(iocp->zombie_list, idx, 1); sz--; } else { idx++; } } unsigned shutdown_grace = 2000; char *override = getenv("PN_SHUTDOWN_GRACE"); if (override) { int grace = atoi(override); if (grace > 0 && grace < 60000) shutdown_grace = (unsigned) grace; } pn_timestamp_t now = pn_i_now(); pn_timestamp_t deadline = now + shutdown_grace; while (pn_list_size(iocp->zombie_list)) { if (now >= deadline) break; int rv = pni_iocp_wait_one(iocp, deadline - now, NULL); if (rv < 0) { iocp_log("unexpected IOCP failure on Proton IO shutdown %d\n", GetLastError()); break; } now = pn_i_now(); } if (now >= deadline && pn_list_size(iocp->zombie_list) && iocp->iocp_trace) // Should only happen if really slow TCP handshakes, i.e. total network failure iocp_log("network failure on Proton shutdown\n"); }
static void zombie_list_add(iocpdesc_t *iocpd) { assert(iocpd->closing); if (!iocpd->ops_in_progress) { // No need to make a zombie. if (iocpd->socket != INVALID_SOCKET) { closesocket(iocpd->socket); iocpd->socket = INVALID_SOCKET; iocpd->read_closed = true; } return; } // Allow 2 seconds for graceful shutdown before releasing socket resource. iocpd->reap_time = pn_i_now() + 2000; pn_list_add(iocpd->iocp->zombie_list, iocpd); }
static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *domain, const char *id ) { pn_timestamp_t now_msec = pn_i_now(); long now_sec = (long)(now_msec / 1000); pn_ssl_session_t *ssn = LL_HEAD( domain, ssn_cache ); while (ssn) { long expire = SSL_SESSION_get_time( ssn->session ) + SSL_SESSION_get_timeout( ssn->session ); if (expire < now_sec) { pn_ssl_session_t *next = ssn->ssn_cache_next; LL_REMOVE( domain, ssn_cache, ssn ); ssl_session_free( ssn ); ssn = next; continue; } if (!strcmp(ssn->id, id)) { break; } ssn = ssn->ssn_cache_next; } return ssn; }