/* 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
<ag@muwiinfa.geschichte.uni-mainz.de, Dr.Graef@t-online.de>
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 <stdlib.h>
#include <stdio.h>
/* __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 <stdlib.h>
#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
syntax highlighted by Code2HTML, v. 0.9.1