/* This document has a minor changes made by
/* Dim Zegebart zager@post.comstar.ru
/* Changes was applied to the lwp_init(), lwp_spawn().
/* C++ support section also removed.
/* All changes marked by DZ comment.
/* Palantir v0.4

                               DISCLAMER
This document describes the terms for distributing the source code of and any
derived work based on the PDMLWP multitasking library. Such source code is
marked with the copyright notice:

 "Copyright (C) 1997 Paolo De Marino"

All the files marked with the above copyright fall either under LGPL
(Library General Public License) or under GPL (General Public License) as
stated at the beginning of each file, with the following exception, that ALL
the people in the THANKS file must receive credit. The example*.* files are
FREEWARE. You can do whatever you want with them.

    All these files are distributed in the hope that they 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
    and of the GNU Library General Public License along with this
    program; see the files COPYING and COPYING.LIB. If not, write to
    the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
    MA 02139, USA.

Enough legal stuff.

A couple of months ago, I found on the Net the excellent LWP
package written by Josh Turpen and Sengan Short. It provided basic
services such as starting a new thread, killing a thread, regulating
the time slice, and so on. But soon I discovered that it didn't provide
any synchronization facility between the MANY threads it could handle.
Thus I decided to add it a set of BASIC synchronization capabilities.
Anyhow, as I needed a C++ interface for a C++ program I was (am!) going
to write, I decided to add a complete set of objects to handle not only
synchronization, but also message posting, readers/writers locks, gates,
Thread objects to the thing.

So came out this package. It is basically divided in three sections:
  lwpasm.S (Capital "S"!)   The assembler stuff
  lwp.c                     The "C" part of the business

Here are brief descriptions of what each section does. Let's begin with
the "C" part, exactly with the original LWP functions (I've slightly
modified almost all of them, but I have left an identical interface).

lwp_init(irq, speed)

//DZ: You don't need to call this function by yourself. Instead of it call
palantir_init() at the top of your code. See palantir.txt for more details.


This function hooks all the stuff that needs to be hooked.  After you call
this function, main() is now operating as a task, but since it is the only
task you won't notice. Both timer IRQs are supported (0 and 8).  IRQ8 only
supports certain speeds (2hz, 4hz, 8hz, 16hz...8192hz, see lwp.h for more).
IRQ0 is programmable, and any speed can be implemented.  10hz < speed
< 1024hz is recommended. This function automagically cleans up after
itself when your program exits.

lwp *lwp_spawn(proc, stack_size,priority,void *args)

DZ: In original version of PDMLWP lwp_spawn() returns PID of spawned thread.
Palantir version returns lwp* of spawned thread.

This function spawns off other tasks.  Proc is the name of a function of
type void proc(void).  Stack size is used to create a stack that is
used by your proc.  It won't spawn if stack_size < 255.  I'd suggest at least
4k, just to be safe.  Your proc will lwp_kill() itself when it returns, so
there is no need to worry about threads joining. Priority specifies the
number of consecutive time slices the thread will have. Any number >0 will
fit.
PAY ATTENTION: lwp_yield is executed anyhow! It doesn't care about priorities.

Example:

void proc(void)
{
   printf("My proc is kewl!\n");
}
main()
{
   lwp_init(0,100);
   lwp_spawn(proc, 4096,1);
   /* do some stuff...*/
}

lwp_kill(lwpid)
This function kills a process.  Simple.  Pass it the pid of the process
and blahmo, it's dead.

lwp_getpid()
This returns the current process's pid, useful in killing the current process.
Example:

void proc1()
{
   ...do stuff
   lwp_kill(lwp_getpid());
   /* never returns */
}

Note:  Processes that return automagically kill themselves.

lwp_thread_disable()
This function disables task switching.  It's mostly used to fix
non re-entrant functions like printf, malloc, etc.

lwp_thread_enable()
This function enables task switching after a previous lwp_thread_disable().


lwp_thread_disable and lwp_thread_enable are not available, or needed,
in the cooperative version.

Example:

lwp_thread_disable();
printf("foo = %d\n", foo);
lwp_thread_enable();

Take a look at the lwpstdio.h, lwpconio.h, and lwppc.h that are included with
this distribution.  It takes care of SOME of libc's non-reentrant functions.
You don't need to wrap functions like printf with thread_disable and
thread enable, because it's automagically done in the included header files.
If there is a function you are using that isn't in one of the above header
files and you aren't sure that it is re-entrant, wrap it with thread_disable
and thread_enable, just to be safe.

lwp_yield()
This function causes a task switch to happen.  You might need to use
this here and there.

