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

#define LOOPS 0xFFFF
#define WEAK 10
#define OBJECTS 10
#define THREADS 8

typedef struct
{
  GObject **objects;
  pthread_barrier_t barrier;
} data_t;

static gpointer
weak_ref_unref (gpointer udata)
{
  guint i, j, k;
  GWeakRef refs[WEAK * OBJECTS];
  GObject **objects;
  data_t *data = (data_t *)udata;
 
  pthread_barrier_wait (&data->barrier);
  objects = data->objects;

  for (j = 0; j < WEAK; j++)
    {
      for (k = 0; k < OBJECTS; k++)
	{
	  g_weak_ref_init (&refs[j * OBJECTS + k], objects[k]);
	}
    }

  for (i = 0; i < LOOPS; i++)
    {
      for (k = 0; k < OBJECTS; k++)
	{
	  for (j = 0; j < WEAK; j++)
	    {
	      g_object_unref (g_weak_ref_get (&refs[j * OBJECTS + k]));
	    }
	}
    }

  GObject *tobjects[OBJECTS];
  for (k = 0; k < OBJECTS; k++)
    {
      tobjects[k] = (GObject *)g_object_new (G_TYPE_OBJECT, NULL);
      for (j = 0; j < WEAK; j++)
	{
	  g_weak_ref_set (&refs[j * OBJECTS + k], tobjects[k]);
	}
    }

  for (i = 0; i < LOOPS; i++)
    {
      for (k = 0; k < OBJECTS; k++)
	{
	  for (j = 0; j < WEAK; j++)
	    {
	      g_object_unref (g_weak_ref_get (&refs[j * OBJECTS + k]));
	    }
	}
    }
  for (k = 0; k < OBJECTS; k++)
    {
      g_object_unref (tobjects[k]);
    }
  for (j = 0; j < WEAK; j++)
    {
      for (k = 0; k < OBJECTS; k++)
	{
	  g_weak_ref_clear (&refs[j * OBJECTS + k]);
	}
    }
  return NULL;
}

int main ()
{
  GObject *objects[OBJECTS];
  guint k, l;
  data_t data;

  for (k = 0; k < OBJECTS; k++)
    {
      objects[k] = (GObject *)g_object_new (G_TYPE_OBJECT, NULL);
    }

  pthread_barrier_init (&data.barrier, NULL, THREADS);
  data.objects = objects;
  GThread *threads[THREADS];
  for (l = 0; l < THREADS; l++)
    {
      threads[l] = g_thread_new (NULL, weak_ref_unref, &data);
    }
  for (l = 0; l < THREADS; l++)
    {
      g_thread_join (threads[l]);
    }
  return 0;
}
