Coverage Report

Created: 2026-08-25 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gstreamer/gst/gsttask.c
Line
Count
Source
1
/* GStreamer
2
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3
 *                    2005 Wim Taymans <wim@fluendo.com>
4
 *
5
 * gsttask.c: Streaming tasks
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Library General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Library General Public
18
 * License along with this library; if not, write to the
19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20
 * Boston, MA 02110-1301, USA.
21
 */
22
23
/**
24
 * SECTION:gsttask
25
 * @title: GstTask
26
 * @short_description: Abstraction of GStreamer streaming threads.
27
 * @see_also: #GstElement, #GstPad
28
 *
29
 * #GstTask is used by #GstElement and #GstPad to provide the data passing
30
 * threads in a #GstPipeline.
31
 *
32
 * A #GstPad will typically start a #GstTask to push or pull data to/from the
33
 * peer pads. Most source elements start a #GstTask to push data. In some cases
34
 * a demuxer element can start a #GstTask to pull data from a peer element. This
35
 * is typically done when the demuxer can perform random access on the upstream
36
 * peer element for improved performance.
37
 *
38
 * Although convenience functions exist on #GstPad to start/pause/stop tasks, it
39
 * might sometimes be needed to create a #GstTask manually if it is not related to
40
 * a #GstPad.
41
 *
42
 * Before the #GstTask can be run, it needs a #GRecMutex that can be set with
43
 * gst_task_set_lock().
44
 *
45
 * The task can be started, paused and stopped with gst_task_start(), gst_task_pause()
46
 * and gst_task_stop() respectively or with the gst_task_set_state() function.
47
 *
48
 * A #GstTask will repeatedly call the #GstTaskFunction with the user data
49
 * that was provided when creating the task with gst_task_new(). While calling
50
 * the function it will acquire the provided lock. The provided lock is released
51
 * when the task pauses or stops.
52
 *
53
 * Stopping a task with gst_task_stop() will not immediately make sure the task is
54
 * not running anymore. Use gst_task_join() to make sure the task is completely
55
 * stopped and the thread is stopped.
56
 *
57
 * After creating a #GstTask, use gst_object_unref() to free its resources. This can
58
 * only be done when the task is not running anymore.
59
 *
60
 * Task functions can send a #GstMessage to send out-of-band data to the
61
 * application. The application can receive messages from the #GstBus in its
62
 * mainloop.
63
 *
64
 * For debugging purposes, the task will configure its object name as the thread
65
 * name on Linux. Please note that the object name should be configured before the
66
 * task is started; changing the object name after the task has been started, has
67
 * no effect on the thread name.
68
 */
69
70
#include "gst_private.h"
71
72
#include "gstinfo.h"
73
#include "gsttask.h"
74
#include "glib-compat-private.h"
75
76
#include <stdio.h>
77
78
#ifdef HAVE_SYS_PRCTL_H
79
#include <sys/prctl.h>
80
#endif
81
82
#ifdef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID
83
#include <pthread.h>
84
#endif
85
86
GST_DEBUG_CATEGORY_STATIC (task_debug);
87
#define GST_CAT_DEFAULT (task_debug)
88
89
402k
#define SET_TASK_STATE(t,s) (g_atomic_int_set (&GST_TASK_STATE(t), (s)))
90
310k
#define GET_TASK_STATE(t)   ((GstTaskState) g_atomic_int_get (&GST_TASK_STATE(t)))
91
92
static const char *
93
task_state_to_string (GstTaskState state)
94
0
{
95
0
  switch (state) {
96
0
    case GST_TASK_STARTED:
97
0
      return "started";
98
0
    case GST_TASK_PAUSED:
99
0
      return "paused";
100
0
    case GST_TASK_STOPPED:
101
0
      return "stopped";
102
0
    default:
103
0
      return "(unknown)";
104
0
  }
105
0
}
106
107
struct _GstTaskPrivate
108
{
109
  /* callbacks for managing the thread of this task */
110
  GstTaskThreadFunc enter_func;
111
  gpointer enter_user_data;
112
  GDestroyNotify enter_notify;
113
114
  GstTaskThreadFunc leave_func;
115
  gpointer leave_user_data;
116
  GDestroyNotify leave_notify;
117
118
  /* configured pool */
119
  GstTaskPool *pool;
120
121
  /* remember the pool and id that is currently running. */
122
  gpointer id;
123
  GstTaskPool *pool_id;
124
125
  /* we're currently inside gst_task_join() */
126
  gboolean joining;
127
};
128
129
#ifdef _MSC_VER
130
#define WIN32_LEAN_AND_MEAN
131
#include <windows.h>
132
133
typedef HRESULT (WINAPI * pSetThreadDescription) (HANDLE hThread,
134
    PCWSTR lpThreadDescription);
