static void zombie_list_hard_close_all(iocp_t *iocp) { pni_iocp_drain_completions(iocp); size_t zs = pn_list_size(iocp->zombie_list); for (size_t i = 0; i < zs; i++) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(iocp->zombie_list, i); if (iocpd->socket != INVALID_SOCKET) { closesocket(iocpd->socket); iocpd->socket = INVALID_SOCKET; iocpd->read_closed = true; iocpd->write_closed = true; } } pni_iocp_drain_completions(iocp); // Zombies should be all gone. Do a sanity check. zs = pn_list_size(iocp->zombie_list); int remaining = 0; int ops = 0; for (size_t i = 0; i < zs; i++) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(iocp->zombie_list, i); remaining++; ops += iocpd->ops_in_progress; } if (remaining) iocp_log("Proton: %d unfinished close operations (ops count = %d)\n", remaining, ops); }
pn_socket_t pni_iocp_end_accept(iocpdesc_t *ld, sockaddr *addr, socklen_t *addrlen, bool *would_block, pn_error_t *error) { if (!is_listener(ld)) { set_iocp_error_status(error, PN_ERR, WSAEOPNOTSUPP); return INVALID_SOCKET; } if (ld->read_closed) { set_iocp_error_status(error, PN_ERR, WSAENOTSOCK); return INVALID_SOCKET; } if (pn_list_size(ld->acceptor->accepts) == 0) { if (ld->events & PN_READABLE && ld->iocp->iocp_trace) iocp_log("listen socket readable with no available accept completions\n"); *would_block = true; return INVALID_SOCKET; } accept_result_t *result = (accept_result_t *) pn_list_get(ld->acceptor->accepts, 0); pn_list_del(ld->acceptor->accepts, 0, 1); if (!pn_list_size(ld->acceptor->accepts)) pni_events_update(ld, ld->events & ~PN_READABLE); // No pending accepts pn_socket_t accept_sock; if (result->base.status) { accept_sock = INVALID_SOCKET; pni_win32_error(ld->error, "accept failure", result->base.status); if (ld->iocp->iocp_trace) iocp_log("%s\n", pn_error_text(ld->error)); // App never sees this socket so close it here. pni_iocp_begin_close(result->new_sock); } else { accept_sock = result->new_sock->socket; // AcceptEx special setsockopt: setsockopt(accept_sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&ld->socket, sizeof (SOCKET)); if (addr && addrlen && *addrlen > 0) { sockaddr_storage *local_addr = NULL; sockaddr_storage *remote_addr = NULL; int local_addrlen, remote_addrlen; LPFN_GETACCEPTEXSOCKADDRS fn = ld->acceptor->fn_get_accept_ex_sockaddrs; fn(result->address_buffer, 0, IOCP_SOCKADDRMAXLEN, IOCP_SOCKADDRMAXLEN, (SOCKADDR **) &local_addr, &local_addrlen, (SOCKADDR **) &remote_addr, &remote_addrlen); *addrlen = pn_min(*addrlen, remote_addrlen); memmove(addr, remote_addr, *addrlen); } } if (accept_sock != INVALID_SOCKET) { // Connected. result->new_sock->read_closed = false; result->new_sock->write_closed = false; } // Done with the completion result, so reuse it result->new_sock = NULL; begin_accept(ld->acceptor, result); return accept_sock; }
static void test_list_refcount(size_t capacity) { void *one = pn_class_new(PN_OBJECT, 0); void *two = pn_class_new(PN_OBJECT, 0); void *three = pn_class_new(PN_OBJECT, 0); void *four = pn_class_new(PN_OBJECT, 0); pn_list_t *list = pn_list(PN_OBJECT, 0); assert(!pn_list_add(list, one)); assert(!pn_list_add(list, two)); assert(!pn_list_add(list, three)); assert(!pn_list_add(list, four)); assert(pn_list_get(list, 0) == one); assert(pn_list_get(list, 1) == two); assert(pn_list_get(list, 2) == three); assert(pn_list_get(list, 3) == four); assert(pn_list_size(list) == 4); assert(pn_refcount(one) == 2); assert(pn_refcount(two) == 2); assert(pn_refcount(three) == 2); assert(pn_refcount(four) == 2); pn_list_del(list, 1, 2); assert(pn_list_size(list) == 2); assert(pn_refcount(one) == 2); assert(pn_refcount(two) == 1); assert(pn_refcount(three) == 1); assert(pn_refcount(four) == 2); assert(pn_list_get(list, 0) == one); assert(pn_list_get(list, 1) == four); assert(!pn_list_add(list, one)); assert(pn_list_size(list) == 3); assert(pn_refcount(one) == 3); pn_decref(list); assert(pn_refcount(one) == 1); assert(pn_refcount(two) == 1); assert(pn_refcount(three) == 1); assert(pn_refcount(four) == 1); pn_decref(one); pn_decref(two); pn_decref(three); pn_decref(four); }
void pn_selector_remove(pn_selector_t *selector, pn_selectable_t *selectable) { assert(selector); assert(selectable); int idx = pni_selectable_get_index(selectable); assert(idx >= 0); iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(selector->iocp_descriptors, idx); if (iocpd) { if (selector->current_triggered == iocpd) selector->current_triggered = iocpd->triggered_list_next; interests_update(iocpd, 0); deadlines_update(iocpd, 0); assert(selector->triggered_list_head != iocpd && !iocpd->triggered_list_prev); assert(selector->deadlines_head != iocpd && !iocpd->deadlines_prev); iocpd->selector = NULL; iocpd->selectable = NULL; } pn_list_del(selector->selectables, idx, 1); pn_list_del(selector->iocp_descriptors, idx, 1); size_t size = pn_list_size(selector->selectables); for (size_t i = idx; i < size; i++) { pn_selectable_t *sel = (pn_selectable_t *) pn_list_get(selector->selectables, i); pni_selectable_set_index(sel, i); } pni_selectable_set_index(selectable, -1); if (selector->current >= (size_t) idx) { selector->current--; } }
int pn_transform_apply(pn_transform_t *transform, const char *src, pn_string_t *dst) { for (size_t i = 0; i < pn_list_size(transform->rules); i++) { pn_rule_t *rule = (pn_rule_t *) pn_list_get(transform->rules, i); if (pni_match(&transform->matcher, pn_string_get(rule->pattern), src)) { transform->matched = true; if (!pn_string_get(rule->substitution)) { return pn_string_set(dst, NULL); } while (true) { size_t capacity = pn_string_capacity(dst); size_t n = pni_substitute(&transform->matcher, pn_string_get(rule->substitution), pn_string_buffer(dst), capacity); int err = pn_string_resize(dst, n); if (err) return err; if (n <= capacity) { return 0; } } } } transform->matched = false; return pn_string_set(dst, src); }
static void *pn_it_next(void *state) { pn_it_state_t *it = (pn_it_state_t *) state; if (it->index < pn_list_size(it->list)) { return pn_list_get(it->list, it->index++); } else { return NULL; } }
pn_timestamp_t pni_zombie_deadline(iocp_t *iocp) { if (pn_list_size(iocp->zombie_list)) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(iocp->zombie_list, 0); return iocpd->reap_time; } return 0; }
static void pni_acceptor_finalize(void *object) { pni_acceptor_t *acceptor = (pni_acceptor_t *) object; size_t len = pn_list_size(acceptor->accepts); for (size_t i = 0; i < len; i++) free(pn_list_get(acceptor->accepts, i)); pn_free(acceptor->accepts); }
static void *pni_list_next(void *ctx) { pni_list_iter_t *iter = (pni_list_iter_t *) ctx; if (iter->index < pn_list_size(iter->list)) { return pn_list_get(iter->list, iter->index++); } else { return NULL; } }
static void test_list(size_t capacity) { pn_list_t *list = pn_list(PN_WEAKREF, 0); assert(pn_list_size(list) == 0); assert(!pn_list_add(list, (void *) 0)); assert(!pn_list_add(list, (void *) 1)); assert(!pn_list_add(list, (void *) 2)); assert(!pn_list_add(list, (void *) 3)); assert(pn_list_get(list, 0) == (void *) 0); assert(pn_list_get(list, 1) == (void *) 1); assert(pn_list_get(list, 2) == (void *) 2); assert(pn_list_get(list, 3) == (void *) 3); assert(pn_list_size(list) == 4); pn_list_del(list, 1, 2); assert(pn_list_size(list) == 2); assert(pn_list_get(list, 0) == (void *) 0); assert(pn_list_get(list, 1) == (void *) 3); pn_decref(list); }
static intptr_t pn_list_compare(void *oa, void *ob) { assert(oa); assert(ob); pn_list_t *a = (pn_list_t *) oa; pn_list_t *b = (pn_list_t *) ob; size_t na = pn_list_size(a); size_t nb = pn_list_size(b); if (na != nb) { return nb - na; } else { for (size_t i = 0; i < na; i++) { intptr_t delta = pn_compare(pn_list_get(a, i), pn_list_get(b, i)); if (delta) return delta; } } return 0; }
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"); }
void test_heap(int seed, int size) { srand(seed); pn_list_t *list = pn_list(PN_VOID, 0); intptr_t min = 0; intptr_t max = 0; for (int i = 0; i < size; i++) { intptr_t r = rand(); if (i == 0) { min = r; max = r; } else { if (r < min) { min = r; } if (r > max) { max = r; } } pn_list_minpush(list, (void *) r); } intptr_t prev = (intptr_t) pn_list_minpop(list); assert(prev == min); assert(pn_list_size(list) == (size_t)(size - 1)); int count = 0; while (pn_list_size(list)) { intptr_t r = (intptr_t) pn_list_minpop(list); assert(r >= prev); prev = r; count++; } assert(count == size - 1); assert(prev == max); pn_free(list); }
void global_shutdown(global_context_t *gc) { if (gc->shutting_down) return; gc->shutting_down = true; pn_acceptor_close(gc->acceptor); size_t n = pn_list_size(gc->active_connections); for (size_t i = 0; i < n; i++) { pn_connection_t *conn = (pn_connection_t *) pn_list_get(gc->active_connections, i); if (!(pn_connection_state(conn) & PN_LOCAL_CLOSED)) { pn_connection_close(conn); } } }
static void begin_accept(pni_acceptor_t *acceptor, accept_result_t *result) { if (acceptor->listen_sock->closing) { if (result) { free(result); acceptor->accept_queue_size--; } if (acceptor->accept_queue_size == 0) acceptor->signalled = true; return; } if (result) { reset_accept_result(result); } else { if (acceptor->accept_queue_size < IOCP_MAX_ACCEPTS && pn_list_size(acceptor->accepts) == acceptor->accept_queue_size ) { result = accept_result(acceptor->listen_sock); acceptor->accept_queue_size++; } else { // an async accept is still pending or max concurrent accepts already hit return; } } result->new_sock = create_same_type_socket(acceptor->listen_sock); if (result->new_sock) { // Not yet connected. result->new_sock->read_closed = true; result->new_sock->write_closed = true; bool success = acceptor->fn_accept_ex(acceptor->listen_sock->socket, result->new_sock->socket, result->address_buffer, 0, IOCP_SOCKADDRMAXLEN, IOCP_SOCKADDRMAXLEN, &result->unused, (LPOVERLAPPED) result); if (!success && WSAGetLastError() != ERROR_IO_PENDING) { result->base.status = WSAGetLastError(); pn_list_add(acceptor->accepts, result); pni_events_update(acceptor->listen_sock, acceptor->listen_sock->events | PN_READABLE); } else { acceptor->listen_sock->ops_in_progress++; // This socket is equally involved in the async operation. result->new_sock->ops_in_progress++; } } else { iocpdesc_fail(acceptor->listen_sock, WSAGetLastError(), "create accept socket"); } }
void pn_selector_add(pn_selector_t *selector, pn_selectable_t *selectable) { assert(selector); assert(selectable); assert(pni_selectable_get_index(selectable) < 0); pn_socket_t sock = pn_selectable_get_fd(selectable); iocpdesc_t *iocpd = NULL; if (pni_selectable_get_index(selectable) < 0) { pn_list_add(selector->selectables, selectable); pn_list_add(selector->iocp_descriptors, NULL); size_t size = pn_list_size(selector->selectables); pni_selectable_set_index(selectable, size - 1); } pn_selector_update(selector, selectable); }
static int pn_list_inspect(void *obj, pn_string_t *dst) { assert(obj); pn_list_t *list = (pn_list_t *) obj; int err = pn_string_addf(dst, "["); if (err) return err; size_t n = pn_list_size(list); for (size_t i = 0; i < n; i++) { if (i > 0) { err = pn_string_addf(dst, ", "); if (err) return err; } err = pn_class_inspect(list->clazz, pn_list_get(list, i), dst); if (err) return err; } return pn_string_addf(dst, "]"); }
static void test_build_list(void) { pn_list_t *l = build_list(0, pn_string("one"), pn_string("two"), pn_string("three"), END); assert(pn_list_size(l) == 3); assert(pn_strequals(pn_string_get((pn_string_t *) pn_list_get(l, 0)), "one")); assert(pn_strequals(pn_string_get((pn_string_t *) pn_list_get(l, 1)), "two")); assert(pn_strequals(pn_string_get((pn_string_t *) pn_list_get(l, 2)), "three")); pn_free(l); }
void pni_zombie_check(iocp_t *iocp, pn_timestamp_t now) { pn_list_t *zl = iocp->zombie_list; // Look for stale zombies that should have been reaped by "now" for (size_t idx = 0; idx < pn_list_size(zl); idx++) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(zl, idx); if (iocpd->reap_time > now) return; if (iocpd->socket == INVALID_SOCKET) continue; assert(iocpd->ops_in_progress > 0); if (iocp->iocp_trace) iocp_log("async close: graceful close timeout exceeded\n"); closesocket(iocpd->socket); iocpd->socket = INVALID_SOCKET; iocpd->read_closed = true; // outstanding ops should complete immediately now } }
void pni_iocp_finalize(void *obj) { iocp_t *iocp = (iocp_t *) obj; // Move sockets to closed state, except external sockets. pn_list_t *externals = iocp_map_close_all(iocp); // Now everything with ops_in_progress is in the zombie_list or the externals list. assert(!pn_hash_head(iocp->iocpdesc_map)); pn_free(iocp->iocpdesc_map); drain_zombie_completions(iocp); // Last chance for graceful close zombie_list_hard_close_all(iocp); CloseHandle(iocp->completion_port); // This cancels all our async ops iocp->completion_port = NULL; if (pn_list_size(externals) && iocp->iocp_trace) iocp_log("%d external sockets not closed and removed from Proton IOCP control\n", pn_list_size(externals)); // Now safe to free everything that might be touched by a former async operation. pn_free(externals); pn_free(iocp->zombie_list); pni_shared_pool_free(iocp); }
static void test_map_iteration(int n) { pn_list_t *pairs = pn_list(PN_OBJECT, 2*n); for (int i = 0; i < n; i++) { void *key = pn_class_new(PN_OBJECT, 0); void *value = pn_class_new(PN_OBJECT, 0); pn_list_add(pairs, key); pn_list_add(pairs, value); pn_decref(key); pn_decref(value); } pn_map_t *map = pn_map(PN_OBJECT, PN_OBJECT, 0, 0.75); assert(pn_map_head(map) == 0); for (int i = 0; i < n; i++) { pn_map_put(map, pn_list_get(pairs, 2*i), pn_list_get(pairs, 2*i + 1)); } for (pn_handle_t entry = pn_map_head(map); entry; entry = pn_map_next(map, entry)) { void *key = pn_map_key(map, entry); void *value = pn_map_value(map, entry); ssize_t idx = pn_list_index(pairs, key); assert(idx >= 0); assert(pn_list_get(pairs, idx) == key); assert(pn_list_get(pairs, idx + 1) == value); pn_list_del(pairs, idx, 2); } assert(pn_list_size(pairs) == 0); pn_decref(map); pn_decref(pairs); }
void *pn_list_minpop(pn_list_t *list) { assert(list); // we use one based indexing for the heap void **heap = list->elements - 1; void *min = heap[1]; void *last = pn_list_pop(list); int size = pn_list_size(list); int now, child; for (now = 1; now*2 <= size; now = child) { child = now*2; if (child != size && pn_class_compare(list->clazz, heap[child], heap[child + 1]) > 0) { child++; } if (pn_class_compare(list->clazz, last, heap[child]) > 0) { heap[now] = heap[child]; } else { break; } } heap[now] = last; return min; }
size_t pn_selector_size(pn_selector_t *selector) { assert(selector); return pn_list_size(selector->selectables); }