Notes on using BASIClib's Thread support:

BASIClib is (hopefully) threadsafe, and has an abstraction layer for
creating and managing threads. Here's all you need to know:

- For reference, the thread abstraction layer is defined in
  BASIClib/Threads.c ... The current implementation uses POSIX threads, but
  there's not necessarily a need to concern yourself with the underlying
  library. As long as a threads package can fulfill the rather undemanding
  needs of BASIClib's API, it can be fitted as the backend. POSIX just seems
  like the best way to go, but BASIClib as a whole is not at all chained to it.

- There're NO POSIX threads on Win95. Cygnus is working on this, but god
  knows how long it will take them to write a POSIX compliant pthreads
  package AND make all their libraries thread-safe. WinNT has some DCE
  threads libraries (older draft of POSIX standard) bundled with the OS,
  but this is not 100% compatible with Linux (read: POSIX standard) threads.
  This isn't a problem, necessarily. We can use the Win32 API directly, as
  the vbSlacker Threads subsystem is pretty abstract, but we need a
  thread-safe C library. Apparently, the latest code shapshots of cygwin32
  have the needed support, but after months of existance, STILL have not
  made it into an official beta release. Microsoft Visual C++ has a thread-safe
  library, but no POSIX support. I might write POSIX wrappers for Win32 threads
  someday...

- You should #include "StdBasic.h" in any modules that use threads in
  anyway. Do not directly include Threads.h, as it expects to be included
  from StdBasic.h ... #including "BasicLib.h" will also suffice. Do not
  include both.