135
static pSetThreadDescription SetThreadDescriptionFunc = NULL;
136
HMODULE kernel32_module = NULL;
137
138
struct _THREADNAME_INFO
139
{
140
  DWORD dwType;                 // must be 0x1000
141
  LPCSTR szName;                // pointer to name (in user addr space)
142
  DWORD dwThreadID;             // thread ID (-1=caller thread)
143
  DWORD dwFlags;                // reserved for future use, must be zero
144
};
145
typedef struct _THREADNAME_INFO THREADNAME_INFO;
146
147
static void
148
SetThreadName (DWORD dwThreadID, LPCSTR szThreadName)
149
{
150
  THREADNAME_INFO info;
151
  info.dwType = 0x1000;
152
  info.szName = szThreadName;
153
  info.dwThreadID = dwThreadID;
154
  info.dwFlags = 0;
155
156
  __try {
157
    RaiseException (0x406D1388, 0, sizeof (info) / sizeof (DWORD),
158
        (const ULONG_PTR *) &info);
159
  }
160
  __except (EXCEPTION_CONTINUE_EXECUTION) {
161
  }
162
}
163
164
static gboolean
165
gst_task_win32_load_library (void)
166
{
167
  /* FIXME: Add support for UWP app */
168
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
169
  static gsize _init_once = 0;
170
  if (g_once_init_enter (&_init_once)) {
171
    kernel32_module = LoadLibraryW (L"kernel32.dll");
172
    if (kernel32_module) {
173
      SetThreadDescriptionFunc =
174
          (pSetThreadDescription) GetProcAddress (kernel32_module,
175
          "SetThreadDescription");
176
      if (!SetThreadDescriptionFunc)
177
        FreeLibrary (kernel32_module);
178
    }
179
    g_once_init_leave (&_init_once, 1);
180
  }
181
#endif
182
183
  return !!SetThreadDescriptionFunc;
184
}
185
186
static gboolean
187
gst_task_win32_set_thread_desc (const gchar * name)
188
{
189
  HRESULT hr;
190
  wchar_t *namew;
191
192
  if (!gst_task_win32_load_library () || !name)
193
    return FALSE;
194
195
  namew = g_utf8_to_utf16 (name, -1, NULL, NULL, NULL);
196
  if (!namew)
197
    return FALSE;
198
199
  hr = SetThreadDescriptionFunc (GetCurrentThread (), namew);
200
201
  g_free (namew);
202
  return SUCCEEDED (hr);
203
}
204
205
static void
206
gst_task_win32_set_thread_name (const gchar * name)
207
{
208
  /* Prefer SetThreadDescription over exception based way if available,
209
   * since thread description set by SetThreadDescription will be preserved
210
   * in dump file */
211
  if (!gst_task_win32_set_thread_desc (name))
212
    SetThreadName ((DWORD) - 1, name);
213
}
214
#endif
215
216
static void gst_task_finalize (GObject * object);
217
218
static void gst_task_func (GstTask * task);
219
220
static GMutex pool_lock;
221
222
static GstTaskPool *_global_task_pool = NULL;
223
224
#define _do_init \
225
{ \
226
  GST_DEBUG_CATEGORY_INIT (task_debug, "task", 0, "Processing tasks"); \
227
}
228
229
951k
G_DEFINE_TYPE_WITH_CODE (GstTask, gst_task, GST_TYPE_OBJECT,
230
951k
    G_ADD_PRIVATE (GstTask) _do_init);
