static pn_list_t *iocp_map_close_all(iocp_t *iocp) { // Zombify stragglers, i.e. no pn_close() from the application. pn_list_t *externals = pn_list(PN_OBJECT, 0); for (pn_handle_t entry = pn_hash_head(iocp->iocpdesc_map); entry; entry = pn_hash_next(iocp->iocpdesc_map, entry)) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_hash_value(iocp->iocpdesc_map, entry); // Just listeners first. if (is_listener(iocpd)) { if (iocpd->external) { // Owned by application, just keep a temporary reference to it. // iocp_result_t structs must not be free'd until completed or // the completion port is closed. if (iocpd->ops_in_progress) pn_list_add(externals, iocpd); pni_iocpdesc_map_del(iocp, iocpd->socket); } else { // Make it a zombie. pni_iocp_begin_close(iocpd); } } } pni_iocp_drain_completions(iocp); for (pn_handle_t entry = pn_hash_head(iocp->iocpdesc_map); entry; entry = pn_hash_next(iocp->iocpdesc_map, entry)) { iocpdesc_t *iocpd = (iocpdesc_t *) pn_hash_value(iocp->iocpdesc_map, entry); if (iocpd->external) { iocpd->read_closed = true; // Do not consume from read side iocpd->write_closed = true; // Do not shutdown write side if (iocpd->ops_in_progress) pn_list_add(externals, iocpd); pni_iocpdesc_map_del(iocp, iocpd->socket); } else { // Make it a zombie. pni_iocp_begin_close(iocpd); } } return externals; }
void pni_iocp_begin_close(iocpdesc_t *iocpd) { assert (!iocpd->closing); if (is_listener(iocpd)) { // Listening socket is easy. Close the socket which will cancel async ops. pn_socket_t old_sock = iocpd->socket; iocpd->socket = INVALID_SOCKET; iocpd->closing = true; iocpd->read_closed = true; iocpd->write_closed = true; closesocket(old_sock); // Pending accepts will now complete. Zombie can die when all consumed. zombie_list_add(iocpd); pni_iocpdesc_map_del(iocpd->iocp, old_sock); // may pn_free *iocpd } else { // Continue async operation looking for graceful close confirmation or timeout. pn_socket_t old_sock = iocpd->socket; iocpd->closing = true; if (!iocpd->write_closed && !write_in_progress(iocpd)) iocp_shutdown(iocpd); zombie_list_add(iocpd); pni_iocpdesc_map_del(iocpd->iocp, old_sock); // may pn_free *iocpd } }
static void ensure_unique(pn_io_t *io, pn_socket_t new_socket) { // A brand new socket can have the same HANDLE value as a previous // one after a socketclose. If the application closes one itself // (i.e. not using pn_close), we don't find out about it until here. iocpdesc_t *iocpd = pni_iocpdesc_map_get(io->iocp, new_socket); if (iocpd) { if (io->trace) io_log("Stale external socket reference discarded\n"); // Re-use means former socket instance was closed assert(iocpd->ops_in_progress == 0); assert(iocpd->external); // Clean up the straggler as best we can pn_socket_t sock = iocpd->socket; iocpd->socket = INVALID_SOCKET; pni_iocpdesc_map_del(io->iocp, sock); // may free the iocpdesc_t depending on refcount } }