#include <glib-object.h>
#include <string.h>

static gpointer target_mem = NULL;

static void provoke_race (void);

static void free_handler_func (gpointer mem);
static gpointer slow_worker_func (gpointer data);
static gpointer fast_worker_func (gpointer data);

gint
main (gint argc, gchar * argv[])
{
  static GMemVTable replacement_mem_vtable =
  {
    malloc,
    realloc,
    free_handler_func,
    NULL,
    NULL,
    NULL,
  };

  g_mem_set_vtable (&replacement_mem_vtable);

  g_setenv ("G_SLICE", "always-malloc", TRUE);

  g_type_init ();
  if (!g_thread_supported ())
    g_thread_init (NULL);

  provoke_race ();

  return 0;
}

static void
provoke_race (void)
{
  const gint num_runs = 1000000;
  gint i;

  for (i = 0; i < num_runs; i++)
  {
    GObject * obj;
    GThread * slow_thread, * fast_thread;

    obj = g_object_new (G_TYPE_OBJECT, NULL);
    g_object_ref (obj); /* start out with ref_count == 2 */

    target_mem = obj;

    slow_thread = g_thread_create_full (slow_worker_func, obj, 0, TRUE,
        TRUE, G_THREAD_PRIORITY_LOW, NULL);
    fast_thread = g_thread_create_full (fast_worker_func, obj, 0, TRUE,
        TRUE, G_THREAD_PRIORITY_URGENT, NULL);

    g_thread_join (slow_thread);
    g_thread_join (fast_thread);
  }
}

static void
free_handler_func (gpointer mem)
{
  if (mem == target_mem)
    memset (mem, 0xff, sizeof (GObject));
  else
    free (mem);
}

static gpointer
slow_worker_func (gpointer data)
{
  GObject * obj = data;

  g_object_unref (data);

  return NULL;
}

static gpointer
fast_worker_func (gpointer data)
{
  GObject * obj = data;

  while (obj->ref_count != 1)
    ;

  g_object_unref (obj);

  return NULL;
}

