static void socket_connect(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port) { MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; if (!data->ss.handle) { struct sockaddr *dest = MVM_io_resolve_host_name(tc, host, port); uv_tcp_t *socket = MVM_malloc(sizeof(uv_tcp_t)); uv_connect_t *connect = MVM_malloc(sizeof(uv_connect_t)); int r; data->ss.cur_tc = tc; connect->data = data; if ((r = uv_tcp_init(tc->loop, socket)) < 0 || (r = uv_tcp_connect(connect, socket, dest, on_connect)) < 0) { MVM_free(socket); MVM_free(connect); MVM_free(dest); MVM_exception_throw_adhoc(tc, "Failed to connect: %s", uv_strerror(r)); } uv_ref((uv_handle_t *)socket); uv_run(tc->loop, UV_RUN_DEFAULT); data->ss.handle = (uv_stream_t *)socket; MVM_free(connect); MVM_free(dest); } else { MVM_exception_throw_adhoc(tc, "Socket is already bound or connected"); } }
static void socket_bind(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port, MVMint32 backlog) { MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; if (!data->ss.handle) { struct sockaddr *dest = MVM_io_resolve_host_name(tc, host, port); uv_tcp_t *socket = MVM_malloc(sizeof(uv_tcp_t)); int r; if ((r = uv_tcp_init(tc->loop, socket)) != 0 || (r = uv_tcp_bind(socket, dest, 0)) != 0) { MVM_free(socket); MVM_free(dest); MVM_exception_throw_adhoc(tc, "Failed to bind: %s", uv_strerror(r)); } MVM_free(dest); /* Start listening, but unref the socket so it won't get in the way of * other things we want to do on this event loop. */ socket->data = data; if ((r = uv_listen((uv_stream_t *)socket, backlog, on_connection)) != 0) { MVM_free(socket); MVM_exception_throw_adhoc(tc, "Failed to listen: %s", uv_strerror(r)); } uv_unref((uv_handle_t *)socket); data->ss.handle = (uv_stream_t *)socket; } else { MVM_exception_throw_adhoc(tc, "Socket is already bound or connected"); } }
/* Establishes a connection. */ static void socket_connect(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port) { MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; unsigned int interval_id; interval_id = MVM_telemetry_interval_start(tc, "syncsocket connect"); if (!data->handle) { struct sockaddr *dest = MVM_io_resolve_host_name(tc, host, port); int r; Socket s = socket(dest->sa_family , SOCK_STREAM , 0); if (MVM_IS_SOCKET_ERROR(s)) { MVM_free(dest); MVM_telemetry_interval_stop(tc, interval_id, "syncsocket connect"); throw_error(tc, s, "create socket"); } do { MVM_gc_mark_thread_blocked(tc); r = connect(s, dest, (socklen_t)get_struct_size_for_family(dest->sa_family)); MVM_gc_mark_thread_unblocked(tc); } while(r == -1 && errno == EINTR); MVM_free(dest); if (MVM_IS_SOCKET_ERROR(r)) { MVM_telemetry_interval_stop(tc, interval_id, "syncsocket connect"); throw_error(tc, s, "connect socket"); } data->handle = s; } else { MVM_telemetry_interval_stop(tc, interval_id, "syncsocket didn't connect"); MVM_exception_throw_adhoc(tc, "Socket is already bound or connected"); } }
static void socket_bind(MVMThreadContext *tc, MVMOSHandle *h, MVMString *host, MVMint64 port, MVMint32 backlog) { MVMIOSyncSocketData *data = (MVMIOSyncSocketData *)h->body.data; if (!data->handle) { struct sockaddr *dest = MVM_io_resolve_host_name(tc, host, port); int r; Socket s = socket(dest->sa_family , SOCK_STREAM , 0); if (MVM_IS_SOCKET_ERROR(s)) { MVM_free(dest); throw_error(tc, s, "create socket"); } /* On POSIX, we set the SO_REUSEADDR option, which allows re-use of * a port in TIME_WAIT state (modulo many hair details). Oringinally, * MoarVM used libuv, which does this automatically on non-Windows. * We have tests with bring up a server, then take it down, and then * bring another up on the same port, and we get test failures due * to racing to re-use the port without this. */ #ifndef _WIN32 { int one = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); } #endif r = bind(s, dest, (socklen_t)get_struct_size_for_family(dest->sa_family)); MVM_free(dest); if (MVM_IS_SOCKET_ERROR(r)) throw_error(tc, s, "bind socket"); r = listen(s, (int)backlog); if (MVM_IS_SOCKET_ERROR(r)) throw_error(tc, s, "start listening on socket"); data->handle = s; } else { MVM_exception_throw_adhoc(tc, "Socket is already bound or connected"); } }