/*
    import.c -- Execute Lisp code from C-generated threads
*/

#include <stdlib.h>
#include <stdio.h>
#ifndef _MSC_VER
# include <unistd.h>
# include <pthread.h>
#endif

/*
 * GOAL:        To execute lisp code from threads which have not
 *              been generated by our lisp environment.
 *
 * ASSUMES:     ECL has been configured with threads (--enable-threads)
 *              and installed somewhere on the path.
 *
 * COMPILE:     Run "make" from the command line.
 *
 *
 * When this example is compiled and run, it generates a number of
 * threads, each one executing some interpreted code -- in this case
 * a bunch of PRINT statements.
 *
 * Importing other threads into lisp is possible if these threads have
 * been intercepted by the garbage collector. The way to do it is to
 * include the <ecl.h> on the source code that generates the threads,
 * as we do here. This takes care of replacing calls to phtread_create
 * or CreateThread (in unix and Windows respectively) with the
 * GC_pthread_create and GC_CreateThread functions.
 */

#include <ecl.h>

#ifdef _MSC_VER
#define sleep(x) Sleep((x)*1000)
#endif

static DWORD WINAPI
thread_entry_point(void *data)
{
        cl_object form = (cl_object)data;

        /*
         * This is the entry point of the threads we have created.
         * These threads have no valid lisp environment. The following
         * routine initializes the lisp and makes it ready for working
         * in this thread.
         */

        ecl_import_current_thread(ECL_NIL, ECL_NIL);

        /*
         * Here we execute some lisp code code.
         */
        cl_eval(form);

        /*
         * Finally, when we exit the thread we have to release the
         * resources allocated by the lisp environment.
         */
        ecl_release_current_thread();

        return 1;
}


int main(int narg, char **argv)
{
        HANDLE code;
        DWORD child_thread;
        int i;
        cl_object sym_print;

        /*
         * First of all, we have to initialize the ECL environment.
         * This should be done from the main thread.
         */
        cl_boot(narg, argv);

        /*
         * Here we spawn 10 threads using the OS functions. The
         * current version is for Unix and uses pthread_create.
         * Since we have included <gc.h>, pthread_create will be
         * replaced with the appropiate routine from the garbage
         * collector.
         */
        sym_print = c_string_to_object("PRINT");
        for (i = 0; i < 10; i++) {
                cl_object form = cl_list(2, sym_print, MAKE_FIXNUM(i));
                code = (HANDLE)CreateThread(NULL, 0, thread_entry_point, form, 0,
                                            &child_thread);
                if (code == NULL) {
                        printf("Unable to create thread. Code: %d\n", GetLastError());
                        exit(1);
                }
        }

        /*
         * Here we wait for the last thread to finish.
         */
        sleep(10);

        return 0;
}
