/*
Copyright (C) 2001-2003, The Perl Foundation.
$Id: tqueue.pmc 16275 2006-12-28 01:20:44Z particle $

=head1 NAME

src/pmc/tqueue.pmc - Threadsafe Queue

=head1 DESCRIPTION

Threadsafe queue class for inter thread communication. If you have an
unthreaded program then please use a PerlArray.

     new P0, .TQueue
     push P0, some
     new P2, .ParrotThread
     ...

and in other thread (at least, when shared PMCs work :)

     shift P1, P0

Note: The TQueue must always be emptied before program exit.

=head2 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"
#include <assert.h>

pmclass TQueue need_ext is_shared {

/*

=item C<void init()>

Initializes the queue.

=cut

*/

    void init() {
        PMC_int_val(SELF) = 0;
        PMC_data(SELF) = queue_init(0);
        PObj_custom_mark_destroy_SETALL(SELF);
    }

/*

=item C<PMC *clone()>

Returns the queue itself. No copy is made.

=cut

*/

    PMC* clone() {
        /* XXX fake a shared PMC */
        return SELF;
    }

/*

=item C<void mark()>

Marks all the threads in the queue as live.

=cut

*/

    void mark() {
        QUEUE *queue = PMC_data(SELF);
        QUEUE_ENTRY *entry;

        queue_lock(queue);
        entry = queue->head;
        while (entry) {
            pobject_lives(INTERP, (PObj*) entry->data);
            if (entry == queue->tail)
                break;
            entry = entry->next;
        }
        queue_unlock(queue);
    }

/*

=item C<void destroy()>

Destroys the queue.

=cut

*/

    void destroy() {
        if (PMC_data(SELF)) {
            QUEUE *queue = PMC_data(SELF);
#if 0
            /*
             * wait til queue is empty
             * XXX implement a time wait and PANIC if queue
             * isn't empty after some TIMEOUT
             */
            while (SELF.elements()) {
                queue_lock(queue);
                queue_wait(queue);
                queue_unlock(queue);
            }
#endif
            mem_sys_free(queue);
            PMC_data(SELF) = NULL;
        }
    }

/*

=item C<INTVAL defined()>

Returns whether there are any threads in the queue.

=cut

*/

    INTVAL defined() {
        return SELF.get_integer() != 0;
    }

/*

=item C<INTVAL get_integer()>

=cut

*/

    INTVAL get_integer() {
        INTVAL items;
        QUEUE *queue = PMC_data(SELF);

        items = PMC_int_val(SELF);
        return items;
    }

/*

=item C<INTVAL elements()>

Returns the number of threads in the queue.

=cut

*/

    INTVAL elements() {
        return SELF.get_integer();
    }

/*

=item C<void push_pmc(PMC *item)>

Adds the thread C<*item> to the end of the queue.

=cut

*/

    void push_pmc(PMC *item) {
        QUEUE_ENTRY* entry = mem_sys_allocate(sizeof (QUEUE_ENTRY));
        QUEUE *queue = PMC_data(SELF);

        /*
         * if item isn't shared nor const, then make
         *       a shared item
         */
        if (!(item->vtable->flags &
                    (VTABLE_IS_CONST_FLAG | VTABLE_IS_SHARED_FLAG)))
            VTABLE_share(INTERP, item);

        DOD_WRITE_BARRIER(INTERP, SELF, NULL, item);
        entry->data = item;
        entry->type = QUEUE_ENTRY_TYPE_NONE;
        /* s. tsq.c:queue_push */
        queue_lock(queue);
        ++PMC_int_val(SELF);
        /* Is there something in the queue? */
        if (queue->tail) {
            queue->tail->next = entry;
            queue->tail = entry;
        }
        else {
            queue->head = entry;
            queue->tail = entry;
        }
        queue_broadcast(queue);        /* signal all waiters */
        queue_unlock(queue);
    }

/*

=item C<PMC *shift_pmc()>

Removes the first thread from the start of the queue.

=cut

*/

    PMC* shift_pmc() {
        QUEUE_ENTRY *entry;
        QUEUE *queue = PMC_data(SELF);
        PMC *ret;

        queue_lock(queue);
        while (queue->head == NULL) {
            queue_wait(queue);
        }
        entry = nosync_pop_entry(queue);
        --PMC_int_val(SELF);
        queue_unlock(queue);
        ret = entry->data;
        mem_sys_free(entry);
        return ret;
    }
}

/*

=back

=head1 HISTORY

2003.12.19 leo initial rev.

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