*Note*
This library uses the IRQ8 real time clock timer.  It interrupts at a rate
of 2hz..8192hz.  I chose this interrupt specifically for the reason
that nobody else uses it, so this tasking library should be compatible
with just about anything.  It took a lot of blood, sweat, and tears to
get it to work with IRQ8!  IRQ0 has also been implemented for its
programmable capabilities.


These were the services provided by LWP.

void lwp_sleep(unsigned int secs,unsigned short msecs)

Puts to bed your task for a given time, given at the precision of
1 thousandth of a second.
IMPORTANT: While the thread "sleeps", IT DOESN'T EAT UP CYCLES. It simply
won't receive time slices.

Example:

void periodic_check(void)
{
 while(!finished)
 {
  lwp_sleep(60*15,0);    /* Wait 15 minutes */

  autosave();
 }
}
This task wakes up every 15 minutes, and auto-saves the situtation.

void lwp_setuserptr(void *usrdata);
void *lwp_getuserptr(void);

These two member functions allow the user to set a per-thread user pointer,
that can be read/written by every single thread. This lets you write code
accessing differentl data depending on the thread it executes under, without
having to check the pid()s to find what to operate on.

void lwp_wait_true(volatile int *what)
void lwp_wait_false(volatile int *what)

These two functions play a very important role in mutitasking: they allow
a thread to wait for an integer to become true or false, without consuming
time slices. Their most immediate use is in the building of a gate, it is
of a "door" that can be opened or closed by another process.

Example:

volatile int trigger = 0;

void triggered_task(void)
{
 lwp_wait_true(&trigger);

... Does what it has to do...
}

void main()
{
 lwp_spawn(triggered_task,8192);

...Waits for some event...

 trigger = 1;     // Here it is. From this moment, the triggered_task
                  // can proceed!

 ... Does the rest of its stuff...
}

void lwp_pulse_true(volatile int *what)
void lwp_pulse_false(volatile int *what)

These two functions are useful to implement event raisers: they let any
thread waiting for "*what" to become true/false pass, WITHOUT CHANGING
THE VALUE OF *what! It is, they'll set all the threads NOW waiting free,
but they'll stop any other thread calling lwp_wait_true/false afterwards.

void lwp_wait_semaphore(lwp_semaphore *sema)
void lwp_init_semaphore(lwp_semaphore *sema)
int lwp_release_semaphore(lwp_semaphore *sema)

These are perhaps the most important functions in the package.
They allow a process to wait for a Mutex semaphore, i.e. for a semaphore that
can be owned by JUST ONE THREAD AT A TIME. Examples of such situations
are resources such as the keyboard, the screen: only one process at a time
may own one of these resources. Another kind of "resource" that needs to
be locked is data: it IS useful to implement locks on particular memory
areas that cannot be safely accessed by two threads at the same time.

Example:

lwp_semaphore sema;
int count = 0;

void thread(void)
{
 ... Does some stuff ...

 lwp_wait_semaphore(&sema); //Wait until semaphore is free
 count++;                   // Do something...
 lwp_release_semaphore(&sema); // Make the semaphore free again.

 ... Does some other stuff ...
}

void lwp_set_priority(unsigned priority)
Sets the priority at the value you specify for it.	Don't rely on this
function to protect the non-reentrant parts of your program: the priority
counter is set only at next task switch.

unsigned lwp_get_priority(void)
Returns the priority count, i.e. the number of consecutive time slices the
thread will receive.

WARNINGS:
This package is pretty stable: it has never crashed in its current version,
which I tested on my 486DX2/50. It doesn't eat up much memory, neither does
it eat too many cycles. BUT: there are lots of non-reentrant functions in
libc and in the C++ libraries, beginning with raise()/signal(), which means
that you cannot use exceptions, for they could be processed by ANY thread.
Another example of non-reentrant procs are all the I/O routines,
malloc,free,realloc (new and delete are non-reentrant too!). The true solution
to this problem would be rewriting all the libraries, but I don't have
enought time for such an enormous job. The only, obviously imperfect solution
is creating small patch header files, i.e. lwpconio.h, lwppc.h, lwpstdio.h,
lwpstdlib.h.

Mainly, PAY ATTENTION TO WHAT YOU DO! I have tried to shield the thing up
as much as I could, isolating operator new, operator delete, malloc,
realloc, free, calloc, cfree, but anyhow PAY ATTENTION to non-reentrant
routines, and remember THERE ARE MANY!.

Anyhow, if you have suggestions, ideas, comments, or just want to say hello,
don't hesitate to mail me.

Paolo De Marino (paolodemarino@usa.net)
Via Donizetti 1/E
80127 Naples
Italy