231
951k
232
951k
/* Called with pool_lock */
233
951k
static void
234
951k
ensure_klass_pool (GstTaskClass * klass)
235
951k
{
236
91.6k
  if (G_UNLIKELY (_global_task_pool == NULL)) {
237
2
    _global_task_pool = gst_task_pool_new ();
238
2
    gst_task_pool_prepare (_global_task_pool, NULL);
239
240
    /* Classes are never destroyed so this ref will never be dropped */
241
2
    GST_OBJECT_FLAG_SET (_global_task_pool, GST_OBJECT_FLAG_MAY_BE_LEAKED);
242
2
  }
243
91.6k
  klass->pool = _global_task_pool;
244
91.6k
}
245
246
static void
247
gst_task_class_init (GstTaskClass * klass)
248
9
{
249
9
  GObjectClass *gobject_class;
250
251
9
  gobject_class = (GObjectClass *) klass;
252
253
9
  gobject_class->finalize = gst_task_finalize;
254
9
}
255
256
static void
257
gst_task_init (GstTask * task)
258
91.6k
{
259
91.6k
  GstTaskClass *klass;
260
261
91.6k
  klass = GST_TASK_GET_CLASS (task);
262
263
91.6k
  task->priv = gst_task_get_instance_private (task);
264
91.6k
  task->priv->joining = FALSE;
265
91.6k
  task->running = FALSE;
266
91.6k
  task->thread = NULL;
267
91.6k
  task->lock = NULL;
268
91.6k
  g_cond_init (&task->cond);
269
91.6k
  SET_TASK_STATE (task, GST_TASK_STOPPED);
270
271
  /* use the default klass pool for this task, users can
272
   * override this later */
273
91.6k
  g_mutex_lock (&pool_lock);
274
91.6k
  ensure_klass_pool (klass);
275
91.6k
  task->priv->pool = gst_object_ref (klass->pool);
276
91.6k
  g_mutex_unlock (&pool_lock);
277
91.6k
}
278
279
static void
280
gst_task_finalize (GObject * object)
281
91.6k
{
282
91.6k
  GstTask *task = GST_TASK (object);
283
91.6k
  GstTaskPrivate *priv = task->priv;
284
285
91.6k
  GST_DEBUG ("task %p finalize", task);
286
287
91.6k
  if (priv->enter_notify)
288
0
    priv->enter_notify (priv->enter_user_data);
289
290
91.6k
  if (priv->leave_notify)
291
0
    priv->leave_notify (priv->leave_user_data);
292
293
91.6k
  if (task->notify)
294
91.6k
    task->notify (task->user_data);
295
296
91.6k
  gst_object_unref (priv->pool);
297
298
  /* task thread cannot be running here since it holds a ref
299
   * to the task so that the finalize could not have happened */
300
91.6k
  g_cond_clear (&task->cond);
301
302
91.6k
  G_OBJECT_CLASS (gst_task_parent_class)->finalize (object);
303
91.6k
}
304
305
/* should be called with the object LOCK */
306
static void
307
gst_task_configure_name (GstTask * task)
308
91.5k
{
309
91.5k
#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NAME)
310
91.5k
  const gchar *name;
311
91.5k
  gchar thread_name[17] = { 0, };
312
313
91.5k
  GST_OBJECT_LOCK (task);
314
91.5k
  name = GST_OBJECT_NAME (task);
315
316
  /* set the thread name to something easily identifiable */
317
91.5k
  if (!snprintf (thread_name, 17, "%s", GST_STR_NULL (name))) {
318
0
    GST_DEBUG_OBJECT (task, "Could not create thread name for '%s'", name);
319
91.5k
  } else {
320
91.5k
    GST_DEBUG_OBJECT (task, "Setting thread name to '%s'", thread_name);
321
91.5k
    if (prctl (PR_SET_NAME, (unsigned long int) thread_name, 0, 0, 0))
322
91.5k
      GST_DEBUG_OBJECT (task, "Failed to set thread name");
323
91.5k
  }
324
91.5k
  GST_OBJECT_UNLOCK (task);
325
#elif defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID)
326
  const gchar *name;
327
328
  GST_OBJECT_LOCK (task);
329
  name = GST_OBJECT_NAME (task);
330
331
  /* set the thread name to something easily identifiable */
332
  GST_DEBUG_OBJECT (task, "Setting thread name to '%s'", name);
333
  if (pthread_setname_np (name))
334
    GST_DEBUG_OBJECT (task, "Failed to set thread name");
335
336
  GST_OBJECT_UNLOCK (task);
337
#elif defined (_MSC_VER)
338
  const gchar *name;
339
  name = GST_OBJECT_NAME (task);
340
341
  /* set the thread name to something easily identifiable */
342
  GST_DEBUG_OBJECT (task, "Setting thread name to '%s'", name);
343
  gst_task_win32_set_thread_name (name);
344
#endif
345
91.5k
}
346
347
static void
348
gst_task_func (GstTask * task)
349
91.6k
{
350
91.6k
  GRecMutex *lock;
351
91.6k
  GThread *tself;
352
91.6k
  GstTaskPrivate *priv;
353
91.6k
  gboolean joining;
354
355
91.6k
  priv = task->priv;
356
357
91.6k
  tself = g_thread_self ();
358
359
91.6k
  GST_DEBUG ("Entering task %p, thread %p", task, tself);
360
361
  /* we have to grab the lock to get the mutex. We also
362
   * mark our state running so that nobody can mess with
363
   * the mutex. */
364
91.6k
  GST_OBJECT_LOCK (task);
365
91.6k
  if (GET_TASK_STATE (task) == GST_TASK_STOPPED)
366
64
    goto exit;
367
91.5k
  lock = GST_TASK_GET_LOCK (task);
368
91.5k
  if (G_UNLIKELY (lock == NULL))
369
0
    goto no_lock;
370
91.5k
  task->thread = tself;
371
91.5k
  GST_OBJECT_UNLOCK (task);
372
373
  /* fire the enter_func callback when we need to */
374
91.5k
  if (priv->enter_func)
375
91.5k
    priv->enter_func (task, tself, priv->enter_user_data);
376
377
  /* locking order is TASK_LOCK, LOCK */
378
91.5k
  g_rec_mutex_lock (lock);
379
  /* configure the thread name now */
380
91.5k
  gst_task_configure_name (task);
381
382
541k
  while (G_LIKELY (GET_TASK_STATE (task) != GST_TASK_STOPPED)) {
383
485k
    GST_OBJECT_LOCK (task);
384
521k
    while (G_UNLIKELY (GST_TASK_STATE (task) == GST_TASK_PAUSED)) {
385
35.9k
      g_rec_mutex_unlock (lock);
386
387
35.9k
      GST_TASK_SIGNAL (task);
388
35.9k
      GST_INFO_OBJECT (task, "Task going to paused");
389
35.9k
      GST_TASK_WAIT (task);
390
35.9k
      GST_INFO_OBJECT (task, "Task resume from paused");
391
35.9k
      GST_OBJECT_UNLOCK (task);
392
      /* locking order.. */
393
35.9k
      g_rec_mutex_lock (lock);
394
35.9k
      GST_OBJECT_LOCK (task);
395
35.9k
    }
396
397
485k
    if (G_UNLIKELY (GET_TASK_STATE (task) == GST_TASK_STOPPED)) {
398
35.9k
      GST_OBJECT_UNLOCK (task);
399
35.9k
      break;
400
449k
    } else {
401
449k
      GST_OBJECT_UNLOCK (task);
402
449k
    }
403
404
449k
    task->func (task->user_data);
405
449k
  }
406
407
91.5k
  g_rec_mutex_unlock (lock);
408
409
91.5k
  GST_OBJECT_LOCK (task);
410
91.5k
  task->thread = NULL;
411
412
91.6k
exit:
413
91.6k
  if (priv->leave_func) {
414
    /* fire the leave_func callback when we need to. We need to do this before
415
     * we signal the task and with the task lock released. */
416
91.6k
    GST_OBJECT_UNLOCK (task);
417
91.6k
    priv->leave_func (task, tself, priv->leave_user_data);
418
91.6k
    GST_OBJECT_LOCK (task);
419
91.6k
  }
420
  /* now we allow messing with the lock again by setting the running flag to
421
   * %FALSE. Together with the SIGNAL this is the sign for the _join() to
422
   * complete. */
423
91.6k
  task->running = FALSE;
424
91.6k
  GST_TASK_SIGNAL (task);
425
  /* Someone called join() while we're exiting, so they are holding
426
   * a reference and we can drop ours already. This ensures that finalize() is
427
   * called when the joiner's reference is dropped. */
428
91.6k
  joining = task->priv->joining;
429
91.6k
  if (joining)
430
69.7k
    gst_object_unref (task);
431
91.6k
  GST_DEBUG ("Exit task %p, thread %p", task, g_thread_self ());
432
91.6k
  GST_OBJECT_UNLOCK (task);
433
434
91.6k
  if (!joining)
435
21.8k
    gst_object_unref (task);
436
437
91.6k
  return;
438
439
0
no_lock:
440
0
  {
441
0
    g_warning ("starting task without a lock");
442
0
    goto exit;
443
91.5k
  }
444
91.5k
}
445
446
/**
447
 * gst_task_cleanup_all:
448
 *
449
 * Wait for all tasks to be stopped. This is mainly used internally
450
 * to ensure proper cleanup of internal data structures in test suites.
451
 *
452
 * MT safe.
453
 */
