Each thread has a specific job. Threads generate some work for each other and send a message to the relevant thread to get it done. There is no immediate acknowledgement or builtin request identifiers. For each class of threads, there is only one instance. i.e. threads are not used for doing many instances of the same work in parallel.The program reads a description file and generates one source file and one header file. The source file is independent of the header file, it already includes the same contents.
CCODE refers to a piece of text which is enclosed within balanced {} pairs. It can include properly terminated quotes and escape sequences can be used to encode special characters within quotes. However, the escape sequences are simply passed on, the program doesn't interpret them in any way.
Some CCODE values have a special marker in them. This marker is later replaced by some string, such as the name of a variable or struct field. The marker character is @. This character is processed whether it's inside a quote or not. When such a marker is required the associated token will be called MCODE.
FILENAME is a sequence of non-whitespace characters. No quotes are necessary and are included as part of the file name if present.
typeThe name is a simple C identifier. MCODE1 tells the program how to declare a variable or struct field of the given type. or type dontfree @ is used to mark the place where the identifier should go. Here are some examples:
{unsigned char @} {void (*@)(int,int)} {int @[]}Note that the program includes only one C type by default: int. MCODE2 is used for destroying a variable of the given type. The marker is used for marking the places where the relevant expression should be put. When you send a message to a thread, that thread executes the relevant function. After that, the generated code destroys the arguments. If you give dontfree as the destruction code, then the respective argument doesn't get deallocated. Otherwise code in MCODE2 is used. Here are some complete type declarations:
type tempstr {char *@} {free(@};} type byte {unsigned char @} dontfree type node {struct node *@} {free(@->data); free(@->key);}Threads consist of function declarations and thread properties. The top level declaration of a thread is as follows:
thread NAME { properties-and-functions }There is only one property currently, and it's an optional one:
free_running FUNCTION_NAME
The rest of the thread definitions should consist of function declarations:
functionTYPEi is a type name declared using the type directive. ARGi is a simple C identifier. A function can have any number of arguments, including zero. ( , , )
thread worker { function process(int a) }One function and two declarations will be generated:
// implement this function in your code // it will be executed from the worker thread void worker_process(int a); // this will be implemented by the generated // code. you call this function from another // thread to cause the worker thread to call // the above function. void worker_PROCESS(int a);It's that simple. You can also declare a thread to be free_running. Normally, a thread (other than the main thread) spends its time inside an event loop, waiting for messages. When a message comes in, it processes that message and then comes back for more.
A free running thread spends its time running the given function over and over again. Between these calls, it checks for a message from other threads and processes those if there is any. The function given to the free_running directive must have the prototype:
void
();
Free running threads have two states: paused and running. Initially, they start out in paused state and they must be resumed using the following function:
void itf_resume_
If you want to pause it from the main thread, or if you run out of
work to do in the free running function, you can pause the thread
using:
();
void itf_pause_
();
int itf_init_main();
void itf_init_
The init_main() function returns a file descriptor. I'll describe its
use in the next section.
();
When you're done with a thread, you can send a quit message to it. This also joins the thread, making sure that it's really dead before returning. Repeatedly quit()ing and init()ing a thread hasn't been tested. It may or may not work. This quit operation is done by:
void itf_quit_
This probably should be done from the main thread of the program since
it also tries to join with the relevant thread and I have no idea
whether it'd work from another.
();
// this function returns non-zero if there are pending messages // for the main thread int itf_pending_main(); // reads one message from the queue and executes it. void itf_process_main();The syntax for the main thread is the same as the other threads. The recognized name for it is main.
If you already use select() to monitor file descriptors in the main thread, you can use the file descriptor returned by itf_init_main() to see whether there is a pending message for main. You must not read from this file descriptor. itf_process_main() will stall if you do so.
touch *.cx && makeYou can then install the binary itf2 anywhere in your $PATH.