void io_dispatch_connect(int fd, short ev, void *humppa) { struct io *io = humppa; int r, e; socklen_t sl; io_frame_enter("io_dispatch_connect", io, ev); if (ev == EV_TIMEOUT) { close(fd); io->sock = -1; io_callback(io, IO_TIMEOUT); } else { sl = sizeof(e); r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &e, &sl); if (r == -1) { warn("io_dispatch_connect: getsockopt"); e = errno; } if (e) { close(fd); io->sock = -1; io->error = strerror(e); io_callback(io, e == ETIMEDOUT ? IO_TIMEOUT : IO_ERROR); } else { io->state = IO_STATE_UP; io_callback(io, IO_CONNECTED); } } io_frame_leave(io); }
void io_dispatch_write_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int n, saved_errno; size_t w2, w; io_frame_enter("io_dispatch_write_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } w = io_queued(io); switch ((n = iobuf_write_ssl(io->iobuf, (SSL*)io->ssl))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_write_ssl); break; case IOBUF_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_write_ssl); break; case IOBUF_CLOSED: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); break; case IOBUF_SSLERROR: io->error = io_ssl_error(); ssl_error("io_dispatch_write_ssl:SSL_write"); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_write_ssl(...) -> w=%d\n", n); w2 = io_queued(io); if (w > io->lowat && w2 <= io->lowat) io_callback(io, IO_LOWAT); break; } leave: io_frame_leave(io); }
void io_dispatch_read_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int n, saved_errno; io_frame_enter("io_dispatch_read_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } again: iobuf_normalize(&io->iobuf); switch ((n = iobuf_read_ssl(&io->iobuf, (SSL*)io->ssl))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_read_ssl); break; case IOBUF_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_read_ssl); break; case IOBUF_CLOSED: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); break; case IOBUF_SSLERROR: io->error = io_ssl_error(); ssl_error("io_dispatch_read_ssl:SSL_read"); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_read_ssl(...) -> r=%d\n", n); io_callback(io, IO_DATAIN); if (current == io && IO_READING(io) && SSL_pending(io->ssl)) goto again; } leave: io_frame_leave(io); }
/* This must be called with the agent lock *held*. */ void nice_component_emit_io_callback (NiceComponent *component, const guint8 *buf, gsize buf_len) { NiceAgent *agent; guint stream_id, component_id; NiceAgentRecvFunc io_callback; gpointer io_user_data; g_assert (component != NULL); g_assert (buf != NULL); g_assert (buf_len > 0); agent = component->agent; stream_id = component->stream->id; component_id = component->id; g_mutex_lock (&component->io_mutex); io_callback = component->io_callback; io_user_data = component->io_user_data; g_mutex_unlock (&component->io_mutex); /* Allow this to be called with a NULL io_callback, since the caller can’t * lock io_mutex to check beforehand. */ if (io_callback == NULL) return; g_assert (NICE_IS_AGENT (agent)); g_assert (stream_id > 0); g_assert (component_id > 0); g_assert (io_callback != NULL); /* Only allocate a closure if the callback is being deferred to an idle * handler. */ if (g_main_context_is_owner (component->ctx)) { /* Thread owns the main context, so invoke the callback directly. */ agent_unlock_and_emit (agent); io_callback (agent, stream_id, component_id, buf_len, (gchar *) buf, io_user_data); agent_lock (); } else { IOCallbackData *data; g_mutex_lock (&component->io_mutex); /* Slow path: Current thread doesn’t own the Component’s context at the * moment, so schedule the callback in an idle handler. */ data = io_callback_data_new (buf, buf_len); g_queue_push_tail (&component->pending_io_messages, data); /* transfer ownership */ nice_debug ("%s: **WARNING: SLOW PATH**", G_STRFUNC); nice_component_schedule_io_callback (component); g_mutex_unlock (&component->io_mutex); } }
void io_dispatch(int fd, short ev, void *humppa) { struct io *io = humppa; size_t w; ssize_t n; int saved_errno; io_frame_enter("io_dispatch", io, ev); if (ev == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } if (ev & EV_WRITE && (w = io_queued(io))) { if ((n = iobuf_write(&io->iobuf, io->sock)) < 0) { if (n == IOBUF_WANT_WRITE) /* kqueue bug? */ goto read; if (n == IOBUF_CLOSED) io_callback(io, IO_DISCONNECTED); else { saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); } goto leave; } if (w > io->lowat && w - n <= io->lowat) io_callback(io, IO_LOWAT); } read: if (ev & EV_READ) { iobuf_normalize(&io->iobuf); if ((n = iobuf_read(&io->iobuf, io->sock)) < 0) { if (n == IOBUF_CLOSED) io_callback(io, IO_DISCONNECTED); else { saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); } goto leave; } if (n) io_callback(io, IO_DATAIN); } leave: io_frame_leave(io); }
void io_dispatch_connect_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int e, ret; io_frame_enter("io_dispatch_connect_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } if ((ret = SSL_connect(io->ssl)) > 0) { io->state = IO_STATE_UP; io_callback(io, IO_TLSREADY); goto leave; } switch ((e = SSL_get_error(io->ssl, ret))) { case SSL_ERROR_WANT_READ: io_reset(io, EV_READ, io_dispatch_connect_ssl); break; case SSL_ERROR_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_connect_ssl); break; default: io->error = io_ssl_error(); ssl_error("io_dispatch_connect_ssl:SSL_connect"); io_callback(io, IO_TLSERROR); break; } leave: io_frame_leave(io); }
/* This is called with the global agent lock released. It does not take that * lock, but does take the io_mutex. */ static gboolean emit_io_callback_cb (gpointer user_data) { NiceComponent *component = user_data; IOCallbackData *data; NiceAgentRecvFunc io_callback; gpointer io_user_data; guint stream_id, component_id; NiceAgent *agent; agent = component->agent; g_object_ref (agent); stream_id = component->stream->id; component_id = component->id; g_mutex_lock (&component->io_mutex); /* The members of Component are guaranteed not to have changed since this * GSource was attached in nice_component_emit_io_callback(). The Component’s agent * and stream are immutable after construction, as are the stream and * component IDs. The callback and its user data may have changed, but are * guaranteed to be non-%NULL at the start as the idle source is removed when * the callback is set to %NULL. They may become %NULL during the io_callback, * so must be re-checked every loop iteration. The data buffer is copied into * the #IOCallbackData closure. * * If the component is destroyed (which happens if the agent or stream are * destroyed) between attaching the GSource and firing it, the GSource is * detached during dispose and this callback is never invoked. If the * agent is destroyed during an io_callback, its weak pointer will be * nullified. Similarly, the Component needs to be re-queried for after every * iteration, just in case the client has removed the stream in the * callback. */ while (TRUE) { io_callback = component->io_callback; io_user_data = component->io_user_data; data = g_queue_peek_head (&component->pending_io_messages); if (data == NULL || io_callback == NULL) break; g_mutex_unlock (&component->io_mutex); io_callback (agent, stream_id, component_id, data->buf_len - data->offset, (gchar *) data->buf + data->offset, io_user_data); /* Check for the user destroying things underneath our feet. */ if (!agent_find_component (agent, stream_id, component_id, NULL, &component)) { nice_debug ("%s: Agent or component destroyed.", G_STRFUNC); goto done; } g_queue_pop_head (&component->pending_io_messages); io_callback_data_free (data); g_mutex_lock (&component->io_mutex); } component->io_callback_id = 0; g_mutex_unlock (&component->io_mutex); done: g_object_unref (agent); return G_SOURCE_REMOVE; }