454
void
455
gst_task_cleanup_all (void)
456
0
{
457
0
  GstTaskClass *klass;
458
459
0
  if ((klass = g_type_class_peek (GST_TYPE_TASK))) {
460
0
    if (klass->pool) {
461
0
      g_mutex_lock (&pool_lock);
462
0
      gst_task_pool_cleanup (klass->pool);
463
0
      gst_object_unref (klass->pool);
464
0
      klass->pool = NULL;
465
0
      _global_task_pool = NULL;
466
0
      g_mutex_unlock (&pool_lock);
467
0
    }
468
0
  }
469
470
  /* Clean up a GThreadPool associated with gst_*_async_call() if any */
471
0
  _priv_gst_thread_pool_cleanup ();
472
0
}
473
474
/**
475
 * gst_task_new:
476
 * @func: The #GstTaskFunction to use
477
 * @user_data: User data to pass to @func
478
 * @notify: the function to call when @user_data is no longer needed.
479
 *
480
 * Create a new Task that will repeatedly call the provided @func
481
 * with @user_data as a parameter. Typically the task will run in
482
 * a new thread.
483
 *
484
 * The function cannot be changed after the task has been created. You
485
 * must create a new #GstTask to change the function.
486
 *
487
 * This function will not yet create and start a thread. Use gst_task_start() or
488
 * gst_task_pause() to create and start the GThread.
489
 *
490
 * Before the task can be used, a #GRecMutex must be configured using the
491
 * gst_task_set_lock() function. This lock will always be acquired while
492
 * @func is called.
493
 *
494
 * Returns: (transfer full): A new #GstTask.
495
 *
496
 * MT safe.
497
 */
