/*  finalizer.c -- daemon thread that runs finalizers  */

#include <assert.h>

#include "toba.h"
#include "sthreads.h"
#include "runtime.h"
#include "java_lang_Thread.h"

struct queue_node {
    struct queue_node *prev_link;
    struct queue_node *next_link;
    Object object;
    void (*func)(void *);
};

struct locked_queue {
    struct queue_node *head;
    struct queue_node *tail;
    struct sthread_mutex lock;
    int length;
};

/* queue of objects to finalize */
static struct locked_queue finalize_q;

/* 
 * free list of queue nodes 
 */
static struct locked_queue free_list;

#if 0 /* UNUSED */
static struct in_java_lang_Thread *create_daemon_thread(void (*f)(), char *nm);
static void start_daemon_thread(struct in_java_lang_Thread *thr);
#endif /* 0 */

static void
enqueue(struct locked_queue *q, struct queue_node *node)
{
    sthread_mutex_lock(&q->lock);
    if(q->head)
        q->head->prev_link = node;
    else
        q->tail = node;
    node->prev_link = 0;
    node->next_link = q->head;
    q->head = node;
    q->length++;
    sthread_mutex_unlock(&q->lock);
}

static struct queue_node *
dequeue(struct locked_queue *q)
{
    struct queue_node *node;

    sthread_mutex_lock(&q->lock);
    node = q->tail;
    if(node) {
        if(node->prev_link)
            node->prev_link->next_link = 0;
        else
            q->head = 0;
        q->tail = node->prev_link;
        q->length--;
    }
    sthread_mutex_unlock(&q->lock);

    return node;
}

static void
locked_queue_init(struct locked_queue *q) 
{
    q->head = 0;
    q->tail = 0;
    sthread_mutex_init(&q->lock);
    q->length = 0;
}

void
finalization_init(void)
{
    locked_queue_init(&finalize_q);
    locked_queue_init(&free_list);
}

static struct queue_node *
get_free_node(struct mythread *thr)
{
    struct queue_node *ret;

    sthread_dontkill_start(thr);
    ret = dequeue(&free_list);
    sthread_dontkill_end(thr);

    /* 
     * a node gets put on the free queue whenever
     * a finalizer is created so we should never have
     * a failure here
     */
    assert(ret);
    return ret;
}

void
finalizer_queue_prealloc()
{
    struct mythread *thr = mythread();
    struct queue_node *n;

    n = (struct queue_node *)allocate(sizeof(*n));

    sthread_dontkill_start(thr);
    enqueue(&free_list, n);
    sthread_dontkill_end(thr);
}

/*
 * add an object to the finalization queue
 */
void
needs_finalization(Object obj, void (*func)(void *))
{
    struct queue_node *new;
    struct mythread *thr = mythread();

    /* thr being null here isn't a problem as dontkill_start and 
     * end are all we use it for, and they don't care. */

    /* calling allocate here could cause infinite recursion
     * so we pre-allocate the nodes (when finalizers are registered)
     * and just grab them from a list here
     */
    new = get_free_node(thr);
    new->object = obj;
    new->func = func;

    /* put it on the queue under protection of the queue lock */
    sthread_dontkill_start(thr);
    enqueue(&finalize_q, new);
    sthread_dontkill_end(thr);
}

void 
run_some_finalizers(struct mythread *thr, int num) 
{
    struct queue_node *node;
    jmp_buf newbuf;
    void *oldbuf;

    /* Bail out as fast as possible */
    if (!finalize_q.length)
	return;

    /* If we get to decide how many to run, then 
       we will run ceil(queue_len/2) - if the queue is long
       we want to run many, and we always want to run at 
       least 1. */
    if (num == 0)
	num = finalize_q.length / 2 + 1;

    /* ignore any uncaught exceptions in finalizers */
    oldbuf = thr->jmpbuf;
    if(setjmp(newbuf)) {
        /*
         * we bail after the first exception
         * we have run at least one finalizer 
         */
        sthread_got_exception();
        thr->jmpbuf = oldbuf;
        return;
    }
    thr->jmpbuf = newbuf;
    while (finalize_q.head && num && !sthread_dontkill_p(thr)) {
        num--;
        sthread_dontkill_start(thr);
        node = dequeue(&finalize_q);
	sthread_dontkill_end(thr);

	node->func(node->object);
        node = 0;
    }
    thr->jmpbuf = oldbuf;
}

#if 0 /* UNUSED */
/* unused right now but of general utility */
static struct in_java_lang_Thread *
create_daemon_thread(void (*startfunc)(), char *name)
{
    struct in_java_lang_Thread *t;

    t = (struct in_java_lang_Thread *)new(&cl_java_lang_Thread.C);
    t->class->M.run__QJ0S5.f = startfunc;
    t->class->M.init_S_RX9z2.f(t, javastring(name));
    t->class->M.setDaemon_z_dnLYB.f(t, JAVA_TRUE);

    return t;
}
static void 
start_daemon_thread(struct in_java_lang_Thread *th)
{
    th->class->M.start__byA09.f(th);
}
#endif /* 0 */
