static gboolean priv_discovery_tick (gpointer pointer) { NiceAgent *agent = pointer; gboolean ret; agent_lock(); if (g_source_is_destroyed (g_main_current_source ())) { nice_debug ("Source was destroyed. " "Avoided race condition in priv_discovery_tick"); agent_unlock (); return FALSE; } ret = priv_discovery_tick_unlocked (pointer); if (ret == FALSE) { if (agent->discovery_timer_source != NULL) { g_source_destroy (agent->discovery_timer_source); g_source_unref (agent->discovery_timer_source); agent->discovery_timer_source = NULL; } } agent_unlock_and_emit (agent); return ret; }
static gboolean nice_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error) { NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; NiceComponent *component = NULL; NiceStream *_stream = NULL; NiceAgent *agent; /* owned */ /* Has the agent disappeared? */ agent = g_weak_ref_get (&priv->agent_ref); if (agent == NULL) return TRUE; agent_lock (); /* Shut down the write side of the pseudo-TCP stream. */ if (agent_find_component (agent, priv->stream_id, priv->component_id, &_stream, &component) && agent->reliable && !pseudo_tcp_socket_is_closed (component->tcp)) { pseudo_tcp_socket_shutdown (component->tcp, PSEUDO_TCP_SHUTDOWN_WR); } agent_unlock (); g_object_unref (agent); return TRUE; }
/* 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); } }
static GSource * nice_output_stream_create_source (GPollableOutputStream *stream, GCancellable *cancellable) { NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; GSource *component_source = NULL; NiceComponent *component = NULL; NiceStream *_stream = NULL; NiceAgent *agent; /* owned */ component_source = g_pollable_source_new (G_OBJECT (stream)); if (cancellable) { GSource *cancellable_source = g_cancellable_source_new (cancellable); g_source_set_dummy_callback (cancellable_source); g_source_add_child_source (component_source, cancellable_source); g_source_unref (cancellable_source); } /* Closed streams cannot have sources. */ if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream))) return component_source; /* Has the agent disappeared? */ agent = g_weak_ref_get (&priv->agent_ref); if (agent == NULL) return component_source; agent_lock (); /* Grab the socket for this component. */ if (!agent_find_component (agent, priv->stream_id, priv->component_id, &_stream, &component)) { g_warning ("Could not find component %u in stream %u", priv->component_id, priv->stream_id); goto done; } if (component->tcp_writable_cancellable) { GSource *cancellable_source = g_cancellable_source_new (component->tcp_writable_cancellable); g_source_set_dummy_callback (cancellable_source); g_source_add_child_source (component_source, cancellable_source); g_source_unref (cancellable_source); } done: agent_unlock (); g_object_unref (agent); return component_source; }
static gboolean nice_input_stream_is_readable (GPollableInputStream *stream) { NiceInputStreamPrivate *priv = NICE_INPUT_STREAM (stream)->priv; Component *component = NULL; Stream *_stream = NULL; gboolean retval = FALSE; GSList *i; NiceAgent *agent; /* owned */ /* Closed streams are not readable. */ if (g_input_stream_is_closed (G_INPUT_STREAM (stream))) return FALSE; /* Has the agent disappeared? */ agent = g_weak_ref_get (&priv->agent_ref); if (agent == NULL) return FALSE; agent_lock (agent); if (!agent_find_component (agent, priv->stream_id, priv->component_id, &_stream, &component)) { g_warning ("Could not find component %u in stream %u", priv->component_id, priv->stream_id); goto done; } /* If it’s a reliable agent, see if there’s any pending data in the pseudo-TCP * buffer. */ if (agent->reliable && pseudo_tcp_socket_get_available_bytes (component->tcp) > 0) { retval = TRUE; goto done; } /* Check whether any of the component’s FDs are pollable. */ for (i = component->socket_sources; i != NULL; i = i->next) { SocketSource *socket_source = i->data; NiceSocket *nicesock = socket_source->socket; if (g_socket_condition_check (nicesock->fileno, G_IO_IN) != 0) { retval = TRUE; break; } } done: agent_unlock (agent); g_object_unref (agent); return retval; }
static gboolean nice_output_stream_is_writable (GPollableOutputStream *stream) { NiceOutputStreamPrivate *priv = NICE_OUTPUT_STREAM (stream)->priv; NiceComponent *component = NULL; NiceStream *_stream = NULL; gboolean retval = FALSE; NiceAgent *agent; /* owned */ /* Closed streams are not writeable. */ if (g_output_stream_is_closed (G_OUTPUT_STREAM (stream))) return FALSE; /* Has the agent disappeared? */ agent = g_weak_ref_get (&priv->agent_ref); if (agent == NULL) return FALSE; agent_lock (); if (!agent_find_component (agent, priv->stream_id, priv->component_id, &_stream, &component)) { g_warning ("Could not find component %u in stream %u", priv->component_id, priv->stream_id); goto done; } if (component->selected_pair.local != NULL) { NiceSocket *sockptr = component->selected_pair.local->sockptr; /* If it’s a reliable agent, see if there’s any space in the pseudo-TCP * output buffer. */ if (!nice_socket_is_reliable (sockptr)) { retval = pseudo_tcp_socket_can_send (component->tcp); } else { retval = (g_socket_condition_check (sockptr->fileno, G_IO_OUT) != 0); } } done: agent_unlock (); g_object_unref (agent); return retval; }
static gboolean socket_send_more ( GSocket *gsocket, GIOCondition condition, gpointer data) { NiceSocket *sock = (NiceSocket *) data; TcpPriv *priv = sock->priv; agent_lock (NULL); if (g_source_is_destroyed (g_main_current_source ())) { nice_debug ("Source was destroyed. " "Avoided race condition in tcp-bsd.c:socket_send_more"); agent_unlock (NULL); return FALSE; } /* connection hangs up or queue was emptied */ if (condition & G_IO_HUP || nice_socket_flush_send_queue_to_socket (sock->fileno, &priv->send_queue)) { g_source_destroy (priv->io_source); g_source_unref (priv->io_source); priv->io_source = NULL; agent_unlock (NULL); if (priv->writable_cb) priv->writable_cb (sock, priv->writable_data); return FALSE; } agent_unlock (NULL); return TRUE; }
static gboolean socket_send_more ( GSocket *gsocket, GIOCondition condition, gpointer data) { NiceSocket *sock = (NiceSocket *) data; TcpPriv *priv = sock->priv; struct to_be_sent *tbs = NULL; GError *gerr = NULL; agent_lock (); if (g_source_is_destroyed (g_main_current_source ())) { nice_debug ("Source was destroyed. " "Avoided race condition in tcp-bsd.c:socket_send_more"); agent_unlock (); return FALSE; } while ((tbs = g_queue_pop_head (&priv->send_queue)) != NULL) { int ret; if(condition & G_IO_HUP) { /* connection hangs up */ ret = -1; } else { GOutputVector local_bufs = { tbs->buf, tbs->length }; ret = g_socket_send_message (sock->fileno, NULL, &local_bufs, 1, NULL, 0, G_SOCKET_MSG_NONE, NULL, &gerr); } if (ret < 0) { if (g_error_matches (gerr, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { GOutputVector local_buf = { tbs->buf, tbs->length }; NiceOutputMessage local_message = {&local_buf, 1}; add_to_be_sent (sock, &local_message, 0, local_buf.size, TRUE); free_to_be_sent (tbs); g_error_free (gerr); break; } g_clear_error (&gerr); } else if (ret < (int) tbs->length) { GOutputVector local_buf = { tbs->buf + ret, tbs->length - ret }; NiceOutputMessage local_message = {&local_buf, 1}; add_to_be_sent (sock, &local_message, 0, local_buf.size, TRUE); free_to_be_sent (tbs); break; } free_to_be_sent (tbs); } if (g_queue_is_empty (&priv->send_queue)) { g_source_destroy (priv->io_source); g_source_unref (priv->io_source); priv->io_source = NULL; agent_unlock (); return FALSE; } agent_unlock (); return TRUE; }
static gboolean component_source_prepare (GSource *source, gint *timeout_) { ComponentSource *component_source = (ComponentSource *) source; NiceAgent *agent; NiceComponent *component; GSList *parentl, *childl; agent = g_weak_ref_get (&component_source->agent_ref); if (!agent) return FALSE; /* Needed due to accessing the Component. */ agent_lock (); if (!agent_find_component (agent, component_source->stream_id, component_source->component_id, NULL, &component)) goto done; if (component->socket_sources_age == component_source->component_socket_sources_age) goto done; /* If the age has changed, either * - one or more new socket has been prepended * - old sockets have been removed */ /* Add the new child sources. */ for (parentl = component->socket_sources; parentl; parentl = parentl->next) { SocketSource *parent_socket_source = parentl->data; SocketSource *child_socket_source; if (parent_socket_source->socket->fileno == NULL) continue; /* Iterating the list of socket sources every time isn't a big problem * because the number of pairs is limited ~100 normally, so there will * rarely be more than 10. */ childl = g_slist_find_custom (component_source->socket_sources, parent_socket_source->socket, _find_socket_source); /* If we have reached this state, then all sources new sources have been * added, because they are always prepended. */ if (childl) break; child_socket_source = g_slice_new0 (SocketSource); child_socket_source->socket = parent_socket_source->socket; child_socket_source->source = g_socket_create_source (child_socket_source->socket->fileno, G_IO_IN, NULL); g_source_set_dummy_callback (child_socket_source->source); g_source_add_child_source (source, child_socket_source->source); g_source_unref (child_socket_source->source); component_source->socket_sources = g_slist_prepend (component_source->socket_sources, child_socket_source); } for (childl = component_source->socket_sources; childl;) { SocketSource *child_socket_source = childl->data; GSList *next = childl->next; parentl = g_slist_find_custom (component->socket_sources, child_socket_source->socket, _find_socket_source); /* If this is not a currently used socket, remove the relevant source */ if (!parentl) { g_source_remove_child_source (source, child_socket_source->source); g_slice_free (SocketSource, child_socket_source); component_source->socket_sources = g_slist_delete_link (component_source->socket_sources, childl); } childl = next; } /* Update the age. */ component_source->component_socket_sources_age = component->socket_sources_age; done: agent_unlock_and_emit (agent); g_object_unref (agent); /* We can’t be sure if the ComponentSource itself needs to be dispatched until * poll() is called on all the child sources. */ return FALSE; }