Blame glib/tests/mainloop.c

Packit ae235b
/* Unit tests for GMainLoop
Packit ae235b
 * Copyright (C) 2011 Red Hat, Inc
Packit ae235b
 * Author: Matthias Clasen
Packit ae235b
 *
Packit ae235b
 * This work is provided "as is"; redistribution and modification
Packit ae235b
 * in whole or in part, in any medium, physical or electronic is
Packit ae235b
 * permitted without restriction.
Packit ae235b
 *
Packit ae235b
 * This work is distributed in the hope that it will be useful,
Packit ae235b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit ae235b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Packit ae235b
 *
Packit ae235b
 * In no event shall the authors or contributors be liable for any
Packit ae235b
 * direct, indirect, incidental, special, exemplary, or consequential
Packit ae235b
 * damages (including, but not limited to, procurement of substitute
Packit ae235b
 * goods or services; loss of use, data, or profits; or business
Packit ae235b
 * interruption) however caused and on any theory of liability, whether
Packit ae235b
 * in contract, strict liability, or tort (including negligence or
Packit ae235b
 * otherwise) arising in any way out of the use of this software, even
Packit ae235b
 * if advised of the possibility of such damage.
Packit ae235b
 */
Packit ae235b
Packit ae235b
#include <glib.h>
Packit ae235b
#include "glib-private.h"
Packit ae235b
#include <stdio.h>
Packit ae235b
#include <string.h>
Packit ae235b
Packit ae235b
static gboolean cb (gpointer data)
Packit ae235b
{
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean prepare (GSource *source, gint *time)
Packit ae235b
{
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
static gboolean check (GSource *source)
Packit ae235b
{
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
static gboolean dispatch (GSource *source, GSourceFunc cb, gpointer date)
Packit ae235b
{
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
GSourceFuncs funcs = {
Packit ae235b
  prepare,
Packit ae235b
  check,
Packit ae235b
  dispatch,
Packit ae235b
  NULL
Packit ae235b
};
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_maincontext_basic (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GSource *source;
Packit ae235b
  guint id;
Packit ae235b
  gpointer data = &funcs;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
Packit ae235b
  g_assert (!g_main_context_pending (ctx));
Packit ae235b
  g_assert (!g_main_context_iteration (ctx, FALSE));
Packit ae235b
Packit ae235b
  source = g_source_new (&funcs, sizeof (GSource));
Packit ae235b
  g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_DEFAULT);
Packit ae235b
  g_assert (!g_source_is_destroyed (source));
Packit ae235b
Packit ae235b
  g_assert (!g_source_get_can_recurse (source));
Packit ae235b
  g_assert (g_source_get_name (source) == NULL);
Packit ae235b
Packit ae235b
  g_source_set_can_recurse (source, TRUE);
Packit ae235b
  g_source_set_name (source, "d");
Packit ae235b
Packit ae235b
  g_assert (g_source_get_can_recurse (source));
Packit ae235b
  g_assert_cmpstr (g_source_get_name (source), ==, "d");
Packit ae235b
Packit ae235b
  g_assert (g_main_context_find_source_by_user_data (ctx, NULL) == NULL);
Packit ae235b
  g_assert (g_main_context_find_source_by_funcs_user_data (ctx, &funcs, NULL) == NULL);
Packit ae235b
Packit ae235b
  id = g_source_attach (source, ctx);
Packit ae235b
  g_assert_cmpint (g_source_get_id (source), ==, id);
Packit ae235b
  g_assert (g_main_context_find_source_by_id (ctx, id) == source);
Packit ae235b
Packit ae235b
  g_source_set_priority (source, G_PRIORITY_HIGH);
Packit ae235b
  g_assert_cmpint (g_source_get_priority (source), ==, G_PRIORITY_HIGH);
Packit ae235b
Packit ae235b
  g_source_destroy (source);
Packit ae235b
  g_assert (g_source_get_context (source) == ctx);
Packit ae235b
  g_assert (g_main_context_find_source_by_id (ctx, id) == NULL);
Packit ae235b
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
Packit ae235b
  if (g_test_undefined ())
Packit ae235b
    {
Packit ae235b
      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
Packit ae235b
                             "*assertion*source->context != NULL*failed*");
Packit ae235b
      g_assert (g_source_get_context (source) == NULL);
Packit ae235b
      g_test_assert_expected_messages ();
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  ctx = g_main_context_default ();
Packit ae235b
  source = g_source_new (&funcs, sizeof (GSource));
Packit ae235b
  g_source_set_funcs (source, &funcs);
Packit ae235b
  g_source_set_callback (source, cb, data, NULL);
Packit ae235b
  id = g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
  g_source_set_name_by_id (id, "e");
Packit ae235b
  g_assert_cmpstr (g_source_get_name (source), ==, "e");
Packit ae235b
  g_assert (g_source_get_context (source) == ctx);
Packit ae235b
  g_assert (g_source_remove_by_funcs_user_data (&funcs, data));
Packit ae235b
Packit ae235b
  source = g_source_new (&funcs, sizeof (GSource));
Packit ae235b
  g_source_set_funcs (source, &funcs);
Packit ae235b
  g_source_set_callback (source, cb, data, NULL);
Packit ae235b
  id = g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
  g_assert (g_source_remove_by_user_data (data));
Packit ae235b
  g_assert (!g_source_remove_by_user_data ((gpointer)0x1234));
Packit ae235b
Packit ae235b
  g_idle_add (cb, data);
Packit ae235b
  g_assert (g_idle_remove_by_data (data));
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_mainloop_basic (void)
Packit ae235b
{
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GMainContext *ctx;
Packit ae235b
Packit ae235b
  loop = g_main_loop_new (NULL, FALSE);
Packit ae235b
Packit ae235b
  g_assert (!g_main_loop_is_running (loop));
Packit ae235b
Packit ae235b
  g_main_loop_ref (loop);
Packit ae235b
Packit ae235b
  ctx = g_main_loop_get_context (loop);
Packit ae235b
  g_assert (ctx == g_main_context_default ());
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
Packit ae235b
  g_assert_cmpint (g_main_depth (), ==, 0);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gint a;
Packit ae235b
static gint b;
Packit ae235b
static gint c;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
count_calls (gpointer data)
Packit ae235b
{
Packit ae235b
  gint *i = data;
Packit ae235b
Packit ae235b
  (*i)++;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_timeouts (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  a = b = c = 0;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  loop = g_main_loop_new (ctx, FALSE);
Packit ae235b
Packit ae235b
  source = g_timeout_source_new (100);
Packit ae235b
  g_source_set_callback (source, count_calls, &a, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  source = g_timeout_source_new (250);
Packit ae235b
  g_source_set_callback (source, count_calls, &b, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  source = g_timeout_source_new (330);
Packit ae235b
  g_source_set_callback (source, count_calls, &c, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  source = g_timeout_source_new (1050);
Packit ae235b
  g_source_set_callback (source, (GSourceFunc)g_main_loop_quit, loop, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  /* We may be delayed for an arbitrary amount of time - for example,
Packit ae235b
   * it's possible for all timeouts to fire exactly once.
Packit ae235b
   */
Packit ae235b
  g_assert_cmpint (a, >, 0);
Packit ae235b
  g_assert_cmpint (a, >=, b);
Packit ae235b
  g_assert_cmpint (b, >=, c);
Packit ae235b
Packit ae235b
  g_assert_cmpint (a, <=, 10);
Packit ae235b
  g_assert_cmpint (b, <=, 4);
Packit ae235b
  g_assert_cmpint (c, <=, 3);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_priorities (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GSource *sourcea;
Packit ae235b
  GSource *sourceb;
Packit ae235b
Packit ae235b
  a = b = c = 0;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
Packit ae235b
  sourcea = g_idle_source_new ();
Packit ae235b
  g_source_set_callback (sourcea, count_calls, &a, NULL);
Packit ae235b
  g_source_set_priority (sourcea, 1);
Packit ae235b
  g_source_attach (sourcea, ctx);
Packit ae235b
  g_source_unref (sourcea);
Packit ae235b
Packit ae235b
  sourceb = g_idle_source_new ();
Packit ae235b
  g_source_set_callback (sourceb, count_calls, &b, NULL);
Packit ae235b
  g_source_set_priority (sourceb, 0);
Packit ae235b
  g_source_attach (sourceb, ctx);
Packit ae235b
  g_source_unref (sourceb);
Packit ae235b
Packit ae235b
  g_assert (g_main_context_pending (ctx));
Packit ae235b
  g_assert (g_main_context_iteration (ctx, FALSE));
Packit ae235b
  g_assert_cmpint (a, ==, 0);
Packit ae235b
  g_assert_cmpint (b, ==, 1);
Packit ae235b
Packit ae235b
  g_assert (g_main_context_iteration (ctx, FALSE));
Packit ae235b
  g_assert_cmpint (a, ==, 0);
Packit ae235b
  g_assert_cmpint (b, ==, 2);
Packit ae235b
Packit ae235b
  g_source_destroy (sourceb);
Packit ae235b
Packit ae235b
  g_assert (g_main_context_iteration (ctx, FALSE));
Packit ae235b
  g_assert_cmpint (a, ==, 1);
Packit ae235b
  g_assert_cmpint (b, ==, 2);
Packit ae235b
Packit ae235b
  g_assert (g_main_context_pending (ctx));
Packit ae235b
  g_source_destroy (sourcea);
Packit ae235b
  g_assert (!g_main_context_pending (ctx));
Packit ae235b
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
quit_loop (gpointer data)
Packit ae235b
{
Packit ae235b
  GMainLoop *loop = data;
Packit ae235b
Packit ae235b
  g_main_loop_quit (loop);
Packit ae235b
Packit ae235b
  return G_SOURCE_REMOVE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gint count;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
func (gpointer data)
Packit ae235b
{
Packit ae235b
  if (data != NULL)
Packit ae235b
    g_assert (data == g_thread_self ());
Packit ae235b
Packit ae235b
  count++;
Packit ae235b
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
call_func (gpointer data)
Packit ae235b
{
Packit ae235b
  func (g_thread_self ());
Packit ae235b
Packit ae235b
  return G_SOURCE_REMOVE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GMutex mutex;
Packit ae235b
static GCond cond;
Packit ae235b
static gboolean thread_ready;
Packit ae235b
Packit ae235b
static gpointer
Packit ae235b
thread_func (gpointer data)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx = data;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  g_main_context_push_thread_default (ctx);
Packit ae235b
  loop = g_main_loop_new (ctx, FALSE);
Packit ae235b
Packit ae235b
  g_mutex_lock (&mutex);
Packit ae235b
  thread_ready = TRUE;
Packit ae235b
  g_cond_signal (&cond;;
Packit ae235b
  g_mutex_unlock (&mutex);
Packit ae235b
Packit ae235b
  source = g_timeout_source_new (500);
Packit ae235b
  g_source_set_callback (source, quit_loop, loop, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  g_main_context_pop_thread_default (ctx);
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_invoke (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GThread *thread;
Packit ae235b
Packit ae235b
  count = 0;
Packit ae235b
Packit ae235b
  /* this one gets invoked directly */
Packit ae235b
  g_main_context_invoke (NULL, func, g_thread_self ());
Packit ae235b
  g_assert_cmpint (count, ==, 1);
Packit ae235b
Packit ae235b
  /* invoking out of an idle works too */
Packit ae235b
  g_idle_add (call_func, NULL);
Packit ae235b
  g_main_context_iteration (g_main_context_default (), FALSE);
Packit ae235b
  g_assert_cmpint (count, ==, 2);
Packit ae235b
Packit ae235b
  /* test thread-default forcing the invocation to go
Packit ae235b
   * to another thread
Packit ae235b
   */
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  thread = g_thread_new ("worker", thread_func, ctx);
Packit ae235b
Packit ae235b
  g_mutex_lock (&mutex);
Packit ae235b
  while (!thread_ready)
Packit ae235b
    g_cond_wait (&cond, &mutex);
Packit ae235b
  g_mutex_unlock (&mutex);
Packit ae235b
Packit ae235b
  g_main_context_invoke (ctx, func, thread);
Packit ae235b
Packit ae235b
  g_thread_join (thread);
Packit ae235b
  g_assert_cmpint (count, ==, 3);
Packit ae235b
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
/* We can't use timeout sources here because on slow or heavily-loaded
Packit ae235b
 * machines, the test program might not get enough cycles to hit the
Packit ae235b
 * timeouts at the expected times. So instead we define a source that
Packit ae235b
 * is based on the number of GMainContext iterations.
Packit ae235b
 */
Packit ae235b
Packit ae235b
static gint counter;
Packit ae235b
static gint64 last_counter_update;
Packit ae235b
Packit ae235b
typedef struct {
Packit ae235b
  GSource source;
Packit ae235b
  gint    interval;
Packit ae235b
  gint    timeout;
Packit ae235b
} CounterSource;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
counter_source_prepare (GSource *source,
Packit ae235b
                        gint    *timeout)
Packit ae235b
{
Packit ae235b
  CounterSource *csource = (CounterSource *)source;
Packit ae235b
  gint64 now;
Packit ae235b
Packit ae235b
  now = g_source_get_time (source);
Packit ae235b
  if (now != last_counter_update)
Packit ae235b
    {
Packit ae235b
      last_counter_update = now;
Packit ae235b
      counter++;
Packit ae235b
    }
Packit ae235b
Packit ae235b
  *timeout = 1;
Packit ae235b
  return counter >= csource->timeout;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
counter_source_dispatch (GSource    *source,
Packit ae235b
                         GSourceFunc callback,
Packit ae235b
                         gpointer    user_data)
Packit ae235b
{
Packit ae235b
  CounterSource *csource = (CounterSource *) source;
Packit ae235b
  gboolean again;
Packit ae235b
Packit ae235b
  again = callback (user_data);
Packit ae235b
Packit ae235b
  if (again)
Packit ae235b
    csource->timeout = counter + csource->interval;
Packit ae235b
Packit ae235b
  return again;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GSourceFuncs counter_source_funcs = {
Packit ae235b
  counter_source_prepare,
Packit ae235b
  NULL,
Packit ae235b
  counter_source_dispatch,
Packit ae235b
  NULL,
Packit ae235b
};
Packit ae235b
Packit ae235b
static GSource *
Packit ae235b
counter_source_new (gint interval)
Packit ae235b
{
Packit ae235b
  GSource *source = g_source_new (&counter_source_funcs, sizeof (CounterSource));
Packit ae235b
  CounterSource *csource = (CounterSource *) source;
Packit ae235b
Packit ae235b
  csource->interval = interval;
Packit ae235b
  csource->timeout = counter + interval;
Packit ae235b
Packit ae235b
  return source;
Packit ae235b
}
Packit ae235b
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
run_inner_loop (gpointer user_data)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx = user_data;
Packit ae235b
  GMainLoop *inner;
Packit ae235b
  GSource *timeout;
Packit ae235b
Packit ae235b
  a++;
Packit ae235b
Packit ae235b
  inner = g_main_loop_new (ctx, FALSE);
Packit ae235b
  timeout = counter_source_new (100);
Packit ae235b
  g_source_set_callback (timeout, quit_loop, inner, NULL);
Packit ae235b
  g_source_attach (timeout, ctx);
Packit ae235b
  g_source_unref (timeout);
Packit ae235b
Packit ae235b
  g_main_loop_run (inner);
Packit ae235b
  g_main_loop_unref (inner);
Packit ae235b
Packit ae235b
  return G_SOURCE_CONTINUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_child_sources (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *parent, *child_b, *child_c, *end;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  loop = g_main_loop_new (ctx, FALSE);
Packit ae235b
Packit ae235b
  a = b = c = 0;
Packit ae235b
Packit ae235b
  parent = counter_source_new (2000);
Packit ae235b
  g_source_set_callback (parent, run_inner_loop, ctx, NULL);
Packit ae235b
  g_source_set_priority (parent, G_PRIORITY_LOW);
Packit ae235b
  g_source_attach (parent, ctx);
Packit ae235b
Packit ae235b
  child_b = counter_source_new (250);
Packit ae235b
  g_source_set_callback (child_b, count_calls, &b, NULL);
Packit ae235b
  g_source_add_child_source (parent, child_b);
Packit ae235b
Packit ae235b
  child_c = counter_source_new (330);
Packit ae235b
  g_source_set_callback (child_c, count_calls, &c, NULL);
Packit ae235b
  g_source_set_priority (child_c, G_PRIORITY_HIGH);
Packit ae235b
  g_source_add_child_source (parent, child_c);
Packit ae235b
Packit ae235b
  /* Child sources always have the priority of the parent */
Packit ae235b
  g_assert_cmpint (g_source_get_priority (parent), ==, G_PRIORITY_LOW);
Packit ae235b
  g_assert_cmpint (g_source_get_priority (child_b), ==, G_PRIORITY_LOW);
Packit ae235b
  g_assert_cmpint (g_source_get_priority (child_c), ==, G_PRIORITY_LOW);
Packit ae235b
  g_source_set_priority (parent, G_PRIORITY_DEFAULT);
Packit ae235b
  g_assert_cmpint (g_source_get_priority (parent), ==, G_PRIORITY_DEFAULT);
Packit ae235b
  g_assert_cmpint (g_source_get_priority (child_b), ==, G_PRIORITY_DEFAULT);
Packit ae235b
  g_assert_cmpint (g_source_get_priority (child_c), ==, G_PRIORITY_DEFAULT);
Packit ae235b
Packit ae235b
  end = counter_source_new (1050);
Packit ae235b
  g_source_set_callback (end, quit_loop, loop, NULL);
Packit ae235b
  g_source_attach (end, ctx);
Packit ae235b
  g_source_unref (end);
Packit ae235b
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  /* The parent source's own timeout will never trigger, so "a" will
Packit ae235b
   * only get incremented when "b" or "c" does. And when timeouts get
Packit ae235b
   * blocked, they still wait the full interval next time rather than
Packit ae235b
   * "catching up". So the timing is:
Packit ae235b
   *
Packit ae235b
   *  250 - b++ -> a++, run_inner_loop
Packit ae235b
   *  330 - (c is blocked)
Packit ae235b
   *  350 - inner_loop ends
Packit ae235b
   *  350 - c++ belatedly -> a++, run_inner_loop
Packit ae235b
   *  450 - inner loop ends
Packit ae235b
   *  500 - b++ -> a++, run_inner_loop
Packit ae235b
   *  600 - inner_loop ends
Packit ae235b
   *  680 - c++ -> a++, run_inner_loop
Packit ae235b
   *  750 - (b is blocked)
Packit ae235b
   *  780 - inner loop ends
Packit ae235b
   *  780 - b++ belatedly -> a++, run_inner_loop
Packit ae235b
   *  880 - inner loop ends
Packit ae235b
   * 1010 - c++ -> a++, run_inner_loop
Packit ae235b
   * 1030 - (b is blocked)
Packit ae235b
   * 1050 - end runs, quits outer loop, which has no effect yet
Packit ae235b
   * 1110 - inner loop ends, a returns, outer loop exits
Packit ae235b
   */
Packit ae235b
Packit ae235b
  g_assert_cmpint (a, ==, 6);
Packit ae235b
  g_assert_cmpint (b, ==, 3);
Packit ae235b
  g_assert_cmpint (c, ==, 3);
Packit ae235b
Packit ae235b
  g_source_destroy (parent);
Packit ae235b
  g_source_unref (parent);
Packit ae235b
  g_source_unref (child_b);
Packit ae235b
  g_source_unref (child_c);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_recursive_child_sources (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *parent, *child_b, *child_c, *end;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  loop = g_main_loop_new (ctx, FALSE);
Packit ae235b
Packit ae235b
  a = b = c = 0;
Packit ae235b
Packit ae235b
  parent = counter_source_new (500);
Packit ae235b
  g_source_set_callback (parent, count_calls, &a, NULL);
Packit ae235b
Packit ae235b
  child_b = counter_source_new (220);
Packit ae235b
  g_source_set_callback (child_b, count_calls, &b, NULL);
Packit ae235b
  g_source_add_child_source (parent, child_b);
Packit ae235b
Packit ae235b
  child_c = counter_source_new (430);
Packit ae235b
  g_source_set_callback (child_c, count_calls, &c, NULL);
Packit ae235b
  g_source_add_child_source (child_b, child_c);
Packit ae235b
Packit ae235b
  g_source_attach (parent, ctx);
Packit ae235b
Packit ae235b
  end = counter_source_new (2010);
Packit ae235b
  g_source_set_callback (end, (GSourceFunc)g_main_loop_quit, loop, NULL);
Packit ae235b
  g_source_attach (end, ctx);
Packit ae235b
  g_source_unref (end);
Packit ae235b
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  /* Sequence of events:
Packit ae235b
   *  220 b (b -> 440, a -> 720)
Packit ae235b
   *  430 c (c -> 860, b -> 650, a -> 930)
Packit ae235b
   *  650 b (b -> 870, a -> 1150)
Packit ae235b
   *  860 c (c -> 1290, b -> 1080, a -> 1360)
Packit ae235b
   * 1080 b (b -> 1300, a -> 1580)
Packit ae235b
   * 1290 c (c -> 1720, b -> 1510, a -> 1790)
Packit ae235b
   * 1510 b (b -> 1730, a -> 2010)
Packit ae235b
   * 1720 c (c -> 2150, b -> 1940, a -> 2220)
Packit ae235b
   * 1940 b (b -> 2160, a -> 2440)
Packit ae235b
   */
Packit ae235b
Packit ae235b
  g_assert_cmpint (a, ==, 9);
Packit ae235b
  g_assert_cmpint (b, ==, 9);
Packit ae235b
  g_assert_cmpint (c, ==, 4);
Packit ae235b
Packit ae235b
  g_source_destroy (parent);
Packit ae235b
  g_source_unref (parent);
Packit ae235b
  g_source_unref (child_b);
Packit ae235b
  g_source_unref (child_c);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct {
Packit ae235b
  GSource *parent, *old_child, *new_child;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
} SwappingTestData;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
swap_sources (gpointer user_data)
Packit ae235b
{
Packit ae235b
  SwappingTestData *data = user_data;
Packit ae235b
Packit ae235b
  if (data->old_child)
Packit ae235b
    {
Packit ae235b
      g_source_remove_child_source (data->parent, data->old_child);
Packit ae235b
      g_clear_pointer (&data->old_child, g_source_unref);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  if (!data->new_child)
Packit ae235b
    {
Packit ae235b
      data->new_child = g_timeout_source_new (0);
Packit ae235b
      g_source_set_callback (data->new_child, quit_loop, data->loop, NULL);
Packit ae235b
      g_source_add_child_source (data->parent, data->new_child);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return G_SOURCE_CONTINUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
assert_not_reached_callback (gpointer user_data)
Packit ae235b
{
Packit ae235b
  g_assert_not_reached ();
Packit ae235b
Packit ae235b
  return G_SOURCE_REMOVE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_swapping_child_sources (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  SwappingTestData data;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  loop = g_main_loop_new (ctx, FALSE);
Packit ae235b
Packit ae235b
  data.parent = counter_source_new (50);
Packit ae235b
  data.loop = loop;
Packit ae235b
  g_source_set_callback (data.parent, swap_sources, &data, NULL);
Packit ae235b
  g_source_attach (data.parent, ctx);
Packit ae235b
Packit ae235b
  data.old_child = counter_source_new (100);
Packit ae235b
  g_source_add_child_source (data.parent, data.old_child);
Packit ae235b
  g_source_set_callback (data.old_child, assert_not_reached_callback, NULL, NULL);
Packit ae235b
Packit ae235b
  data.new_child = NULL;
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  g_source_destroy (data.parent);
Packit ae235b
  g_source_unref (data.parent);
Packit ae235b
  g_source_unref (data.new_child);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
add_source_callback (gpointer user_data)
Packit ae235b
{
Packit ae235b
  GMainLoop *loop = user_data;
Packit ae235b
  GSource *self = g_main_current_source (), *child;
Packit ae235b
  GIOChannel *io;
Packit ae235b
Packit ae235b
  /* It doesn't matter whether this is a valid fd or not; it never
Packit ae235b
   * actually gets polled; the test is just checking that
Packit ae235b
   * g_source_add_child_source() doesn't crash.
Packit ae235b
   */
Packit ae235b
  io = g_io_channel_unix_new (0);
Packit ae235b
  child = g_io_create_watch (io, G_IO_IN);
Packit ae235b
  g_source_add_child_source (self, child);
Packit ae235b
  g_source_unref (child);
Packit ae235b
  g_io_channel_unref (io);
Packit ae235b
Packit ae235b
  g_main_loop_quit (loop);
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_blocked_child_sources (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  g_test_bug ("701283");
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  loop = g_main_loop_new (ctx, FALSE);
Packit ae235b
Packit ae235b
  source = g_idle_source_new ();
Packit ae235b
  g_source_set_callback (source, add_source_callback, loop, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  g_source_destroy (source);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct {
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
Packit ae235b
  GSource *timeout1, *timeout2;
Packit ae235b
  gint64 time1;
Packit ae235b
  GTimeVal tv;
Packit ae235b
} TimeTestData;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
timeout1_callback (gpointer user_data)
Packit ae235b
{
Packit ae235b
  TimeTestData *data = user_data;
Packit ae235b
  GSource *source;
Packit ae235b
  gint64 mtime1, mtime2, time2;
Packit ae235b
Packit ae235b
  source = g_main_current_source ();
Packit ae235b
  g_assert (source == data->timeout1);
Packit ae235b
Packit ae235b
  if (data->time1 == -1)
Packit ae235b
    {
Packit ae235b
      /* First iteration */
Packit ae235b
      g_assert (!g_source_is_destroyed (data->timeout2));
Packit ae235b
Packit ae235b
      mtime1 = g_get_monotonic_time ();
Packit ae235b
      data->time1 = g_source_get_time (source);
Packit ae235b
Packit ae235b
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
Packit ae235b
      g_source_get_current_time (source, &data->tv);
Packit ae235b
G_GNUC_END_IGNORE_DEPRECATIONS
Packit ae235b
Packit ae235b
      /* g_source_get_time() does not change during a single callback */
Packit ae235b
      g_usleep (1000000);
Packit ae235b
      mtime2 = g_get_monotonic_time ();
Packit ae235b
      time2 = g_source_get_time (source);
Packit ae235b
Packit ae235b
      g_assert_cmpint (mtime1, <, mtime2);
Packit ae235b
      g_assert_cmpint (data->time1, ==, time2);
Packit ae235b
    }
Packit ae235b
  else
Packit ae235b
    {
Packit ae235b
      GTimeVal tv;
Packit ae235b
Packit ae235b
      /* Second iteration */
Packit ae235b
      g_assert (g_source_is_destroyed (data->timeout2));
Packit ae235b
Packit ae235b
      /* g_source_get_time() MAY change between iterations; in this
Packit ae235b
       * case we know for sure that it did because of the g_usleep()
Packit ae235b
       * last time.
Packit ae235b
       */
Packit ae235b
      time2 = g_source_get_time (source);
Packit ae235b
      g_assert_cmpint (data->time1, <, time2);
Packit ae235b
Packit ae235b
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
Packit ae235b
      g_source_get_current_time (source, &tv;;
Packit ae235b
G_GNUC_END_IGNORE_DEPRECATIONS
Packit ae235b
Packit ae235b
      g_assert (tv.tv_sec > data->tv.tv_sec ||
Packit ae235b
                (tv.tv_sec == data->tv.tv_sec &&
Packit ae235b
                 tv.tv_usec > data->tv.tv_usec));
Packit ae235b
Packit ae235b
      g_main_loop_quit (data->loop);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
timeout2_callback (gpointer user_data)
Packit ae235b
{
Packit ae235b
  TimeTestData *data = user_data;
Packit ae235b
  GSource *source;
Packit ae235b
  gint64 time2, time3;
Packit ae235b
Packit ae235b
  source = g_main_current_source ();
Packit ae235b
  g_assert (source == data->timeout2);
Packit ae235b
Packit ae235b
  g_assert (!g_source_is_destroyed (data->timeout1));
Packit ae235b
Packit ae235b
  /* g_source_get_time() does not change between different sources in
Packit ae235b
   * a single iteration of the mainloop.
Packit ae235b
   */
Packit ae235b
  time2 = g_source_get_time (source);
Packit ae235b
  g_assert_cmpint (data->time1, ==, time2);
Packit ae235b
Packit ae235b
  /* The source should still have a valid time even after being
Packit ae235b
   * destroyed, since it's currently running.
Packit ae235b
   */
Packit ae235b
  g_source_destroy (source);
Packit ae235b
  time3 = g_source_get_time (source);
Packit ae235b
  g_assert_cmpint (time2, ==, time3);
Packit ae235b
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_source_time (void)
Packit ae235b
{
Packit ae235b
  TimeTestData data;
Packit ae235b
Packit ae235b
  data.ctx = g_main_context_new ();
Packit ae235b
  data.loop = g_main_loop_new (data.ctx, FALSE);
Packit ae235b
Packit ae235b
  data.timeout1 = g_timeout_source_new (0);
Packit ae235b
  g_source_set_callback (data.timeout1, timeout1_callback, &data, NULL);
Packit ae235b
  g_source_attach (data.timeout1, data.ctx);
Packit ae235b
Packit ae235b
  data.timeout2 = g_timeout_source_new (0);
Packit ae235b
  g_source_set_callback (data.timeout2, timeout2_callback, &data, NULL);
Packit ae235b
  g_source_attach (data.timeout2, data.ctx);
Packit ae235b
Packit ae235b
  data.time1 = -1;
Packit ae235b
Packit ae235b
  g_main_loop_run (data.loop);
Packit ae235b
Packit ae235b
  g_assert (!g_source_is_destroyed (data.timeout1));
Packit ae235b
  g_assert (g_source_is_destroyed (data.timeout2));
Packit ae235b
Packit ae235b
  g_source_destroy (data.timeout1);
Packit ae235b
  g_source_unref (data.timeout1);
Packit ae235b
  g_source_unref (data.timeout2);
Packit ae235b
Packit ae235b
  g_main_loop_unref (data.loop);
Packit ae235b
  g_main_context_unref (data.ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct {
Packit ae235b
  guint outstanding_ops;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
} TestOverflowData;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
on_source_fired_cb (gpointer user_data)
Packit ae235b
{
Packit ae235b
  TestOverflowData *data = user_data;
Packit ae235b
  GSource *current_source;
Packit ae235b
  GMainContext *current_context;
Packit ae235b
  guint source_id;
Packit ae235b
Packit ae235b
  data->outstanding_ops--;
Packit ae235b
Packit ae235b
  current_source = g_main_current_source ();
Packit ae235b
  current_context = g_source_get_context (current_source);
Packit ae235b
  source_id = g_source_get_id (current_source);
Packit ae235b
  g_assert (g_main_context_find_source_by_id (current_context, source_id) != NULL);
Packit ae235b
  g_source_destroy (current_source);
Packit ae235b
  g_assert (g_main_context_find_source_by_id (current_context, source_id) == NULL);
Packit ae235b
Packit ae235b
  if (data->outstanding_ops == 0)
Packit ae235b
    g_main_loop_quit (data->loop);
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static GSource *
Packit ae235b
add_idle_source (GMainContext *ctx,
Packit ae235b
                 TestOverflowData *data)
Packit ae235b
{
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  source = g_idle_source_new ();
Packit ae235b
  g_source_set_callback (source, on_source_fired_cb, data, NULL);
Packit ae235b
  g_source_attach (source, ctx);
Packit ae235b
  g_source_unref (source);
Packit ae235b
  data->outstanding_ops++;
Packit ae235b
Packit ae235b
  return source;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_mainloop_overflow (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *source;
Packit ae235b
  TestOverflowData data;
Packit ae235b
  guint i;
Packit ae235b
Packit ae235b
  g_test_bug ("687098");
Packit ae235b
Packit ae235b
  memset (&data, 0, sizeof (data));
Packit ae235b
Packit ae235b
  ctx = GLIB_PRIVATE_CALL (g_main_context_new_with_next_id) (G_MAXUINT-1);
Packit ae235b
Packit ae235b
  loop = g_main_loop_new (ctx, TRUE);
Packit ae235b
  data.outstanding_ops = 0;
Packit ae235b
  data.loop = loop;
Packit ae235b
Packit ae235b
  source = add_idle_source (ctx, &data);
Packit ae235b
  g_assert_cmpint (source->source_id, ==, G_MAXUINT-1);
Packit ae235b
Packit ae235b
  source = add_idle_source (ctx, &data);
Packit ae235b
  g_assert_cmpint (source->source_id, ==, G_MAXUINT);
Packit ae235b
Packit ae235b
  source = add_idle_source (ctx, &data);
Packit ae235b
  g_assert_cmpint (source->source_id, !=, 0);
Packit ae235b
Packit ae235b
  /* Now, a lot more sources */
Packit ae235b
  for (i = 0; i < 50; i++)
Packit ae235b
    {
Packit ae235b
      source = add_idle_source (ctx, &data);
Packit ae235b
      g_assert_cmpint (source->source_id, !=, 0);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
  g_assert_cmpint (data.outstanding_ops, ==, 0);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
static volatile gint ready_time_dispatched;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
ready_time_dispatch (GSource     *source,
Packit ae235b
                     GSourceFunc  callback,
Packit ae235b
                     gpointer     user_data)
Packit ae235b
{
Packit ae235b
  g_atomic_int_set (&ready_time_dispatched, TRUE);
Packit ae235b
Packit ae235b
  g_source_set_ready_time (source, -1);
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gpointer
Packit ae235b
run_context (gpointer user_data)
Packit ae235b
{
Packit ae235b
  g_main_loop_run (user_data);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_ready_time (void)
Packit ae235b
{
Packit ae235b
  GThread *thread;
Packit ae235b
  GSource *source;
Packit ae235b
  GSourceFuncs source_funcs = {
Packit ae235b
    NULL, NULL, ready_time_dispatch
Packit ae235b
  };
Packit ae235b
  GMainLoop *loop;
Packit ae235b
Packit ae235b
  source = g_source_new (&source_funcs, sizeof (GSource));
Packit ae235b
  g_source_attach (source, NULL);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  /* Unfortunately we can't do too many things with respect to timing
Packit ae235b
   * without getting into trouble on slow systems or heavily loaded
Packit ae235b
   * builders.
Packit ae235b
   *
Packit ae235b
   * We can test that the basics are working, though.
Packit ae235b
   */
Packit ae235b
Packit ae235b
  /* A source with no ready time set should not fire */
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_assert (!ready_time_dispatched);
Packit ae235b
Packit ae235b
  /* The ready time should not have been changed */
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
Packit ae235b
  /* Of course this shouldn't change anything either */
Packit ae235b
  g_source_set_ready_time (source, -1);
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
Packit ae235b
  /* A source with a ready time set to tomorrow should not fire on any
Packit ae235b
   * builder, no matter how badly loaded...
Packit ae235b
   */
Packit ae235b
  g_source_set_ready_time (source, g_get_monotonic_time () + G_TIME_SPAN_DAY);
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_assert (!ready_time_dispatched);
Packit ae235b
  /* Make sure it didn't get reset */
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), !=, -1);
Packit ae235b
Packit ae235b
  /* Ready time of -1 -> don't fire */
Packit ae235b
  g_source_set_ready_time (source, -1);
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_assert (!ready_time_dispatched);
Packit ae235b
  /* Not reset, but should still be -1 from above */
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
Packit ae235b
  /* A ready time of the current time should fire immediately */
Packit ae235b
  g_source_set_ready_time (source, g_get_monotonic_time ());
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_assert (ready_time_dispatched);
Packit ae235b
  ready_time_dispatched = FALSE;
Packit ae235b
  /* Should have gotten reset by the handler function */
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
Packit ae235b
  /* As well as one in the recent past... */
Packit ae235b
  g_source_set_ready_time (source, g_get_monotonic_time () - G_TIME_SPAN_SECOND);
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_assert (ready_time_dispatched);
Packit ae235b
  ready_time_dispatched = FALSE;
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
Packit ae235b
  /* Zero is the 'official' way to get a source to fire immediately */
Packit ae235b
  g_source_set_ready_time (source, 0);
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_assert (ready_time_dispatched);
Packit ae235b
  ready_time_dispatched = FALSE;
Packit ae235b
  g_assert_cmpint (g_source_get_ready_time (source), ==, -1);
Packit ae235b
Packit ae235b
  /* Now do some tests of cross-thread wakeups.
Packit ae235b
   *
Packit ae235b
   * Make sure it wakes up right away from the start.
Packit ae235b
   */
Packit ae235b
  g_source_set_ready_time (source, 0);
Packit ae235b
  loop = g_main_loop_new (NULL, FALSE);
Packit ae235b
  thread = g_thread_new ("context thread", run_context, loop);
Packit ae235b
  while (!g_atomic_int_get (&ready_time_dispatched));
Packit ae235b
Packit ae235b
  /* Now let's see if it can wake up from sleeping. */
Packit ae235b
  g_usleep (G_TIME_SPAN_SECOND / 2);
Packit ae235b
  g_atomic_int_set (&ready_time_dispatched, FALSE);
Packit ae235b
  g_source_set_ready_time (source, 0);
Packit ae235b
  while (!g_atomic_int_get (&ready_time_dispatched));
Packit ae235b
Packit ae235b
  /* kill the thread */
Packit ae235b
  g_main_loop_quit (loop);
Packit ae235b
  g_thread_join (thread);
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
Packit ae235b
  g_source_destroy (source);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_wakeup(void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  int i;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
Packit ae235b
  /* run a random large enough number of times because 
Packit ae235b
   * main contexts tend to wake up a few times after creation.
Packit ae235b
   */
Packit ae235b
  for (i = 0; i < 100; i++)
Packit ae235b
    {
Packit ae235b
      /* This is the invariant we care about:
Packit ae235b
       * g_main_context_wakeup(ctx,) ensures that the next call to
Packit ae235b
       * g_main_context_iteration (ctx, TRUE) returns and doesn't
Packit ae235b
       * block.
Packit ae235b
       * This is important in threaded apps where we might not know
Packit ae235b
       * if the thread calls g_main_context_wakeup() before or after
Packit ae235b
       * we enter g_main_context_iteration().
Packit ae235b
       */
Packit ae235b
      g_main_context_wakeup (ctx);
Packit ae235b
      g_main_context_iteration (ctx, TRUE);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_remove_invalid (void)
Packit ae235b
{
Packit ae235b
  g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "Source ID 3000000000 was not found*");
Packit ae235b
  g_source_remove (3000000000u);
Packit ae235b
  g_test_assert_expected_messages ();
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
trivial_prepare (GSource *source,
Packit ae235b
                 gint    *timeout)
Packit ae235b
{
Packit ae235b
  *timeout = 0;
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gint n_finalized;
Packit ae235b
Packit ae235b
static void
Packit ae235b
trivial_finalize (GSource *source)
Packit ae235b
{
Packit ae235b
  n_finalized++;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_unref_while_pending (void)
Packit ae235b
{
Packit ae235b
  static GSourceFuncs funcs = { trivial_prepare, NULL, NULL, trivial_finalize };
Packit ae235b
  GMainContext *context;
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  context = g_main_context_new ();
Packit ae235b
Packit ae235b
  source = g_source_new (&funcs, sizeof (GSource));
Packit ae235b
  g_source_attach (source, context);
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  /* Do incomplete main iteration -- get a pending source but don't dispatch it. */
Packit ae235b
  g_main_context_prepare (context, NULL);
Packit ae235b
  g_main_context_query (context, 0, NULL, NULL, 0);
Packit ae235b
  g_main_context_check (context, 1000, NULL, 0);
Packit ae235b
Packit ae235b
  /* Destroy the context */
Packit ae235b
  g_main_context_unref (context);
Packit ae235b
Packit ae235b
  /* Make sure we didn't leak the source */
Packit ae235b
  g_assert_cmpint (n_finalized, ==, 1);
Packit ae235b
}
Packit ae235b
Packit ae235b
#ifdef G_OS_UNIX
Packit ae235b
Packit ae235b
#include <glib-unix.h>
Packit ae235b
#include <unistd.h>
Packit ae235b
Packit ae235b
static gchar zeros[1024];
Packit ae235b
Packit ae235b
static gsize
Packit ae235b
fill_a_pipe (gint fd)
Packit ae235b
{
Packit ae235b
  gsize written = 0;
Packit ae235b
  GPollFD pfd;
Packit ae235b
Packit ae235b
  pfd.fd = fd;
Packit ae235b
  pfd.events = G_IO_OUT;
Packit ae235b
  while (g_poll (&pfd, 1, 0) == 1)
Packit ae235b
    /* we should never see -1 here */
Packit ae235b
    written += write (fd, zeros, sizeof zeros);
Packit ae235b
Packit ae235b
  return written;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
write_bytes (gint         fd,
Packit ae235b
             GIOCondition condition,
Packit ae235b
             gpointer     user_data)
Packit ae235b
{
Packit ae235b
  gssize *to_write = user_data;
Packit ae235b
  gint limit;
Packit ae235b
Packit ae235b
  if (*to_write == 0)
Packit ae235b
    return FALSE;
Packit ae235b
Packit ae235b
  /* Detect if we run before we should */
Packit ae235b
  g_assert (*to_write >= 0);
Packit ae235b
Packit ae235b
  limit = MIN (*to_write, sizeof zeros);
Packit ae235b
  *to_write -= write (fd, zeros, limit);
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
read_bytes (gint         fd,
Packit ae235b
            GIOCondition condition,
Packit ae235b
            gpointer     user_data)
Packit ae235b
{
Packit ae235b
  static gchar buffer[1024];
Packit ae235b
  gssize *to_read = user_data;
Packit ae235b
Packit ae235b
  *to_read -= read (fd, buffer, sizeof buffer);
Packit ae235b
Packit ae235b
  /* The loop will exit when there is nothing else to read, then we will
Packit ae235b
   * use g_source_remove() to destroy this source.
Packit ae235b
   */
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_unix_fd (void)
Packit ae235b
{
Packit ae235b
  gssize to_write = -1;
Packit ae235b
  gssize to_read;
Packit ae235b
  gint fds[2];
Packit ae235b
  gint a, b;
Packit ae235b
  gint s;
Packit ae235b
  GSource *source_a;
Packit ae235b
  GSource *source_b;
Packit ae235b
Packit ae235b
  s = pipe (fds);
Packit ae235b
  g_assert (s == 0);
Packit ae235b
Packit ae235b
  to_read = fill_a_pipe (fds[1]);
Packit ae235b
  /* write at higher priority to keep the pipe full... */
Packit ae235b
  a = g_unix_fd_add_full (G_PRIORITY_HIGH, fds[1], G_IO_OUT, write_bytes, &to_write, NULL);
Packit ae235b
  source_a = g_source_ref (g_main_context_find_source_by_id (NULL, a));
Packit ae235b
  /* make sure no 'writes' get dispatched yet */
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
Packit ae235b
  to_read += 128 * 1024 * 1024;
Packit ae235b
  to_write = 128 * 1024 * 1024;
Packit ae235b
  b = g_unix_fd_add (fds[0], G_IO_IN, read_bytes, &to_read);
Packit ae235b
  source_b = g_source_ref (g_main_context_find_source_by_id (NULL, b));
Packit ae235b
Packit ae235b
  /* Assuming the kernel isn't internally 'laggy' then there will always
Packit ae235b
   * be either data to read or room in which to write.  That will keep
Packit ae235b
   * the loop running until all data has been read and written.
Packit ae235b
   */
Packit ae235b
  while (TRUE)
Packit ae235b
    {
Packit ae235b
      gssize to_write_was = to_write;
Packit ae235b
      gssize to_read_was = to_read;
Packit ae235b
Packit ae235b
      if (!g_main_context_iteration (NULL, FALSE))
Packit ae235b
        break;
Packit ae235b
Packit ae235b
      /* Since the sources are at different priority, only one of them
Packit ae235b
       * should possibly have run.
Packit ae235b
       */
Packit ae235b
      g_assert (to_write == to_write_was || to_read == to_read_was);
Packit ae235b
    }
Packit ae235b
Packit ae235b
  g_assert (to_write == 0);
Packit ae235b
  g_assert (to_read == 0);
Packit ae235b
Packit ae235b
  /* 'a' is already removed by itself */
Packit ae235b
  g_assert (g_source_is_destroyed (source_a));
Packit ae235b
  g_source_unref (source_a);
Packit ae235b
  g_source_remove (b);
Packit ae235b
  g_assert (g_source_is_destroyed (source_b));
Packit ae235b
  g_source_unref (source_b);
Packit ae235b
  close (fds[1]);
Packit ae235b
  close (fds[0]);
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
assert_main_context_state (gint n_to_poll,
Packit ae235b
                           ...)
Packit ae235b
{
Packit ae235b
  GMainContext *context;
Packit ae235b
  gboolean consumed[10] = { };
Packit ae235b
  GPollFD poll_fds[10];
Packit ae235b
  gboolean acquired;
Packit ae235b
  gboolean immediate;
Packit ae235b
  gint max_priority;
Packit ae235b
  gint timeout;
Packit ae235b
  gint n;
Packit ae235b
  gint i, j;
Packit ae235b
  va_list ap;
Packit ae235b
Packit ae235b
  context = g_main_context_default ();
Packit ae235b
Packit ae235b
  acquired = g_main_context_acquire (context);
Packit ae235b
  g_assert (acquired);
Packit ae235b
Packit ae235b
  immediate = g_main_context_prepare (context, &max_priority);
Packit ae235b
  g_assert (!immediate);
Packit ae235b
  n = g_main_context_query (context, max_priority, &timeout, poll_fds, 10);
Packit ae235b
  g_assert_cmpint (n, ==, n_to_poll + 1); /* one will be the gwakeup */
Packit ae235b
Packit ae235b
  va_start (ap, n_to_poll);
Packit ae235b
  for (i = 0; i < n_to_poll; i++)
Packit ae235b
    {
Packit ae235b
      gint expected_fd = va_arg (ap, gint);
Packit ae235b
      GIOCondition expected_events = va_arg (ap, GIOCondition);
Packit ae235b
      GIOCondition report_events = va_arg (ap, GIOCondition);
Packit ae235b
Packit ae235b
      for (j = 0; j < n; j++)
Packit ae235b
        if (!consumed[j] && poll_fds[j].fd == expected_fd && poll_fds[j].events == expected_events)
Packit ae235b
          {
Packit ae235b
            poll_fds[j].revents = report_events;
Packit ae235b
            consumed[j] = TRUE;
Packit ae235b
            break;
Packit ae235b
          }
Packit ae235b
Packit ae235b
      if (j == n)
Packit ae235b
        g_error ("Unable to find fd %d (index %d) with events 0x%x\n", expected_fd, i, (guint) expected_events);
Packit ae235b
    }
Packit ae235b
  va_end (ap);
Packit ae235b
Packit ae235b
  /* find the gwakeup, flag as non-ready */
Packit ae235b
  for (i = 0; i < n; i++)
Packit ae235b
    if (!consumed[i])
Packit ae235b
      poll_fds[i].revents = 0;
Packit ae235b
Packit ae235b
  if (g_main_context_check (context, max_priority, poll_fds, n))
Packit ae235b
    g_main_context_dispatch (context);
Packit ae235b
Packit ae235b
  g_main_context_release (context);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
flag_bool (gint         fd,
Packit ae235b
           GIOCondition condition,
Packit ae235b
           gpointer     user_data)
Packit ae235b
{
Packit ae235b
  gboolean *flag = user_data;
Packit ae235b
Packit ae235b
  *flag = TRUE;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_unix_fd_source (void)
Packit ae235b
{
Packit ae235b
  GSource *out_source;
Packit ae235b
  GSource *in_source;
Packit ae235b
  GSource *source;
Packit ae235b
  gboolean out, in;
Packit ae235b
  gint fds[2];
Packit ae235b
  gint s;
Packit ae235b
Packit ae235b
  assert_main_context_state (0);
Packit ae235b
Packit ae235b
  s = pipe (fds);
Packit ae235b
  g_assert (s == 0);
Packit ae235b
Packit ae235b
  source = g_unix_fd_source_new (fds[1], G_IO_OUT);
Packit ae235b
  g_source_attach (source, NULL);
Packit ae235b
Packit ae235b
  /* Check that a source with no callback gets successfully detached
Packit ae235b
   * with a warning printed.
Packit ae235b
   */
Packit ae235b
  g_test_expect_message ("GLib", G_LOG_LEVEL_WARNING, "*GUnixFDSource dispatched without callback*");
Packit ae235b
  while (g_main_context_iteration (NULL, FALSE));
Packit ae235b
  g_test_assert_expected_messages ();
Packit ae235b
  g_assert (g_source_is_destroyed (source));
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  out = in = FALSE;
Packit ae235b
  out_source = g_unix_fd_source_new (fds[1], G_IO_OUT);
Packit ae235b
  g_source_set_callback (out_source, (GSourceFunc) flag_bool, &out, NULL);
Packit ae235b
  g_source_attach (out_source, NULL);
Packit ae235b
  assert_main_context_state (1,
Packit ae235b
                             fds[1], G_IO_OUT, 0);
Packit ae235b
  g_assert (!in && !out);
Packit ae235b
Packit ae235b
  in_source = g_unix_fd_source_new (fds[0], G_IO_IN);
Packit ae235b
  g_source_set_callback (in_source, (GSourceFunc) flag_bool, &in, NULL);
Packit ae235b
  g_source_set_priority (in_source, G_PRIORITY_DEFAULT_IDLE);
Packit ae235b
  g_source_attach (in_source, NULL);
Packit ae235b
  assert_main_context_state (2,
Packit ae235b
                             fds[0], G_IO_IN, G_IO_IN,
Packit ae235b
                             fds[1], G_IO_OUT, G_IO_OUT);
Packit ae235b
  /* out is higher priority so only it should fire */
Packit ae235b
  g_assert (!in && out);
Packit ae235b
Packit ae235b
  /* raise the priority of the in source to higher than out*/
Packit ae235b
  in = out = FALSE;
Packit ae235b
  g_source_set_priority (in_source, G_PRIORITY_HIGH);
Packit ae235b
  assert_main_context_state (2,
Packit ae235b
                             fds[0], G_IO_IN, G_IO_IN,
Packit ae235b
                             fds[1], G_IO_OUT, G_IO_OUT);
Packit ae235b
  g_assert (in && !out);
Packit ae235b
Packit ae235b
  /* now, let them be equal */
Packit ae235b
  in = out = FALSE;
Packit ae235b
  g_source_set_priority (in_source, G_PRIORITY_DEFAULT);
Packit ae235b
  assert_main_context_state (2,
Packit ae235b
                             fds[0], G_IO_IN, G_IO_IN,
Packit ae235b
                             fds[1], G_IO_OUT, G_IO_OUT);
Packit ae235b
  g_assert (in && out);
Packit ae235b
Packit ae235b
  g_source_destroy (out_source);
Packit ae235b
  g_source_unref (out_source);
Packit ae235b
  g_source_destroy (in_source);
Packit ae235b
  g_source_unref (in_source);
Packit ae235b
  close (fds[1]);
Packit ae235b
  close (fds[0]);
Packit ae235b
}
Packit ae235b
Packit ae235b
typedef struct
Packit ae235b
{
Packit ae235b
  GSource parent;
Packit ae235b
  gboolean flagged;
Packit ae235b
} FlagSource;
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
return_true (GSource *source, GSourceFunc callback, gpointer user_data)
Packit ae235b
{
Packit ae235b
  FlagSource *flag_source = (FlagSource *) source;
Packit ae235b
Packit ae235b
  flag_source->flagged = TRUE;
Packit ae235b
Packit ae235b
  return TRUE;
Packit ae235b
}
Packit ae235b
Packit ae235b
#define assert_flagged(s) g_assert (((FlagSource *) (s))->flagged);
Packit ae235b
#define assert_not_flagged(s) g_assert (!((FlagSource *) (s))->flagged);
Packit ae235b
#define clear_flag(s) ((FlagSource *) (s))->flagged = 0
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_source_unix_fd_api (void)
Packit ae235b
{
Packit ae235b
  GSourceFuncs no_funcs = {
Packit ae235b
    NULL, NULL, return_true
Packit ae235b
  };
Packit ae235b
  GSource *source_a;
Packit ae235b
  GSource *source_b;
Packit ae235b
  gpointer tag1, tag2;
Packit ae235b
  gint fds_a[2];
Packit ae235b
  gint fds_b[2];
Packit ae235b
Packit ae235b
  pipe (fds_a);
Packit ae235b
  pipe (fds_b);
Packit ae235b
Packit ae235b
  source_a = g_source_new (&no_funcs, sizeof (FlagSource));
Packit ae235b
  source_b = g_source_new (&no_funcs, sizeof (FlagSource));
Packit ae235b
Packit ae235b
  /* attach a source with more than one fd */
Packit ae235b
  g_source_add_unix_fd (source_a, fds_a[0], G_IO_IN);
Packit ae235b
  g_source_add_unix_fd (source_a, fds_a[1], G_IO_OUT);
Packit ae235b
  g_source_attach (source_a, NULL);
Packit ae235b
  assert_main_context_state (2,
Packit ae235b
                             fds_a[0], G_IO_IN, 0,
Packit ae235b
                             fds_a[1], G_IO_OUT, 0);
Packit ae235b
  assert_not_flagged (source_a);
Packit ae235b
Packit ae235b
  /* attach a higher priority source with no fds */
Packit ae235b
  g_source_set_priority (source_b, G_PRIORITY_HIGH);
Packit ae235b
  g_source_attach (source_b, NULL);
Packit ae235b
  assert_main_context_state (2,
Packit ae235b
                             fds_a[0], G_IO_IN, G_IO_IN,
Packit ae235b
                             fds_a[1], G_IO_OUT, 0);
Packit ae235b
  assert_flagged (source_a);
Packit ae235b
  assert_not_flagged (source_b);
Packit ae235b
  clear_flag (source_a);
Packit ae235b
Packit ae235b
  /* add some fds to the second source, while attached */
Packit ae235b
  tag1 = g_source_add_unix_fd (source_b, fds_b[0], G_IO_IN);
Packit ae235b
  tag2 = g_source_add_unix_fd (source_b, fds_b[1], G_IO_OUT);
Packit ae235b
  assert_main_context_state (4,
Packit ae235b
                             fds_a[0], G_IO_IN, 0,
Packit ae235b
                             fds_a[1], G_IO_OUT, G_IO_OUT,
Packit ae235b
                             fds_b[0], G_IO_IN, 0,
Packit ae235b
                             fds_b[1], G_IO_OUT, G_IO_OUT);
Packit ae235b
  /* only 'b' (higher priority) should have dispatched */
Packit ae235b
  assert_not_flagged (source_a);
Packit ae235b
  assert_flagged (source_b);
Packit ae235b
  clear_flag (source_b);
Packit ae235b
Packit ae235b
  /* change our events on b to the same as they were before */
Packit ae235b
  g_source_modify_unix_fd (source_b, tag1, G_IO_IN);
Packit ae235b
  g_source_modify_unix_fd (source_b, tag2, G_IO_OUT);
Packit ae235b
  assert_main_context_state (4,
Packit ae235b
                             fds_a[0], G_IO_IN, 0,
Packit ae235b
                             fds_a[1], G_IO_OUT, G_IO_OUT,
Packit ae235b
                             fds_b[0], G_IO_IN, 0,
Packit ae235b
                             fds_b[1], G_IO_OUT, G_IO_OUT);
Packit ae235b
  assert_not_flagged (source_a);
Packit ae235b
  assert_flagged (source_b);
Packit ae235b
  clear_flag (source_b);
Packit ae235b
Packit ae235b
  /* now reverse them */
Packit ae235b
  g_source_modify_unix_fd (source_b, tag1, G_IO_OUT);
Packit ae235b
  g_source_modify_unix_fd (source_b, tag2, G_IO_IN);
Packit ae235b
  assert_main_context_state (4,
Packit ae235b
                             fds_a[0], G_IO_IN, 0,
Packit ae235b
                             fds_a[1], G_IO_OUT, G_IO_OUT,
Packit ae235b
                             fds_b[0], G_IO_OUT, 0,
Packit ae235b
                             fds_b[1], G_IO_IN, 0);
Packit ae235b
  /* 'b' had no events, so 'a' can go this time */
Packit ae235b
  assert_flagged (source_a);
Packit ae235b
  assert_not_flagged (source_b);
Packit ae235b
  clear_flag (source_a);
Packit ae235b
Packit ae235b
  /* remove one of the fds from 'b' */
Packit ae235b
  g_source_remove_unix_fd (source_b, tag1);
Packit ae235b
  assert_main_context_state (3,
Packit ae235b
                             fds_a[0], G_IO_IN, 0,
Packit ae235b
                             fds_a[1], G_IO_OUT, 0,
Packit ae235b
                             fds_b[1], G_IO_IN, 0);
Packit ae235b
  assert_not_flagged (source_a);
Packit ae235b
  assert_not_flagged (source_b);
Packit ae235b
Packit ae235b
  /* remove the other */
Packit ae235b
  g_source_remove_unix_fd (source_b, tag2);
Packit ae235b
  assert_main_context_state (2,
Packit ae235b
                             fds_a[0], G_IO_IN, 0,
Packit ae235b
                             fds_a[1], G_IO_OUT, 0);
Packit ae235b
  assert_not_flagged (source_a);
Packit ae235b
  assert_not_flagged (source_b);
Packit ae235b
Packit ae235b
  /* destroy the sources */
Packit ae235b
  g_source_destroy (source_a);
Packit ae235b
  g_source_destroy (source_b);
Packit ae235b
  assert_main_context_state (0);
Packit ae235b
Packit ae235b
  g_source_unref (source_a);
Packit ae235b
  g_source_unref (source_b);
Packit ae235b
  close (fds_a[0]);
Packit ae235b
  close (fds_a[1]);
Packit ae235b
  close (fds_b[0]);
Packit ae235b
  close (fds_b[1]);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
unixfd_quit_loop (gint         fd,
Packit ae235b
                  GIOCondition condition,
Packit ae235b
                  gpointer     user_data)
Packit ae235b
{
Packit ae235b
  GMainLoop *loop = user_data;
Packit ae235b
Packit ae235b
  g_main_loop_quit (loop);
Packit ae235b
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_unix_file_poll (void)
Packit ae235b
{
Packit ae235b
  gint fd;
Packit ae235b
  GSource *source;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
Packit ae235b
  fd = open ("/dev/null", O_RDONLY);
Packit ae235b
  g_assert (fd >= 0);
Packit ae235b
Packit ae235b
  loop = g_main_loop_new (NULL, FALSE);
Packit ae235b
Packit ae235b
  source = g_unix_fd_source_new (fd, G_IO_IN);
Packit ae235b
  g_source_set_callback (source, (GSourceFunc) unixfd_quit_loop, loop, NULL);
Packit ae235b
  g_source_attach (source, NULL);
Packit ae235b
Packit ae235b
  /* Should not block */
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  g_source_destroy (source);
Packit ae235b
Packit ae235b
  assert_main_context_state (0);
Packit ae235b
Packit ae235b
  g_source_unref (source);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
Packit ae235b
  close (fd);
Packit ae235b
}
Packit ae235b
Packit ae235b
#endif
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
timeout_cb (gpointer data)
Packit ae235b
{
Packit ae235b
  GMainLoop *loop = data;
Packit ae235b
  GMainContext *context;
Packit ae235b
Packit ae235b
  context = g_main_loop_get_context (loop);
Packit ae235b
  g_assert (g_main_loop_is_running (loop));
Packit ae235b
  g_assert (g_main_context_is_owner (context));
Packit ae235b
Packit ae235b
  g_main_loop_quit (loop);
Packit ae235b
Packit ae235b
  return G_SOURCE_REMOVE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gpointer
Packit ae235b
threadf (gpointer data)
Packit ae235b
{
Packit ae235b
  GMainContext *context = data;
Packit ae235b
  GMainLoop *loop;
Packit ae235b
  GSource *source;
Packit ae235b
Packit ae235b
  loop = g_main_loop_new (context, FALSE);
Packit ae235b
  source = g_timeout_source_new (250);
Packit ae235b
  g_source_set_callback (source, timeout_cb, loop, NULL);
Packit ae235b
  g_source_attach (source, context);
Packit ae235b
  g_source_unref (source);
Packit ae235b
 
Packit ae235b
  g_main_loop_run (loop);
Packit ae235b
Packit ae235b
  g_main_loop_unref (loop);
Packit ae235b
Packit ae235b
  return NULL;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_mainloop_wait (void)
Packit ae235b
{
Packit ae235b
  GMainContext *context;
Packit ae235b
  GThread *t1, *t2;
Packit ae235b
Packit ae235b
  context = g_main_context_new ();
Packit ae235b
Packit ae235b
  t1 = g_thread_new ("t1", threadf, context);
Packit ae235b
  t2 = g_thread_new ("t2", threadf, context);
Packit ae235b
Packit ae235b
  g_thread_join (t1);
Packit ae235b
  g_thread_join (t2);
Packit ae235b
Packit ae235b
  g_main_context_unref (context);
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
nfds_in_cb (GIOChannel   *io,
Packit ae235b
            GIOCondition  condition,
Packit ae235b
            gpointer      user_data)
Packit ae235b
{
Packit ae235b
  gboolean *in_cb_ran = user_data;
Packit ae235b
Packit ae235b
  *in_cb_ran = TRUE;
Packit ae235b
  g_assert_cmpint (condition, ==, G_IO_IN);
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
nfds_out_cb (GIOChannel   *io,
Packit ae235b
             GIOCondition  condition,
Packit ae235b
             gpointer      user_data)
Packit ae235b
{
Packit ae235b
  gboolean *out_cb_ran = user_data;
Packit ae235b
Packit ae235b
  *out_cb_ran = TRUE;
Packit ae235b
  g_assert_cmpint (condition, ==, G_IO_OUT);
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static gboolean
Packit ae235b
nfds_out_low_cb (GIOChannel   *io,
Packit ae235b
                 GIOCondition  condition,
Packit ae235b
                 gpointer      user_data)
Packit ae235b
{
Packit ae235b
  g_assert_not_reached ();
Packit ae235b
  return FALSE;
Packit ae235b
}
Packit ae235b
Packit ae235b
static void
Packit ae235b
test_nfds (void)
Packit ae235b
{
Packit ae235b
  GMainContext *ctx;
Packit ae235b
  GPollFD out_fds[3];
Packit ae235b
  gint fd, nfds;
Packit ae235b
  GIOChannel *io;
Packit ae235b
  GSource *source1, *source2, *source3;
Packit ae235b
  gboolean source1_ran = FALSE, source3_ran = FALSE;
Packit ae235b
  gchar *tmpfile;
Packit ae235b
  GError *error = NULL;
Packit ae235b
Packit ae235b
  ctx = g_main_context_new ();
Packit ae235b
  nfds = g_main_context_query (ctx, G_MAXINT, NULL,
Packit ae235b
                               out_fds, G_N_ELEMENTS (out_fds));
Packit ae235b
  /* An "empty" GMainContext will have a single GPollFD, for its
Packit ae235b
   * internal GWakeup.
Packit ae235b
   */
Packit ae235b
  g_assert_cmpint (nfds, ==, 1);
Packit ae235b
Packit ae235b
  fd = g_file_open_tmp (NULL, &tmpfile, &error);
Packit ae235b
  g_assert_no_error (error);
Packit ae235b
Packit ae235b
  io = g_io_channel_unix_new (fd);
Packit ae235b
#ifdef G_OS_WIN32
Packit ae235b
  /* The fd in the pollfds won't be the same fd we passed in */
Packit ae235b
  g_io_channel_win32_make_pollfd (io, G_IO_IN, out_fds);
Packit ae235b
  fd = out_fds[0].fd;
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  /* Add our first pollfd */
Packit ae235b
  source1 = g_io_create_watch (io, G_IO_IN);
Packit ae235b
  g_source_set_priority (source1, G_PRIORITY_DEFAULT);
Packit ae235b
  g_source_set_callback (source1, (GSourceFunc) nfds_in_cb,
Packit ae235b
                         &source1_ran, NULL);
Packit ae235b
  g_source_attach (source1, ctx);
Packit ae235b
Packit ae235b
  nfds = g_main_context_query (ctx, G_MAXINT, NULL,
Packit ae235b
                               out_fds, G_N_ELEMENTS (out_fds));
Packit ae235b
  g_assert_cmpint (nfds, ==, 2);
Packit ae235b
  if (out_fds[0].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[0].events, ==, G_IO_IN);
Packit ae235b
  else if (out_fds[1].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[1].events, ==, G_IO_IN);
Packit ae235b
  else
Packit ae235b
    g_assert_not_reached ();
Packit ae235b
Packit ae235b
  /* Add a second pollfd with the same fd but different event, and
Packit ae235b
   * lower priority.
Packit ae235b
   */
Packit ae235b
  source2 = g_io_create_watch (io, G_IO_OUT);
Packit ae235b
  g_source_set_priority (source2, G_PRIORITY_LOW);
Packit ae235b
  g_source_set_callback (source2, (GSourceFunc) nfds_out_low_cb,
Packit ae235b
                         NULL, NULL);
Packit ae235b
  g_source_attach (source2, ctx);
Packit ae235b
Packit ae235b
  /* g_main_context_query() should still return only 2 pollfds,
Packit ae235b
   * one of which has our fd, and a combined events field.
Packit ae235b
   */
Packit ae235b
  nfds = g_main_context_query (ctx, G_MAXINT, NULL,
Packit ae235b
                               out_fds, G_N_ELEMENTS (out_fds));
Packit ae235b
  g_assert_cmpint (nfds, ==, 2);
Packit ae235b
  if (out_fds[0].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[0].events, ==, G_IO_IN | G_IO_OUT);
Packit ae235b
  else if (out_fds[1].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[1].events, ==, G_IO_IN | G_IO_OUT);
Packit ae235b
  else
Packit ae235b
    g_assert_not_reached ();
Packit ae235b
Packit ae235b
  /* But if we query with a max priority, we won't see the
Packit ae235b
   * lower-priority one.
Packit ae235b
   */
Packit ae235b
  nfds = g_main_context_query (ctx, G_PRIORITY_DEFAULT, NULL,
Packit ae235b
                               out_fds, G_N_ELEMENTS (out_fds));
Packit ae235b
  g_assert_cmpint (nfds, ==, 2);
Packit ae235b
  if (out_fds[0].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[0].events, ==, G_IO_IN);
Packit ae235b
  else if (out_fds[1].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[1].events, ==, G_IO_IN);
Packit ae235b
  else
Packit ae235b
    g_assert_not_reached ();
Packit ae235b
Packit ae235b
  /* Third pollfd */
Packit ae235b
  source3 = g_io_create_watch (io, G_IO_OUT);
Packit ae235b
  g_source_set_priority (source3, G_PRIORITY_DEFAULT);
Packit ae235b
  g_source_set_callback (source3, (GSourceFunc) nfds_out_cb,
Packit ae235b
                         &source3_ran, NULL);
Packit ae235b
  g_source_attach (source3, ctx);
Packit ae235b
Packit ae235b
  nfds = g_main_context_query (ctx, G_MAXINT, NULL,
Packit ae235b
                               out_fds, G_N_ELEMENTS (out_fds));
Packit ae235b
  g_assert_cmpint (nfds, ==, 2);
Packit ae235b
  if (out_fds[0].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[0].events, ==, G_IO_IN | G_IO_OUT);
Packit ae235b
  else if (out_fds[1].fd == fd)
Packit ae235b
    g_assert_cmpint (out_fds[1].events, ==, G_IO_IN | G_IO_OUT);
Packit ae235b
  else
Packit ae235b
    g_assert_not_reached ();
Packit ae235b
Packit ae235b
  /* Now actually iterate the loop; the fd should be readable and
Packit ae235b
   * writable, so source1 and source3 should be triggered, but *not*
Packit ae235b
   * source2, since it's lower priority than them. (Though on
Packit ae235b
   * G_OS_WIN32, source3 doesn't get triggered, probably because of
Packit ae235b
   * giowin32 weirdness...)
Packit ae235b
   */
Packit ae235b
  g_main_context_iteration (ctx, FALSE);
Packit ae235b
Packit ae235b
  g_assert (source1_ran);
Packit ae235b
#ifndef G_OS_WIN32
Packit ae235b
  g_assert (source3_ran);
Packit ae235b
#endif
Packit ae235b
Packit ae235b
  g_source_destroy (source1);
Packit ae235b
  g_source_unref (source1);
Packit ae235b
  g_source_destroy (source2);
Packit ae235b
  g_source_unref (source2);
Packit ae235b
  g_source_destroy (source3);
Packit ae235b
  g_source_unref (source3);
Packit ae235b
Packit ae235b
  g_io_channel_unref (io);
Packit ae235b
  remove (tmpfile);
Packit ae235b
  g_free (tmpfile);
Packit ae235b
Packit ae235b
  g_main_context_unref (ctx);
Packit ae235b
}
Packit ae235b
Packit ae235b
int
Packit ae235b
main (int argc, char *argv[])
Packit ae235b
{
Packit ae235b
  g_test_init (&argc, &argv, NULL);
Packit ae235b
  g_test_bug_base ("http://bugzilla.gnome.org/");
Packit ae235b
Packit ae235b
  g_test_add_func ("/maincontext/basic", test_maincontext_basic);
Packit ae235b
  g_test_add_func ("/mainloop/basic", test_mainloop_basic);
Packit ae235b
  g_test_add_func ("/mainloop/timeouts", test_timeouts);
Packit ae235b
  g_test_add_func ("/mainloop/priorities", test_priorities);
Packit ae235b
  g_test_add_func ("/mainloop/invoke", test_invoke);
Packit ae235b
  g_test_add_func ("/mainloop/child_sources", test_child_sources);
Packit ae235b
  g_test_add_func ("/mainloop/recursive_child_sources", test_recursive_child_sources);
Packit ae235b
  g_test_add_func ("/mainloop/swapping_child_sources", test_swapping_child_sources);
Packit ae235b
  g_test_add_func ("/mainloop/blocked_child_sources", test_blocked_child_sources);
Packit ae235b
  g_test_add_func ("/mainloop/source_time", test_source_time);
Packit ae235b
  g_test_add_func ("/mainloop/overflow", test_mainloop_overflow);
Packit ae235b
  g_test_add_func ("/mainloop/ready-time", test_ready_time);
Packit ae235b
  g_test_add_func ("/mainloop/wakeup", test_wakeup);
Packit ae235b
  g_test_add_func ("/mainloop/remove-invalid", test_remove_invalid);
Packit ae235b
  g_test_add_func ("/mainloop/unref-while-pending", test_unref_while_pending);
Packit ae235b
#ifdef G_OS_UNIX
Packit ae235b
  g_test_add_func ("/mainloop/unix-fd", test_unix_fd);
Packit ae235b
  g_test_add_func ("/mainloop/unix-fd-source", test_unix_fd_source);
Packit ae235b
  g_test_add_func ("/mainloop/source-unix-fd-api", test_source_unix_fd_api);
Packit ae235b
  g_test_add_func ("/mainloop/wait", test_mainloop_wait);
Packit ae235b
  g_test_add_func ("/mainloop/unix-file-poll", test_unix_file_poll);
Packit ae235b
#endif
Packit ae235b
  g_test_add_func ("/mainloop/nfds", test_nfds);
Packit ae235b
Packit ae235b
  return g_test_run ();
Packit ae235b
}