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); }
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--; } }
void pn_selector_update(pn_selector_t *selector, pn_selectable_t *selectable) { // A selectable's fd may switch from PN_INVALID_SCOKET to a working socket between // update calls. If a selectable without a valid socket has a deadline, we need // a dummy iocpdesc_t to participate in the deadlines list. int idx = pni_selectable_get_index(selectable); assert(idx >= 0); pn_timestamp_t deadline = pn_selectable_get_deadline(selectable); pn_socket_t sock = pn_selectable_get_fd(selectable); iocpdesc_t *iocpd = (iocpdesc_t *) pn_list_get(selector->iocp_descriptors, idx); if (!iocpd && deadline && sock == PN_INVALID_SOCKET) { iocpd = pni_deadline_desc(selector->iocp); assert(iocpd); pn_list_set(selector->iocp_descriptors, idx, iocpd); pn_decref(iocpd); // life is solely tied to iocp_descriptors list iocpd->selector = selector; iocpd->selectable = selectable; } else if (iocpd && iocpd->deadline_desc && sock != PN_INVALID_SOCKET) { // Switching to a real socket. Stop using a deadline descriptor. deadlines_update(iocpd, 0); // decref descriptor in list and pick up a real iocpd below pn_list_set(selector->iocp_descriptors, idx, NULL); iocpd = NULL; } // The selectables socket may be set long after it has been added if (!iocpd && sock != PN_INVALID_SOCKET) { iocpd = pni_iocpdesc_map_get(selector->iocp, sock); if (!iocpd) { // Socket created outside proton. Hook it up to iocp. iocpd = pni_iocpdesc_create(selector->iocp, sock, true); assert(iocpd); if (iocpd) pni_iocpdesc_start(iocpd); } if (iocpd) { pn_list_set(selector->iocp_descriptors, idx, iocpd); iocpd->selector = selector; iocpd->selectable = selectable; } } if (iocpd) { assert(sock == iocpd->socket || iocpd->closing); int interests = PN_ERROR; // Always if (pn_selectable_is_reading(selectable)) { interests |= PN_READABLE; } if (pn_selectable_is_writing(selectable)) { interests |= PN_WRITABLE; } if (deadline) { interests |= PN_EXPIRED; } interests_update(iocpd, interests); deadlines_update(iocpd, deadline); } }
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); }
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; }
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 *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; } }
static void pn_list_finalize(void *object) { assert(object); pn_list_t *list = (pn_list_t *) object; for (size_t i = 0; i < list->size; i++) { pn_class_decref(list->clazz, pn_list_get(list, i)); } free(list->elements); }
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 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 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); }
static uintptr_t pn_list_hashcode(void *object) { assert(object); pn_list_t *list = (pn_list_t *) object; uintptr_t hash = 1; for (size_t i = 0; i < list->size; i++) { hash = hash * 31 + pn_hashcode(pn_list_get(list, i)); } return hash; }
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 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); }
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_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 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 } }
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_iterator(void) { pn_list_t *list = build_list(0, pn_string("one"), pn_string("two"), pn_string("three"), pn_string("four"), END); pn_iterator_t *it = pn_iterator(); pn_it_state_t *state = (pn_it_state_t *) pn_iterator_start (it, pn_it_next, sizeof(pn_it_state_t)); state->list = list; state->index = 0; void *obj; int index = 0; while ((obj = pn_iterator_next(it))) { assert(obj == pn_list_get(list, index++)); } assert(index == 4); pn_free(list); pn_free(it); }
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); }