/* libq.h: C interface library */ /* ALWAYS include gmp.h prior to this header when your module links to a static version of the GMP library. This is required to properly initialize GMP memory management in your module. */ /* Q eQuational Programming System Copyright (c) 1991-2001 by Albert Graef This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef LIBQ_H #define LIBQ_H 1 #include #include /* __declspec magic required to make Windows DLLs work */ #ifndef __DLL_BUILD #if defined _WIN32 #define __DLLIMPORT __declspec(dllimport) #define __DLLEXPORT __declspec(dllexport) #else #define __DLLIMPORT extern #define __DLLEXPORT #endif #endif /* WIN32 KLUDGE ALERT: Mingw and MSVC malloc() are not compatible. This may lead to memory corruption when using mingw- and msvc-compiled modules together. We redefine the memory management routines here to use routines provided by libq instead. */ #ifndef __DLL_BUILD #if defined _WIN32 #include #define malloc libq_malloc #define realloc libq_realloc #define calloc libq_calloc #define free libq_free #endif #endif /* support C++ */ #ifdef __cplusplus extern "C" { #endif __DLLIMPORT void *libq_malloc(size_t size); __DLLIMPORT void *libq_realloc(void *p, size_t size); __DLLIMPORT void *libq_calloc(size_t num, size_t size); __DLLIMPORT void libq_free(void *p); /* Q expression type (opaque). */ typedef void *expr; /* Declaration macros. */ /* The module header. */ #ifndef __GNU_MP_VERSION #define MODULE(id) static int __modno = -1; \ __DLLEXPORT void __ ## id ## __initmod(int modno) { __modno = modno; } #else #define MODULE(id) static int __modno = -1; \ __DLLEXPORT void __ ## id ## __initmod(int modno, \ void *(*gmp_allocate) (), void *(*gmp_reallocate) (), void (*gmp_free) ()) \ { \ __modno = modno; \ mp_set_memory_functions((void *(*) (size_t))gmp_allocate, \ (void *(*) (void *, size_t, size_t))gmp_reallocate, \ (void (*) (void *, size_t))gmp_free); \ } #endif /* User-defined module initialization/finalization code. */ #define INIT(id) \ __DLLEXPORT void __ ## id ## __init(void) #define FINI(id) \ __DLLEXPORT void __ ## id ## __fini(void) /* Declare functions and destructors. */ #define FUNCTION(id,name,argc,argv) __DLLEXPORT expr __F__ ## id ## _ ## name (int argc, expr *argv) #define DESTRUCTOR(id,name,ptr) __DLLEXPORT void __D__ ## id ## _ ## name (void *ptr) /* Call an external function in C. */ #define FUNCALL(id,name,argc,argv) __F__ ## id ## _ ## name(argc,argv) /* Special expr return values in case of error conditions. */ #define __FAIL NULL /* failure to apply dl function (no error) */ #define __ERROR __mkerror() /* error in dl function (aborts evaluation) */ /* Macros to access the symbol table. sym(name) returns the integer code for symbol name (without double quotes). type(name) does the same for type symbols. You can use these macros to pass symbol values to mksym() and mkobj(). */ #define sym(name) __getsym( #name , __modno) #define type(name) __gettype( #name , __modno) /* Predefined function and type symbols. */ __DLLIMPORT const int truesym, falsesym, nilsym, voidsym; __DLLIMPORT const int inttype, floattype, booltype, strtype, filetype, listtype, tupletype; /* Expression construction. */ /* Expressions are allocated dynamically on the interpreter's expression heap. If this fails, NULL is returned, and any dynamic argument values are collected using dispose(). You can use the constructed expressions as return values in interface functions, or as temporary values during the execution of an interface function. In the former case the return value will be evaluated by the interpreter automatically. In the latter case the values should be destroyed using dispose() (see below) before the interface function finishes. Note that it is generally *not* save to assume that a temporary expression persists after an external function invokation, since the garbage collector will collect all unreachable expression nodes, including temporaries created by hosted modules. */ /* Atomic objects: integers, real values, strings, files, and pipes created with popen(3). */ /* Note that mkint() and mkuint() can only be used to construct machine integers. For constructing arbitrary-sized integers from GMP mpz_t values, the mkmpz() routine is used. It takes a mpz_t value (cast to void*) as its argument. To avoid the overhead of taking an extra copy, this value is taken over by the interpreter, hence the calling program must _not_ manipulate the passed value afterwards. The mkmpz_float() function is a convenience function which creates an integer expression from a floating point number. */ __DLLIMPORT expr mkint(long i); __DLLIMPORT expr mkuint(unsigned long i); __DLLIMPORT expr mkmpz(void *z /* mpz_t z */); __DLLIMPORT expr mkmpz_float(double f); __DLLIMPORT expr mkfloat(double f); __DLLIMPORT expr mkstr(char *s); __DLLIMPORT expr mkfile(FILE *fp); __DLLIMPORT expr mkpipe(FILE *fp); /* Function (and variable) symbols and external objects. The mkbool() routine is a convenience function which creates a Bool value from an int (0 for false, nonzero for true). */ __DLLIMPORT expr mksym(int sym); __DLLIMPORT expr mkbool(int flag); __DLLIMPORT expr mkobj(int type, void *ptr); /* Applications, list and tuple construction ([|] resp. (|)). */ __DLLIMPORT expr mkapp(expr fun, expr arg); __DLLIMPORT expr mkcons(expr hd, expr tl); __DLLIMPORT expr mkcont(expr hd, expr tl); /* Create a list or tuple from a given element list. The mklistvl()/mktuplel() functions take the elements directly as parameters, while the mklistv()/ mktuplev() functions take an expression vector. The interpreter takes control of these vectors and will deallocate them when it is appropriate. */ __DLLIMPORT expr mklistl(int nelems, ...); __DLLIMPORT expr mklistv(int nelems, expr *elems); __DLLIMPORT expr mktuplel(int nelems, ...); __DLLIMPORT expr mktuplev(int nelems, expr *elems); /* Common types of constants defined as parameterless macros. */ #define mktrue mksym(truesym) #define mkfalse mksym(falsesym) #define mknil mksym(nilsym) #define mkvoid mksym(voidsym) /* Type checking and unboxing. exprsym() and exprtype() return the function and type symbol of an expression. The remaining functions are used to check for a specific kind of object, and return the corresponding component values. */ __DLLIMPORT int exprsym(const expr x); __DLLIMPORT int exprtype(const expr x); /* In analogy to the construction functions, there are several functions to check for integer objects. The isint() and isuint() functions will only return an integer value which fits into a machine integer. Arbitrary-sized integers can be obtained using the ismpz() function which returns an mpz_t value. This function overwrites the value of the given mpz_t variable, hence the variable must not be initialized. Also note that the value returned by ismpz() is owned by the interpreter and hence must not be modified by the calling program in any way. If necessary, you can make your own copy of the value using the GMP mpz_set() function. Finally, the ismpz_float() function returns an integer value as a floating point number, and isbool() returns a Bool value as an integer (0 for false, 1 for true). */ __DLLIMPORT int isint(const expr x, long *i); __DLLIMPORT int isuint(const expr x, unsigned long *i); __DLLIMPORT int ismpz(const expr x, void *z /*mpz_t z*/); __DLLIMPORT int ismpz_float(const expr x, double *f); __DLLIMPORT int isfloat(const expr x, double *f); __DLLIMPORT int isstr(const expr x, char **s); __DLLIMPORT int isfile(const expr x, FILE **fp); __DLLIMPORT int issym(const expr x, int sym); __DLLIMPORT int isbool(const expr x, int *flag); __DLLIMPORT int isobj(const expr x, int type, void **ptr); __DLLIMPORT int isapp(const expr x, expr *fun, expr *arg); __DLLIMPORT int iscons(const expr x, expr *hd, expr *tl); __DLLIMPORT int iscont(const expr x, expr *hd, expr *tl); /* The elems vector returned by istuple() is owned by the interpreter, and must *not* be manipulated in any way. This is again to save the overhead of creating an extra copy. */ __DLLIMPORT int istuple(const expr x, int *nelems, expr **elems); /* Check for the predefined constants. */ #define istrue(x) issym(x, truesym) #define isfalse(x) issym(x, falsesym) #define isnil(x) issym(x, nilsym) #define isvoid(x) issym(x, voidsym) /* Expression evaluation. eval() recursively invokes the interpreter on the given expression and returns the computed result (NULL if an error occurs or the evaluation is aborted). In any case, the argument (if it is different from the result) is collected using dispose(). Thus, after applying eval() to a temporary value, never use dispose() on the argument, but only on the evaluated result. */ __DLLIMPORT expr eval(const expr x); /* Garbage collection of temporary toplevel values (created with the construction functions or with eval()). You can safely apply this to any expression, since only top level temporaries will actually be destroyed. Component values of a toplevel expression will be collected automatically when they are not referred to any more. */ __DLLIMPORT void dispose(expr x); /* Multithreading support. As of Q 3.0, you can safely invoke eval() and the other libq operations in child threads created by a hosted module. For this to work, some amount of synchronization with the interpreter is required. First, you must invoke init_thread() from the new thread before invoking other libq operations. This function initializes some thread-local resources in the interpreter and obtains an internal mutex which allows the other libq operations to run in a safe context. It also returns the id under which the thread is known by the interpreter. (If the result is -1 then the operation was unsuccessful, possibly because the interpreter does not support multithreading. In this case it is *not* safe to call the other operations afterwards.) Second, you call eval() and/or whatever other calculations with the interpreter are required. Third, once you're finished with the interpreter, you must invoke the exit_thread() function with the id returned by init_thread() to release the internal mutex obtained with the init_thread() function, and to update some other internal bookkeeping information. After calling exit_thread(), it is *not* safe to call other libq operations any more. But you can still access the results of your calculations as the thread-local resources are still available. Fourth, you call fini_thread() with the id returned by init_thread() to release the thread-local resources associated with the new thread. This can be done from the new thread, but also from any other thread. Once this operation is completed, any trace of the new thread will have vanished from the interpreter. Note that the new thread, while performing an evaluation, is subject to preemption, since the interpreter will switch over to other waiting tasks in its internal execution loop. Moreoever, the thread may also be canceled during evaluation when the interpreter is about to exit or run a new script. Therefore you should provide an appropriate cleanup handler. At the very least, your cleanup handler must call exit_thread() in case the thread is canceled after init_thread() but before the thread reaches the exit_thread() call. Finally, if your module uses any internal synchronization objects, these should be handled at fork time with appropriate "atfork" handlers, see the POSIX threads manual for details. (Never call pthread_atfork yourself to install the handlers, but use the provided atfork() interface routine. This is necessary to prevent dangling function pointers if the module is unloaded by the interpreter.) Another issue arises with external operations which may block for extended or even indefinite periods of time. By default, the interpreter will only allow one external function to be entered at any time, hence such an operation may block other threads and the interpreter itself. To prevent this, the release_lock() and acquire_lock() operation can be used to temporarily release the lock on the interpreter's mutex, which lets the interpreter continue with another thread while your thread performs a lengthy or potentially blocking operation. These operations also establish cancellation points. Note that after releasing the mutex your thread continues to run in parallel with the interpreter, so the executed code must be thread-safe. In particular, you must not invoke any other libq operations after releasing the mutex, and you must do your own mutex locking on private globals if necessary. Before returning from the operation, you must *always* reacquire the mutex. The have_lock() function can be used to test whether the current thread currently holds the mutex. Synchronization with the interpreter is also required if you want to do terminal input, to prevent strange interactions with the readline library. A "tty" mutex is provided for that purpose. When you're about to read something from stdin you should acquire this mutex and release it when finished. Finally, when passing an (argument or temporary) expression to a newly created thread, you must ensure that the expression is not garbage collected by the main thread or some other thread while your thread is still executing. For this purpose, you can count a new reference to the expression using the newref() function, which will cause garbage collection to be deferred until you call freeref(). Note that each call to newref() counts a new reference, so the same number of freeref() invokations is required to actually free the expression. There also is an unref() operation which merely decreases the reference count without actually deallocating the expression. Note that you must make sure that the expression is actually freed *before* the thread terminates. */ /* thread initialization and termination */ __DLLIMPORT int init_thread(void); __DLLIMPORT void exit_thread(int id); __DLLIMPORT void fini_thread(int id); /* get the id of the current thread */ __DLLIMPORT int this_thread(void); /* enable background processing */ __DLLIMPORT int have_lock(void); __DLLIMPORT void release_lock(void); __DLLIMPORT void acquire_lock(void); /* stdin access */ __DLLIMPORT void acquire_tty(void); __DLLIMPORT void release_tty(void); /* defer garbage collection */ __DLLIMPORT expr newref(expr x); __DLLIMPORT expr unref(expr x); __DLLIMPORT void freeref(expr x); /* register pthread_atfork handlers (only one set of handlers can be installed for each module) */ #define thread_atfork(prepare,parent,child) \ __thread_atfork(prepare,parent,child,__modno) /* Internals (*never* access these directly!). */ __DLLIMPORT int __libq_init(); __DLLIMPORT expr __mkerror(void); __DLLIMPORT int __getsym(const char *name, int modno); __DLLIMPORT int __gettype(const char *name, int modno); __DLLIMPORT const char *__getpname(int sym, int modno); __DLLIMPORT void __thread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void), int modno); #ifdef __cplusplus } #endif #endif