% vim: ts=4 sw=4 expandtab ft=mercury
%
% Test the RTS' weak pointer support.
%
% Running this with GC_PRINT_STATS=1 GC_PRINT_VERBOSE_STATS=1 in the
% environment will show that this support is working.
%
% The output of this test is extreamly volatile. It depends on the garbage
% collector's state (which objects get collected when) which can depend on
% a lot of other things. Therefore we don't compare it's output with expected
% output. The best we can do is test that it exits cleanly.

:- module weak_ptr.
:- interface.
:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

main(!IO) :-
    test(!IO).

:- pragma foreign_decl("C", local,
"
#include \"gc.mh\"

struct list {
    int             item;
    struct list*    next;
    MR_weak_ptr     prev;
};

typedef struct list list;

static list* build_list(void);

static void traverse_forwards(list* list);

static void traverse_backwards(list* list);

static list* get_tail(list* list);

static list* drop(int n, list* list);
").

:- pragma foreign_code("C",
"
static list*
build_list(void)
{
    list*   head;
    list*   cur;
    list*   prev;
    int     i;

    cur = MR_GC_malloc(sizeof(struct list));
    cur->item = 0;
    cur->next = NULL;
    cur->prev = MR_NULL_WEAK_PTR;
    prev = cur;
    head = cur;

    for (i = 1; i < 10000; i++) {
        cur = MR_GC_malloc(sizeof(struct list));
        cur->item = i;
        cur->next = NULL;

        prev->next = cur;
        MR_new_weak_ptr(&(cur->prev), prev);

        prev = cur;
    }

    /* Help the GC */
    cur = NULL;
    prev = NULL;

    return head;
}

static void
traverse_forwards(list* cur)
{
    printf(\"Forwards: \");

    while (NULL != cur) {
        printf(\"%d \", cur->item);
        cur = cur->next;
    }

    printf(\"\\n\");
}

static void
traverse_backwards(list* cur)
{
    printf(\"Backwards: \");

    while (NULL != cur) {
        printf(\"%d \", cur->item);
        cur = MR_weak_ptr_read(&(cur->prev));
    }

    printf(\"\\n\");
}

static list*
get_tail(list* cur)
{
    list *prev = NULL;

    while (NULL != cur) {
        prev = cur;
        cur = cur->next;
    }

    return prev;
}

static list*
drop(int n, list* cur)
{
    int i;

    for (i = 0; (i < n) && (cur != NULL); i++) {
        cur = cur->next;
    }

    return cur;
}
").

:- pred test(io::di, io::uo) is det.

:- pragma foreign_proc("C",
    test(_IO0::di, _IO::uo),
    [will_not_throw_exception, thread_safe, promise_pure],
"
    list*   list_head;
    list*   list_tail;

    ML_garbage_collect();

    list_head = build_list();
    ML_garbage_collect();

    list_tail = get_tail(list_head);
    traverse_forwards(list_head);
    ML_garbage_collect();

    traverse_backwards(list_tail);
    ML_garbage_collect();

    list_head = drop(9000, list_head);
    ML_garbage_collect();
    traverse_forwards(list_head);
    traverse_backwards(list_tail);

").

test(!IO) :-
    % We provide a weak_ptr.exp2 file so that the test passes in non C grades.
    % Java and C# provide their own weak pointer code that we do not need
    % to test as part of the Mercury test suite.
    io.write_string("Test not supported in this grade.\n", !IO).