498
GstTask *
499
gst_task_new (GstTaskFunction func, gpointer user_data, GDestroyNotify notify)
500
91.6k
{
501
91.6k
  GstTask *task;
502
503
91.6k
  g_return_val_if_fail (func != NULL, NULL);
504
505
91.6k
  task = g_object_new (GST_TYPE_TASK, NULL);
506
91.6k
  task->func = func;
507
91.6k
  task->user_data = user_data;
508
91.6k
  task->notify = notify;
509
510
91.6k
  GST_DEBUG ("Created task %p", task);
511
512
  /* clear floating flag */
513
91.6k
  gst_object_ref_sink (task);
514
515
91.6k
  return task;
516
91.6k
}
517
518
/**
519
 * gst_task_set_lock:
520
 * @task: The #GstTask to use
521
 * @mutex: The #GRecMutex to use
522
 *
523
 * Set the mutex used by the task. The mutex will be acquired before
524
 * calling the #GstTaskFunction.
525
 *
526
 * This function has to be called before calling gst_task_pause() or
527
 * gst_task_start().
528
 *
529
 * MT safe.
530
 */
531
void
532
gst_task_set_lock (GstTask * task, GRecMutex * mutex)
533
91.6k
{
534
91.6k
  g_return_if_fail (GST_IS_TASK (task));
535
536
91.6k
  GST_OBJECT_LOCK (task);
537
91.6k
  if (G_UNLIKELY (task->running))
538
0
    goto is_running;
539
91.6k
  GST_INFO ("setting stream lock %p on task %p", mutex, task);
540
91.6k
  GST_TASK_GET_LOCK (task) = mutex;
541
91.6k
  GST_OBJECT_UNLOCK (task);
542
543
91.6k
  return;
544
545
  /* ERRORS */
546
0
is_running:
547
0
  {
548
0
    GST_OBJECT_UNLOCK (task);
549
0
    g_warning ("cannot call set_lock on a running task");
550
0
  }
551
0
}
552
553
/**
554
 * gst_task_get_pool:
555
 * @task: a #GstTask
556
 *
557
 * Get the #GstTaskPool that this task will use for its streaming
558
 * threads.
559
 *
560
 * MT safe.
561
 *
562
 * Returns: (transfer full): the #GstTaskPool used by @task. gst_object_unref()
563
 * after usage.
564
 */
565
GstTaskPool *
566
gst_task_get_pool (GstTask * task)
567
0
{
568
0
  GstTaskPool *result;
569
0
  GstTaskPrivate *priv;
570
571
0
  g_return_val_if_fail (GST_IS_TASK (task), NULL);
572
573
0
  priv = task->priv;
574
575
0
  GST_OBJECT_LOCK (task);
576
0
  result = gst_object_ref (priv->pool);
577
0
  GST_OBJECT_UNLOCK (task);
578
579
0
  return result;
580
0
}
581
582
/**
583
 * gst_task_set_pool:
584
 * @task: a #GstTask
585
 * @pool: (transfer none): a #GstTaskPool
586
 *
587
 * Set @pool as the new GstTaskPool for @task. Any new streaming threads that
588
 * will be created by @task will now use @pool.
589
 *
590
 * MT safe.
591
 */
592
void
593
gst_task_set_pool (GstTask * task, GstTaskPool * pool)
594
0
{
595
0
  GstTaskPool *old;
596
0
  GstTaskPrivate *priv;
597
598
0
  g_return_if_fail (GST_IS_TASK (task));
599
0
  g_return_if_fail (GST_IS_TASK_POOL (pool));
600
601
0
  priv = task->priv;
602
603
0
  GST_OBJECT_LOCK (task);
604
0
  if (priv->pool != pool) {
605
0
    old = priv->pool;
606
0
    priv->pool = gst_object_ref (pool);
607
0
  } else
608
0
    old = NULL;
609
0
  GST_OBJECT_UNLOCK (task);
610
611
0
  if (old)
612
0
    gst_object_unref (old);
613
0
}
614
615
/**
616
 * gst_task_set_enter_callback:
617
 * @task: The #GstTask to use
618
 * @enter_func: (in): a #GstTaskThreadFunc
619
 * @user_data: user data passed to @enter_func
620
 * @notify: called when @user_data is no longer referenced
621
 *
622
 * Call @enter_func when the task function of @task is entered. @user_data will
623
 * be passed to @enter_func and @notify will be called when @user_data is no
624
 * longer referenced.
625
 */
