#include "glibtest.h"

#include <glib-object.h>

static volatile gint construct_counter = 0;

#define FOO_TYPE_REINDEER            (foo_reindeer_get_type ())
#define FOO_REINDEER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_REINDEER, FooReindeer))
#define FOO_REINDEER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  FOO_TYPE_REINDEER, FooReindeerClass))
#define FOO_IS_REINDEER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_REINDEER))
#define FOO_IS_REINDEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  FOO_TYPE_REINDEER))
#define FOO_REINDEER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  FOO_TYPE_REINDEER, FooReindeerClass))

typedef struct _FooReindeer FooReindeer;
typedef struct _FooReindeerClass FooReindeerClass;

struct _FooReindeer
{
  GObject parent;
};

struct _FooReindeerClass
{
  GObjectClass parent_class;
};

G_DEFINE_TYPE (FooReindeer, foo_reindeer, G_TYPE_OBJECT);


static void
foo_reindeer_init (FooReindeer *self)
{
}

FooReindeer *
foo_reindeer_new (void)
{
  FooReindeer * self;

  self = FOO_REINDEER (g_object_new (FOO_TYPE_REINDEER, NULL));

  return self;
}

static GObject * foo_reindeer_constructor (GType type,
    guint n_construct_properties,
    GObjectConstructParam * construct_properties)
{
  GObject * _obj;

  _obj = G_OBJECT_CLASS (foo_reindeer_parent_class)->constructor (type,
      n_construct_properties, construct_properties);

  g_atomic_int_inc (&construct_counter);

  return _obj;
}

static void
foo_reindeer_class_init (FooReindeerClass *klass)
{
  GObjectClass * object_class = G_OBJECT_CLASS (klass);

  g_usleep (G_USEC_PER_SEC / 20);

  object_class->constructor = foo_reindeer_constructor;
}

static gpointer
worker_func (gpointer data)
{
  g_object_unref (foo_reindeer_new ());
  return NULL;
}

static void
test_concurrent_construction (void)
{
  GThread * threads[4];
  guint i;

  for (i = 0; i < G_N_ELEMENTS (threads); i++)
  {
    threads[i] = g_thread_create_full (worker_func, NULL, 0, TRUE, TRUE,
        G_THREAD_PRIORITY_NORMAL, NULL);
  }

  for (i = 0; i < G_N_ELEMENTS (threads); i++)
  {
    g_thread_join (threads[i]);
  }

  g_assert_cmpint (construct_counter, ==, G_N_ELEMENTS (threads));
}

void
glibtest_register_constructor_tests (void)
{
  g_test_add_func ("/Constructor/test-concurrent-construction",
      &test_concurrent_construction);
}