- For single-threaded programs and modules, you may define SINGLE_THREADED
  ON THE COMMAND LINE when compiling your code. This replaces all Thread API
  function calls with do-nothing "++ safe" macros. This allows you to use
  identical (but more efficient) code for both single-threaded and multi-
  threaded versions of your program. (For example, __getCurrentThreadIndex
  is replaced with (0) (and no function call) when SINGLE_THREADED is
  defined, since you'd always be dealing with thread index (0) anyhow.)

- You need to, even for single-threaded code, define _REENTRANT.

  gcc -D_REENTRANT MyCode.c

  _REENTRANT changes the behavior of the C library headers to be thread safe.
  (Most notably, (errno) works in a downright wrong way if you don't use this.)
  YOU SHOULD DEFINE _REENTRANT EVEN IF YOU DON'T DIRECTLY USE THREADS.
  Threads could be calling your functions, even if you don't specifically
  create a thread do so. Including StdBasic.h and not defining _REENTRANT
  will cause an error, just as a friendly reminder. You are still on your
  own for thread-proofing your code, and should do so, even if in single
  threaded mode. (Since the performance hit is so infintestimal for the
  single-thread macros, but it will be ready for multithreaded apps.)

- Spin a thread like this:

  void myThreadEntryPoint(void *_args)
  {
      MyDataType *args = (MyDataType *) _args;
      /* do something. */
  } /* myThreadEntryPoint */

  void main(void)
  {
          /*
           * The NULL can be a pointer to anything you like. It will
           * be passed to myThreadEntryPoint() as the only argument...
           */
      __spinThread(myThreadEntryPoint, NULL);
  } /* main */

- Some thread libraries (like POSIX threads) allow for threads to be "joined",
   that is, terminating threads can return a value. This threads package
   does not allow for that (all threads run "detached"), but there are a
   thousand workarounds for this, and you'll probably never need a single one.

- Every thread has an index number (a "tidx," or "Thread Index"). The first
  thread, the one that runs the mainline, is index (0). The next thread spun
  is index (1). As threads are destroyed, their index numbers are recycled
  for new threads that are spun. You can find out the currently executing
  thread by calling __getCurrentThreadIndex(). It will return the index of
  the thread that called the function.

- Threads are destroyed automatically when they return. You can have one
  destroy itself by calling __terminateCurrentThread(). You can kill other
  threads, if you know its thread index, by calling __terminateThread(tidx)...
  calling __terminateThread() with the current thread's index will cause
  BASIClib to call __terminateCurrentThread() for you. It's that smart. :)

- Behavior is ALWAYS undefined if you try to manipulate threads with any
  functions other than BASIClib's threads API. Don't assume that the
  underlying code is a POSIX implementation, or even the WIN32 API, even
  if you are running under Windows...maybe we got witty and created
  a "green threads" package that runs multiple threads on one.

- Most (if not all) of your thread-bug problems will come from global and
  static variables. Expect to be thinking about how to protect that data.
  Remember that even thread-proofed data in a dynamically-allocated array
  isn't safe if you plan to reallocate that array at any point.

- Just about every other thread-bug comes from i/o to the same device/file
  simultaneously from two different threads. These are sometimes even harder
  to track, because crashes/unexpected behavior tend to occur inside the C
  library, which you probably aren't willing to trace into.  :)

- Almost every thread-bug can be prevented by correct use of ThreadLocks.
  Keep reading.

- There is no EnterCriticalThreadSection() API in the POSIX standard. Use
  BASIClib's ThreadLocks instead. This is an abstraction layer over POSIX
  mutexes. Like so:

  /* (at top of module:) */

  #include "StdBasic.h"
  ThreadLock myThreadLock;


  /* (in init code for module:) */

  __createThreadLock(&myThreadLock);


  /* (Whenever you need to protect data from race conditions...) */

  __obtainThreadLock(&myThreadLock);
  /* Mess with data... */
  __releaseThreadLock(&myThreadLock);


  /* (in deinit code for module:) */

  __destroyThreadLock(&myThreadLock);


  Try to group sensitive data by ThreadLocks, so we don't hog too much
  memory/processor time with a ThreadLock for every bit of data, but
  there are enough locks in the right places so there aren't any massive
  slowdowns while threads wait unnecessarily to obtain a lock. Use common
  sense. Too many locks == slow. Too few locks == slower, but more idling.
  No locks == inexplicable crashes.

- If you have to wait for a thread to terminate for any reason, you can call
  __waitForThreadToDie(tidx), and the calling thread will block until
  the thread with index (tidx) terminates, either by returning or suicide or
  another thread terminating it. Be careful whenever blocking is involved,
  like this. Two threads waiting for each other to die will be forever
  deadlocked.

- If your thread is looping for any amount of time, it might be wise to
  place a call to __timesliceThread in your loop. (Note this is a macro,
  so don't use "()"...) This call surrenders control of the processor so
  other threads and processes can use it. Eventually control will return
  to the caller. VERY handy when waiting endlessly for user input, or
  calculating a jazillion places of Pi.

- For accounting purposes, you can call __getThreadCount, (a macro) which
  returns a count of all threads in this process...although this isn't as
  useful as __getHighestThreadIndex, (a macro) which returns the highest
  thread index in use. This is good for allocating arrays that keep one
  element for each thread, since such arrays are inherently thread-safe
  (as long as we use ThreadLocks when reallocating the array.)

- To be notified when a thread is created, add a function to your module
  called (for example) "void __initThreadMyModule(int tidx)", and add a
  call to it inside of __initThread(int tidx) (found in Initialize.c).
  This new function will be called whenever a new thread is created, with
  tidx set to the thread index of the new thread. This will allow you to
  update thread-separated arrays and such. Be careful about reallocating
  arrays, since this function can be called at any time, so any access to
  those array elements will need to be protected by ThreadLocks.
  The newly created thread will be the one to call __initThread().

- The same rules apply for thread termination...add your notification
  function calls to __deinitThread(int tidx) in Initialize.c ... The
  thread that did the terminating (the one who called __terminateThread()
  or __terminateCurrentThread()) will be the one to call __deinitThread().

- Be careful about obtaining thread locks and error handling; If you own
  a thread lock, and there's a possibility of a runtime error being thrown,
  register an OnError handler that releases possession of the lock,
  and rethrows the error. Other protections include making sure you don't
  call any routines that could possibly throw (standard C library routines
  never do so, any BASIClib function may), or registering an OnError 
  handler that fixes problems, and executes a RESUME of some sort. If you
  don't take measures, other threads will block inexplicably and
  probably permenently if they try to obtain the lock that your error-throwing
  thread has possession of.

- The best idea is just to avoid global and static data wherever possible.
  Remember that every thread has it's own stack, and data stored on it will
  be separate from other functions...therefore local variables and arguments
  are thread-proofed already. This doesn't solve EVERY problem, but keep it
  in mind.

/* end of threads.txt ... */