626
void
627
gst_task_set_enter_callback (GstTask * task, GstTaskThreadFunc enter_func,
628
    gpointer user_data, GDestroyNotify notify)
629
91.6k
{
630
91.6k
  GDestroyNotify old_notify;
631
632
91.6k
  g_return_if_fail (task != NULL);
633
91.6k
  g_return_if_fail (GST_IS_TASK (task));
634
635
91.6k
  GST_OBJECT_LOCK (task);
636
91.6k
  if ((old_notify = task->priv->enter_notify)) {
637
0
    gpointer old_data = task->priv->enter_user_data;
638
639
0
    task->priv->enter_user_data = NULL;
640
0
    task->priv->enter_notify = NULL;
641
0
    GST_OBJECT_UNLOCK (task);
642
643
0
    old_notify (old_data);
644
645
0
    GST_OBJECT_LOCK (task);
646
0
  }
647
91.6k
  task->priv->enter_func = enter_func;
648
91.6k
  task->priv->enter_user_data = user_data;
649
91.6k
  task->priv->enter_notify = notify;
650
91.6k
  GST_OBJECT_UNLOCK (task);
651
91.6k
}
652
653
/**
654
 * gst_task_set_leave_callback:
655
 * @task: The #GstTask to use
656
 * @leave_func: (in): a #GstTaskThreadFunc
657
 * @user_data: user data passed to @leave_func
658
 * @notify: called when @user_data is no longer referenced
659
 *
660
 * Call @leave_func when the task function of @task is left. @user_data will
661
 * be passed to @leave_func and @notify will be called when @user_data is no
662
 * longer referenced.
663
 */
664
void
665
gst_task_set_leave_callback (GstTask * task, GstTaskThreadFunc leave_func,
666
    gpointer user_data, GDestroyNotify notify)
667
91.6k
{
668
91.6k
  GDestroyNotify old_notify;
669
670
91.6k
  g_return_if_fail (task != NULL);
671
91.6k
  g_return_if_fail (GST_IS_TASK (task));
672
673
91.6k
  GST_OBJECT_LOCK (task);
674
91.6k
  if ((old_notify = task->priv->leave_notify)) {
675
0
    gpointer old_data = task->priv->leave_user_data;
676
677
0
    task->priv->leave_user_data = NULL;
678
0
    task->priv->leave_notify = NULL;
679
0
    GST_OBJECT_UNLOCK (task);
680
681
0
    old_notify (old_data);
682
683
0
    GST_OBJECT_LOCK (task);
684
0
  }
685
91.6k
  task->priv->leave_func = leave_func;
686
91.6k
  task->priv->leave_user_data = user_data;
687
91.6k
  task->priv->leave_notify = notify;
688
91.6k
  GST_OBJECT_UNLOCK (task);
689
91.6k
}
690
691
/**
692
 * gst_task_get_state:
693
 * @task: The #GstTask to query
694
 *
695
 * Get the current state of the task.
696
 *
697
 * Returns: The #GstTaskState of the task
698
 *
699
 * MT safe.
700
 */
701
GstTaskState
702
gst_task_get_state (GstTask * task)
703
0
{
704
0
  GstTaskState result;
705
706
0
  g_return_val_if_fail (GST_IS_TASK (task), GST_TASK_STOPPED);
707
708
0
  result = GET_TASK_STATE (task);
709
710
0
  return result;
711
0
}
712
713
/* make sure the task is running and start a thread if it's not.
714
 * This function must be called with the task LOCK. */
