GMutex lock; /* protects everything in this struct */
guint max_sessions;
GHashTable *sessions;
+ guint sessions_cookie;
};
#define DEFAULT_MAX_SESSIONS 0
'8', '9', '$', '-', '_', '.', '+'
};
+enum
+{
+ SIGNAL_SESSION_REMOVED,
+ SIGNAL_LAST
+};
+
+static guint gst_rtsp_session_pool_signals[SIGNAL_LAST] = { 0 };
+
GST_DEBUG_CATEGORY_STATIC (rtsp_session_debug);
#define GST_CAT_DEFAULT rtsp_session_debug
0, G_MAXUINT, DEFAULT_MAX_SESSIONS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED] =
+ g_signal_new ("session-removed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPSessionPoolClass,
+ session_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
+ 1, GST_TYPE_RTSP_SESSION);
+
klass->create_session_id = create_session_id;
klass->create_session = create_session;
priv->max_sessions = DEFAULT_MAX_SESSIONS;
}
+static GstRTSPFilterResult
+remove_sessions_func (GstRTSPSessionPool * pool, GstRTSPSession * session,
+ gpointer user_data)
+{
+ return GST_RTSP_FILTER_REMOVE;
+}
+
static void
gst_rtsp_session_pool_finalize (GObject * object)
{
GstRTSPSessionPool *pool = GST_RTSP_SESSION_POOL (object);
GstRTSPSessionPoolPrivate *priv = pool->priv;
- g_mutex_clear (&priv->lock);
+ gst_rtsp_session_pool_filter (pool, remove_sessions_func, NULL);
g_hash_table_unref (priv->sessions);
+ g_mutex_clear (&priv->lock);
G_OBJECT_CLASS (gst_rtsp_session_pool_parent_class)->finalize (object);
}
* Find the session with @sessionid in @pool. The access time of the session
* will be updated with gst_rtsp_session_touch().
*
- * Returns: (transfer full): the #GstRTSPSession with @sessionid or %NULL when the session did
- * not exist. g_object_unref() after usage.
+ * Returns: (transfer full) (nullable): the #GstRTSPSession with @sessionid
+ * or %NULL when the session did not exist. g_object_unref() after usage.
*/
GstRTSPSession *
gst_rtsp_session_pool_find (GstRTSPSessionPool * pool, const gchar * sessionid)
static gchar *
create_session_id (GstRTSPSessionPool * pool)
{
- gchar id[17];
+ gchar id[16];
gint i;
for (i = 0; i < 16; i++) {
session_id_charset[g_random_int_range (0,
G_N_ELEMENTS (session_id_charset))];
}
- id[16] = 0;
- return g_uri_escape_string (id, NULL, FALSE);
+ return g_strndup (id, 16);
}
static GstRTSPSession *
g_object_ref (result);
g_hash_table_insert (priv->sessions,
(gchar *) gst_rtsp_session_get_sessionid (result), result);
+ priv->sessions_cookie++;
}
g_mutex_unlock (&priv->lock);
priv = pool->priv;
g_mutex_lock (&priv->lock);
+ g_object_ref (sess);
found =
g_hash_table_remove (priv->sessions,
gst_rtsp_session_get_sessionid (sess));
+ if (found)
+ priv->sessions_cookie++;
g_mutex_unlock (&priv->lock);
+ if (found)
+ g_signal_emit (pool, gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED],
+ 0, sess);
+
+ g_object_unref (sess);
+
return found;
}
+typedef struct
+{
+ GTimeVal now;
+ GstRTSPSessionPool *pool;
+ GList *removed;
+} CleanupData;
+
static gboolean
-cleanup_func (gchar * sessionid, GstRTSPSession * sess, GTimeVal * now)
+cleanup_func (gchar * sessionid, GstRTSPSession * sess, CleanupData * data)
{
- return gst_rtsp_session_is_expired (sess, now);
+ gboolean expired;
+
+ expired = gst_rtsp_session_is_expired (sess, &data->now);
+ if (expired) {
+ GST_DEBUG ("session expired");
+ data->removed = g_list_prepend (data->removed, g_object_ref (sess));
+ }
+ return expired;
}
/**
{
GstRTSPSessionPoolPrivate *priv;
guint result;
- GTimeVal now;
+ CleanupData data;
+ GList *walk;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), 0);
priv = pool->priv;
- g_get_current_time (&now);
+ g_get_current_time (&data.now);
+ data.pool = pool;
+ data.removed = NULL;
g_mutex_lock (&priv->lock);
result =
g_hash_table_foreach_remove (priv->sessions, (GHRFunc) cleanup_func,
- &now);
+ &data);
+ if (result > 0)
+ priv->sessions_cookie++;
g_mutex_unlock (&priv->lock);
- return result;
-}
+ for (walk = data.removed; walk; walk = walk->next) {
+ GstRTSPSession *sess = walk->data;
-typedef struct
-{
- GstRTSPSessionPool *pool;
- GstRTSPSessionPoolFilterFunc func;
- gpointer user_data;
- GList *list;
-} FilterData;
+ g_signal_emit (pool,
+ gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0, sess);
-static gboolean
-filter_func (gchar * sessionid, GstRTSPSession * sess, FilterData * data)
-{
- GstRTSPFilterResult res;
-
- if (data->func)
- res = data->func (data->pool, sess, data->user_data);
- else
- res = GST_RTSP_FILTER_REF;
-
- switch (res) {
- case GST_RTSP_FILTER_REMOVE:
- return TRUE;
- case GST_RTSP_FILTER_REF:
- /* keep ref */
- data->list = g_list_prepend (data->list, g_object_ref (sess));
- /* fallthrough */
- default:
- case GST_RTSP_FILTER_KEEP:
- return FALSE;
+ g_object_unref (sess);
}
+ g_list_free (data.removed);
+
+ return result;
}
/**
* what happens to the session. @func will be called with the session pool
* locked so no further actions on @pool can be performed from @func.
*
- * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be removed from
+ * If @func returns #GST_RTSP_FILTER_REMOVE, the session will be set to the
+ * expired state with gst_rtsp_session_set_expired() and removed from
* @pool.
*
* If @func returns #GST_RTSP_FILTER_KEEP, the session will remain in @pool.
GstRTSPSessionPoolFilterFunc func, gpointer user_data)
{
GstRTSPSessionPoolPrivate *priv;
- FilterData data;
+ GHashTableIter iter;
+ gpointer key, value;
+ GList *result;
+ GHashTable *visited;
+ guint cookie;
g_return_val_if_fail (GST_IS_RTSP_SESSION_POOL (pool), NULL);
priv = pool->priv;
- data.pool = pool;
- data.func = func;
- data.user_data = user_data;
- data.list = NULL;
+ result = NULL;
+ if (func)
+ visited = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
g_mutex_lock (&priv->lock);
- g_hash_table_foreach_remove (priv->sessions, (GHRFunc) filter_func, &data);
+restart:
+ g_hash_table_iter_init (&iter, priv->sessions);
+ cookie = priv->sessions_cookie;
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ GstRTSPSession *session = value;
+ GstRTSPFilterResult res;
+ gboolean changed;
+
+ if (func) {
+ /* only visit each session once */
+ if (g_hash_table_contains (visited, session))
+ continue;
+
+ g_hash_table_add (visited, g_object_ref (session));
+ g_mutex_unlock (&priv->lock);
+
+ res = func (pool, session, user_data);
+
+ g_mutex_lock (&priv->lock);
+ } else
+ res = GST_RTSP_FILTER_REF;
+
+ changed = (cookie != priv->sessions_cookie);
+
+ switch (res) {
+ case GST_RTSP_FILTER_REMOVE:
+ {
+ gboolean removed = TRUE;
+
+ if (changed)
+ /* something changed, check if we still have the session */
+ removed = g_hash_table_remove (priv->sessions, key);
+ else
+ g_hash_table_iter_remove (&iter);
+
+ if (removed) {
+ /* if we managed to remove the session, update the cookie and
+ * signal */
+ cookie = ++priv->sessions_cookie;
+ g_mutex_unlock (&priv->lock);
+
+ g_signal_emit (pool,
+ gst_rtsp_session_pool_signals[SIGNAL_SESSION_REMOVED], 0,
+ session);
+
+ g_mutex_lock (&priv->lock);
+ /* cookie could have changed again, make sure we restart */
+ changed |= (cookie != priv->sessions_cookie);
+ }
+ break;
+ }
+ case GST_RTSP_FILTER_REF:
+ /* keep ref */
+ result = g_list_prepend (result, g_object_ref (session));
+ break;
+ case GST_RTSP_FILTER_KEEP:
+ default:
+ break;
+ }
+ if (changed)
+ goto restart;
+ }
g_mutex_unlock (&priv->lock);
- return data.list;
+ if (func)
+ g_hash_table_unref (visited);
+
+ return result;
}
typedef struct