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; }
void pn_close(pn_io_t *io, pn_socket_t socket) { iocpdesc_t *iocpd = pni_iocpdesc_map_get(io->iocp, socket); if (iocpd) pni_iocp_begin_close(iocpd); else { closesocket(socket); } }
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; }
static void complete_accept(accept_result_t *result, HRESULT status) { result->new_sock->ops_in_progress--; iocpdesc_t *ld = result->base.iocpd; if (ld->read_closed) { if (!result->new_sock->closing) pni_iocp_begin_close(result->new_sock); free(result); // discard reap_check(ld); } else { result->base.status = status; pn_list_add(ld->acceptor->accepts, result); pni_events_update(ld, ld->events | PN_READABLE); } }
pn_socket_t pni_iocp_begin_connect(iocp_t *iocp, pn_socket_t sock, struct addrinfo *addr, pn_error_t *error) { // addr lives for the duration of the async connect. Caller has passed ownership here. // See connect_result_finalize(). // Use of Windows-specific ConnectEx() requires our socket to be "loosely" pre-bound: sockaddr_storage sa; memset(&sa, 0, sizeof(sa)); sa.ss_family = addr->ai_family; if (bind(sock, (SOCKADDR *) &sa, addr->ai_addrlen)) { pni_win32_error(error, "begin async connection", WSAGetLastError()); if (iocp->iocp_trace) iocp_log("%s\n", pn_error_text(error)); closesocket(sock); freeaddrinfo(addr); return INVALID_SOCKET; } iocpdesc_t *iocpd = pni_iocpdesc_create(iocp, sock, false); bind_to_completion_port(iocpd); LPFN_CONNECTEX fn_connect_ex = lookup_connect_ex(iocpd->socket); connect_result_t *result = connect_result(iocpd, addr); DWORD unused; bool success = fn_connect_ex(iocpd->socket, result->addrinfo->ai_addr, result->addrinfo->ai_addrlen, NULL, 0, &unused, (LPOVERLAPPED) result); if (!success && WSAGetLastError() != ERROR_IO_PENDING) { pni_win32_error(error, "ConnectEx failure", WSAGetLastError()); pn_free(result); iocpd->write_closed = true; iocpd->read_closed = true; pni_iocp_begin_close(iocpd); sock = INVALID_SOCKET; if (iocp->iocp_trace) iocp_log("%s\n", pn_error_text(error)); } else { iocpd->ops_in_progress++; } return sock; }