715
static gboolean
716
start_task (GstTask * task)
717
91.6k
{
718
91.6k
  gboolean res = TRUE;
719
91.6k
  GError *error = NULL;
720
91.6k
  GstTaskPrivate *priv;
721
722
91.6k
  priv = task->priv;
723
724
  /* new task, We ref before so that it remains alive while
725
   * the thread is running. */
726
91.6k
  gst_object_ref (task);
727
  /* mark task as running so that a join will wait until we schedule
728
   * and exit the task function. */
729
91.6k
  task->running = TRUE;
730
91.6k
  task->priv->joining = FALSE;
731
732
  /* push on the thread pool, we remember the original pool because the user
733
   * could change it later on and then we join to the wrong pool. */
734
91.6k
  priv->pool_id = gst_object_ref (priv->pool);
735
91.6k
  priv->id =
736
91.6k
      gst_task_pool_push (priv->pool_id, (GstTaskPoolFunction) gst_task_func,
737
91.6k
      task, &error);
738
739
91.6k
  if (error != NULL) {
740
0
    g_warning ("failed to create thread: %s", error->message);
741
0
    g_error_free (error);
742
0
    res = FALSE;
743
0
  }
744
91.6k
  return res;
745
91.6k
}
746
747
static inline gboolean
748
gst_task_set_state_unlocked (GstTask * task, GstTaskState state)
749
219k
{
750
219k
  GstTaskState old;
751
219k
  gboolean res = TRUE;
752
753
219k
  GST_DEBUG_OBJECT (task, "Changing task %p to state %s", task,
754
219k
      task_state_to_string (state));
755
756
219k
  if (state != GST_TASK_STOPPED)
757
127k
    if (G_UNLIKELY (GST_TASK_GET_LOCK (task) == NULL))
758
0
      goto no_lock;
759
760
  /* if the state changed, do our thing */
761
219k
  old = GET_TASK_STATE (task);
762
219k
  if (old != state) {
763
219k
    SET_TASK_STATE (task, state);
764
219k
    switch (old) {
765
91.6k
      case GST_TASK_STOPPED:
766
        /* If the task already has a thread scheduled we don't have to do
767
         * anything. */
768
91.6k
        if (G_UNLIKELY (!task->running))
769
91.6k
          res = start_task (task);
770
91.6k
        break;
771
35.9k
      case GST_TASK_PAUSED:
772
        /* when we are paused, signal to go to the new state */
773
35.9k
        GST_TASK_SIGNAL (task);
774
35.9k
        break;
775
91.6k
      case GST_TASK_STARTED:
776
        /* if we were started, we'll go to the new state after the next
777
         * iteration. */
778
91.6k
        break;
779
219k
    }
780
219k
  }
781
782
219k
  return res;
783
784
  /* ERRORS */
785
0
no_lock:
786
0
  {
787
0
    GST_WARNING_OBJECT (task, "state %s set on task without a lock",
788
0
        task_state_to_string (state));
789
0
    g_warning ("task without a lock can't be set to state %s",
790
0
        task_state_to_string (state));
791
0
    return FALSE;
792
219k
  }
793
219k
}
794
795
796
/**
797
 * gst_task_set_state:
798
 * @task: a #GstTask
799
 * @state: the new task state
800
 *
801
 * Sets the state of @task to @state.
802
 *
803
 * The @task must have a lock associated with it using
804
 * gst_task_set_lock() when going to GST_TASK_STARTED or GST_TASK_PAUSED or
805
 * this function will return %FALSE.
806
 *
807
 * MT safe.
808
 *
809
 * Returns: %TRUE if the state could be changed.
810
 */
811
gboolean
812
gst_task_set_state (GstTask * task, GstTaskState state)
813
219k
{
814
219k
  gboolean res = TRUE;
815
816
219k
  g_return_val_if_fail (GST_IS_TASK (task), FALSE);
817
818
219k
  GST_OBJECT_LOCK (task);
819
219k
  res = gst_task_set_state_unlocked (task, state);
820
219k
  GST_OBJECT_UNLOCK (task);
821
822
219k
  return res;
823
219k
}
824
825
/**
826
 * gst_task_start:
827
 * @task: The #GstTask to start
828
 *
829
 * Starts @task. The @task must have a lock associated with it using
830
 * gst_task_set_lock() or this function will return %FALSE.
831
 *
832
 * Returns: %TRUE if the task could be started.
833
 *
834
 * MT safe.
835
 */
836
gboolean
837
gst_task_start (GstTask * task)
838
0
{
839
0
  return gst_task_set_state (task, GST_TASK_STARTED);
840
0
}
841
842
/**
843
 * gst_task_stop:
844
 * @task: The #GstTask to stop
845
 *
846
 * Stops @task. This method merely schedules the task to stop and
847
 * will not wait for the task to have completely stopped. Use
848
 * gst_task_join() to stop and wait for completion.
849
 *
850
 * Returns: %TRUE if the task could be stopped.
851
 *
852
 * MT safe.
853
 */
