#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <semaphore.h>

#include <stdio.h>
#include <glib-2.0/glib.h>


#define _DECLARE_G_SEMAPHORE(s_name) sem_t s_name;

#define _G_SUNLOCK(s_name)  sem_post(&s_name);

#define _G_STIMED_WAIT(seconds, s_name, exp_var, tumeout_st)\
    int exp_var=0;struct timespec tumeout_st;\
    clock_gettime(CLOCK_REALTIME, &tumeout_st);tumeout_st.tv_sec += seconds;\
    if(sem_timedwait(&s_name, &tumeout_st) != 0)\
    {if(errno == ETIMEDOUT) exp_var=1;}

#define _G_STIMED_WAIT_2(seconds, s_name, exp_var, tumeout_st)\
    exp_var=0;clock_gettime(CLOCK_REALTIME, &tumeout_st);\
    tumeout_st.tv_sec += seconds;\
    if(sem_timedwait(&s_name, &tumeout_st) != 0)\
    {if(errno == ETIMEDOUT) exp_var=1;}

#define _G_STIMED_WAIT_EXPIRED(exp_var) exp_var

#define DECLARE_G_SEMAPHORE         _DECLARE_G_SEMAPHORE(_g_sem_)
#define G_SUNLOCK                     _G_SUNLOCK(_g_sem_)
#define G_STIMED_WAIT(seconds)         _G_STIMED_WAIT\
                                (seconds, _g_sem_, __stw_expired, __stimeout)
#define G_STIMED_WAIT_2(seconds)    _G_STIMED_WAIT_2\
                                (seconds, _g_sem_, __stw_expired, __stimeout)
#define G_STIMED_WAIT_EXPIRED        _G_STIMED_WAIT_EXPIRED(__stw_expired)


#define DECLARE_G_SEMAPHORE_EX         _DECLARE_G_SEMAPHORE(_g_sem_2)
#define G_SUNLOCK_EX                 _G_SUNLOCK(_g_sem_2)
#define G_STIMED_WAIT_EX(seconds)     _G_STIMED_WAIT\
                            (seconds, _g_sem_2, __stw_expired_2, __stimeout_2)
#define G_STIMED_WAIT_2_EX(seconds)    _G_STIMED_WAIT_2\
                            (seconds, _g_sem_2, __stw_expired_2, __stimeout_2)
#define G_STIMED_WAIT_EXPIRED_EX    _G_STIMED_WAIT_EXPIRED(__stw_expired_2)

#define DECLARE_G_SEMAPHORE_EX_2         _DECLARE_G_SEMAPHORE(_g_sem_3)
#define G_SUNLOCK_EX_2                     _G_SUNLOCK(_g_sem_3)
#define G_STIMED_WAIT_EX_2(seconds)     _G_STIMED_WAIT\
                            (seconds, _g_sem_3, __stw_expired_3, __stimeout_3)
#define G_STIMED_WAIT_2_EX_2(seconds)    _G_STIMED_WAIT_2\
                            (seconds, _g_sem_3, __stw_expired_3, __stimeout_3)
#define G_STIMED_WAIT_EXPIRED_EX_2        _G_STIMED_WAIT_EXPIRED(__stw_expired_3)


DECLARE_G_SEMAPHORE
DECLARE_G_SEMAPHORE_EX
DECLARE_G_SEMAPHORE_EX_2

void initGlobalSemaphores()
{
    sem_init(&_g_sem_, 0, 0);
    sem_init(&_g_sem_2, 0, 0);
    sem_init(&_g_sem_3, 0, 0);
}

GThread* _g_thread_create(GThreadFunc func, gpointer data, gboolean joinable,
            GError **error)
{
    return g_thread_create_full(func, data, 0, joinable, FALSE,
                    G_THREAD_PRIORITY_NORMAL, error);
}

GMainLoop* mlrMainLoop = NULL;
GMainContext* mlrMainContext = NULL;

gboolean mlr_callback(gpointer data){G_STIMED_WAIT(10000); return TRUE;}
gboolean mlr_prepare(GSource    *source, gint       *timeout_){return TRUE;}
gboolean mlr_check(GSource    *source){return TRUE;}
gboolean mlr_dispatch(GSource    *source, GSourceFunc callback, gpointer
                                user_data){return callback(user_data);}

GSourceFuncs mlr_funcs =
{
  mlr_prepare,
  mlr_check,
  mlr_dispatch,
  NULL
};

int mlr_status[2] = {0, 0};

gpointer mlr_loop_main(gpointer data)
{
    mlrMainContext = g_main_context_new();
    if(mlrMainContext == NULL)
    {
        mlr_status[0]  = -1;
        return NULL;
    }
    mlrMainLoop = g_main_loop_new(mlrMainContext, FALSE);

    if(mlrMainLoop == NULL)
    {
        mlr_status[0]  = -2;
        return NULL;
    }

    GSource *mlr_source = (GSource*)g_source_new(&mlr_funcs, sizeof (GSource));

    if(mlr_source == NULL)
    {
        mlr_status[0]  = -3;
        return NULL;
    }

    g_source_set_callback (mlr_source, mlr_callback, NULL, NULL);
    g_source_attach(mlr_source, mlrMainContext);
    g_source_unref(mlr_source);

    mlr_status[0] = 1;
    G_SUNLOCK_EX;

    g_main_loop_run(mlrMainLoop);

    mlr_status[0] = 2;
    G_SUNLOCK_EX;
    return NULL;
}

gpointer mlr_loop_2nd(gpointer data)
{
    mlr_status[1] = 1;

    g_main_loop_run(mlrMainLoop);

    mlr_status[1] = 2;
    G_SUNLOCK_EX_2;
    return NULL;
}


GMainContext *chk_mainContext;

int main()
{

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

    initGlobalSemaphores();

    g_thread_create(mlr_loop_main, NULL, TRUE, NULL);

    //Waiting for the first thread to wrap up the initialization
    G_STIMED_WAIT_EX(2);

    g_thread_create(mlr_loop_2nd, NULL, TRUE, NULL);
    g_usleep(3000000);
    g_main_loop_quit(mlrMainLoop);

    //Waiting for the second thread to finish
    G_STIMED_WAIT_EX_2(1);

    G_SUNLOCK

    //Waiting for the first thread to finish
    G_STIMED_WAIT_2_EX(1);

    if(mlrMainLoop)
        g_main_loop_unref(mlrMainLoop);
    if(mlrMainContext)
        g_main_context_unref(mlrMainContext);

    return 0;
}