
#ifndef THREAD_PTHREAD_H_INCLUDED
#define THREAD_PTHREAD_H_INCLUDED

#include <pthread.h>
typedef pthread_t yarv_thread_id_t;
typedef pthread_mutex_t yarv_thread_lock_t;

#define native_mutex_lock   pthread_mutex_lock
#define native_mutex_unlock pthread_mutex_unlock
#define native_mutex_trylock pthread_mutex_trylock

typedef struct native_thread_data_struct {
    void *signal_thread_list;
} native_thread_data_t;

#endif /* THREAD_PTHREAD_H_INCLUDED */


/**********************************************************************/

#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION

#define native_mutex_initialize(lock) do { \
  pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER; \
  ((*lock) = _lock); \
} while (0)

#define native_cleanup_push pthread_cleanup_push
#define native_cleanup_pop  pthread_cleanup_pop
#define native_thread_yield() sched_yield()

static void yarv_add_signal_thread_list(yarv_thread_t *th);
static void yarv_remove_signal_thread_list(yarv_thread_t *th);

static yarv_thread_lock_t signal_thread_list_lock;

static void
null_func()
{
}

static void
Init_native_thread()
{
    GET_THREAD()->thread_id = pthread_self();
    native_mutex_initialize(&signal_thread_list_lock);
    posix_signal(SIGVTALRM, null_func);
}

NOINLINE(static int
	 thread_start_func_2(yarv_thread_t *th, VALUE *stack_start));
void static thread_cleanup_func(void *th_ptr);

static void *
thread_start_func_1(void *th_ptr)
{
    yarv_thread_t *th = th_ptr;
    VALUE stack_start;
    /* ignore self and klass */

    native_cleanup_push(thread_cleanup_func, th);

    /* run */
    thread_start_func_2(th, &stack_start);
    thread_cleanup_func(th);
    native_cleanup_pop(0);
    return 0;
}

static void make_timer_thread();

static int
native_thread_create(yarv_thread_t *th)
{
    pthread_attr_t attr;
    size_t stack_size = 4 * 1024 - sizeof(int);	/* 4KB */
    int err;
    static int init = 0;

    if (!init) {
	make_timer_thread();
    }

    thread_debug("create: %p, stack size: %ld\n", th, stack_size);

    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, stack_size);
    pthread_attr_setdetachstate(&attr, 1);
    
    err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
    
    if (err != 0) {
	th->status = THREAD_KILLED;
	rb_raise(rb_eThreadError, "can't create Thread (%d)", err);
    }
    return err;
}

static void
native_thread_apply_priority(yarv_thread_t *th)
{
    struct sched_param sp;
    int policy;
    int priority = 0 - th->priority;
    int max, min;
    pthread_getschedparam(th->thread_id, &policy, &sp);
    max = sched_get_priority_max(policy);
    min = sched_get_priority_min(policy);

    if (min < priority) {
	priority = max;
    }
    else if (max > priority) {
	priority = min;
    }

    sp.sched_priority = priority;
    pthread_setschedparam(th->thread_id, policy, &sp);
}

static void
native_thread_send_interrupt_signal(yarv_thread_t *th)
{
    thread_debug("native_thread_send_interrupt_signal (%p)\n", th->thread_id);
    if (th) {
	pthread_kill(th->thread_id, SIGVTALRM);
    }
}

void
native_thread_interrupt(yarv_thread_t *th)
{
    yarv_add_signal_thread_list(th);
}

struct yarv_signal_thread_list {
    yarv_thread_t *th;
    struct yarv_signal_thread_list *prev;
    struct yarv_signal_thread_list *next;
};

static struct yarv_signal_thread_list signal_thread_list_anchor = {
    0, 0, 0,
};

#define FGLOCK(lock, body) do { \
    native_mutex_lock(lock); \
    { \
	body; \
    } \
    native_mutex_unlock(lock); \
} while (0)

static void
print_signal_list(char *str)
{
    struct yarv_signal_thread_list *list =
      signal_thread_list_anchor.next;
    thread_debug("list (%s)> ", str);
    while(list){
	thread_debug("%p (%p), ", list->th, list->th->thread_id);
	list = list->next;
    }
    thread_debug("\n");
}

static void
yarv_add_signal_thread_list(yarv_thread_t *th)
{
    if (!th->native_thread_data.signal_thread_list) {
	FGLOCK(&signal_thread_list_lock, {
	    struct yarv_signal_thread_list *list =
	      xmalloc(sizeof(struct yarv_signal_thread_list));
	    list->th = th;

	    list->prev = &signal_thread_list_anchor;
	    list->next = signal_thread_list_anchor.next;
	    if (list->next) {
		list->next->prev = list;
	    }
	    signal_thread_list_anchor.next = list;
	    th->native_thread_data.signal_thread_list = list;
	});
    }
}

static void
yarv_remove_signal_thread_list(yarv_thread_t *th)
{
    if (th->native_thread_data.signal_thread_list) {
	FGLOCK(&signal_thread_list_lock, {
	    struct yarv_signal_thread_list *list =
	      (struct yarv_signal_thread_list *)
		th->native_thread_data.signal_thread_list;

	    list->prev->next = list->next;
	    if (list->next) {
		list->next->prev = list->prev;
	    }
	    th->native_thread_data.signal_thread_list = 0;
	    list->th = 0;
	    xfree(list);
	});
    }
    else {
	/* */
    }
}

static pthread_t time_thread;

static void timer_function(void);

static void *
thread_timer(void *dummy)
{
    while (system_working) {
#ifdef HAVE_NANOSLEEP
	struct timespec req, rem;
	req.tv_sec = 0;
	req.tv_nsec = 10 * 1000;	/* 10 ms */
	nanosleep(&req, &rem);
#else
	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 10000;     	/* 10 ms */
	select(0, NULL, NULL, NULL, &tv);
#endif
	
	if (signal_thread_list_anchor.next) {
	    FGLOCK(&signal_thread_list_lock, {
		struct yarv_signal_thread_list *list;
		list = signal_thread_list_anchor.next;
		while (list) {
		    native_thread_send_interrupt_signal(list->th);
		    list = list->next;
		}
	    });
	}

	timer_function();
    }
    return NULL;
}

static void
make_timer_thread()
{
    if (!time_thread) {
	pthread_create(&time_thread, 0, thread_timer, 0);
    }
}

#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