854
gboolean
855
gst_task_stop (GstTask * task)
856
20
{
857
20
  return gst_task_set_state (task, GST_TASK_STOPPED);
858
20
}
859
860
/**
861
 * gst_task_pause:
862
 * @task: The #GstTask to pause
863
 *
864
 * Pauses @task. This method can also be called on a task in the
865
 * stopped state, in which case a thread will be started and will remain
866
 * in the paused state. This function does not wait for the task to complete
867
 * the paused state.
868
 *
869
 * Returns: %TRUE if the task could be paused.
870
 *
871
 * MT safe.
872
 */
873
gboolean
874
gst_task_pause (GstTask * task)
875
0
{
876
0
  return gst_task_set_state (task, GST_TASK_PAUSED);
877
0
}
878
879
/**
880
 * gst_task_resume:
881
 * @task: The #GstTask to resume
882
 *
883
 * Resume @task in case it was paused. If the task was stopped, it will
884
 * remain in that state and this function will return %FALSE.
885
 *
886
 * Returns: %TRUE if the task could be resumed.
887
 *
888
 * MT safe.
889
 * Since: 1.18
890
 */
891
gboolean
892
gst_task_resume (GstTask * task)
893
0
{
894
0
  gboolean res = FALSE;
895
0
  g_return_val_if_fail (GST_IS_TASK (task), FALSE);
896
897
0
  GST_OBJECT_LOCK (task);
898
0
  if (GET_TASK_STATE (task) != GST_TASK_STOPPED)
899
0
    res = gst_task_set_state_unlocked (task, GST_TASK_STARTED);
900
0
  GST_OBJECT_UNLOCK (task);
901
902
0
  return res;
903
0
}
904
905
/**
906
 * gst_task_join:
907
 * @task: The #GstTask to join
908
 *
909
 * Joins @task. After this call, it is safe to unref the task
910
 * and clean up the lock set with gst_task_set_lock().
911
 *
912
 * The task will automatically be stopped with this call.
913
 *
914
 * This function cannot be called from within a task function as this
915
 * would cause a deadlock. The function will detect this and print a
916
 * g_warning.
917
 *
918
 * Returns: %TRUE if the task could be joined.
919
 *
920
 * MT safe.
921
 */
922
gboolean
923
gst_task_join (GstTask * task)
924
91.6k
{
925
91.6k
  GThread *tself;
926
91.6k
  GstTaskPrivate *priv;
927
91.6k
  gpointer id;
928
91.6k
  GstTaskPool *pool = NULL;
929
930
91.6k
  g_return_val_if_fail (GST_IS_TASK (task), FALSE);
931
932
91.6k
  priv = task->priv;
933
934
91.6k
  tself = g_thread_self ();
935
936
91.6k
  GST_DEBUG_OBJECT (task, "Joining task %p, thread %p", task, tself);
937
938
  /* we don't use a real thread join here because we are using
939
   * thread pools */
940
91.6k
  GST_OBJECT_LOCK (task);
941
91.6k
  if (G_UNLIKELY (tself == task->thread))
942
0
    goto joining_self;
943
91.6k
  priv->joining = TRUE;
944
91.6k
  SET_TASK_STATE (task, GST_TASK_STOPPED);
945
  /* signal the state change for when it was blocked in PAUSED. */
946
91.6k
  GST_TASK_SIGNAL (task);
947
  /* we set the running flag when pushing the task on the thread pool.
948
   * This means that the task function might not be called when we try
949
   * to join it here. */
950
161k
  while (G_LIKELY (task->running))
951
69.7k
    GST_TASK_WAIT (task);
952
  /* clean the thread */
953
91.6k
  task->thread = NULL;
954
  /* get the id and pool to join */
955
91.6k
  pool = priv->pool_id;
956
91.6k
  id = priv->id;
957
91.6k
  priv->pool_id = NULL;
958
91.6k
  priv->id = NULL;
959
91.6k
  priv->joining = FALSE;
960
91.6k
  GST_OBJECT_UNLOCK (task);
961
962
91.6k
  if (pool) {
963
91.6k
    if (id)
964
0
      gst_task_pool_join (pool, id);
965
91.6k
    gst_object_unref (pool);
966
91.6k
  }
967
968
91.6k
  GST_DEBUG_OBJECT (task, "Joined task %p", task);
969
970
91.6k
  return TRUE;
971
972
  /* ERRORS */
973
0
joining_self:
974
0
  {
975
0
    GST_WARNING_OBJECT (task, "trying to join task from its thread");
976
0
    GST_OBJECT_UNLOCK (task);
977
0
    g_warning ("\nTrying to join task %p from its thread would deadlock.\n"
978
0
        "You cannot change the state of an element from its streaming\n"
979
0
        "thread. Use g_idle_add() or post a GstMessage on the bus to\n"
980
0
        "schedule the state change from the main thread.\n", task);
981
0
    return FALSE;
982
91.6k
  }
983
91.6k
}