Introduction To YAAF Thread Support
WARNING! Thread support in YAAF currently is fairly primitive and is one of the more fragile parts of the YAAF toolkit. While it does work well enough for incorporation into shipping products, it does need some work to make better.
Each environment that YAAF runs on (Macintosh, Windows, Unix) provides some form of thread support. The purpose of this class is to wrapper the process of creating and controlling threads into a common API. This is harder than it seems: on the Macintosh the Thread Manager is cooperative (meaning you must explicitly yield time to other threads), while the Pthread API on Unix doesn't seem to provide a mechanism for controlling how threads are scheduled. (Or so I have found; if I'm wrong, correct the code and send me a patch!)
The XGThread class on YAAF takes a "least common denominator" approach. By encapsulating the concept of a thread in a separate class, I hope to at least provide some of the basic functionality necessary to create 'background' processing threads.
Creating and Using Threads
A thread is created by creating an XGThread object with a pointer to a function that the thread should execute. Note that for maximal portability, there are some restrictions on what a thread can and cannot do.
Threads may not participate in providing user interface elements, such as windows, dialogs and the like. Threads are only for providing background processing for tasks. If a thread must communicate it's state to the foreground, you should set up mechanism for sending and receiving messages, such as storing the state of a thread in a regularly polled global variable. (This is because Windows 95 and Windows NT requires that threads which bring forth their own UI elements must manage them with it's own event loop, and that confuses the YAAF event loop.)
In order to permit processing time to be given to other threads, you must periodically call XGThread::YieldThread() inside your own thread. XGThread::YieldThread() provides a flag which indicates if the thread is waiting for I/O in a 'polling loop', or if the 'YieldThread' call is being made as a courtesy to other threads. (This is because of the two thread APIs on the Macintosh, the Thread Manager places the least restrictions on what a thread can do--but threads are also required to be cooperative. On the Macintosh, if all threads indicate they are waiting for I/O, the main event loop will give more time to other applications by calling WaitNextEvent() with a larger parameter.)
Once a thread is created, the XGThread object is owned by the thread itself. That is, when the thread exits, the XGThread object representing that thread is destroyed. In order to prevent this (for example, you wish to control a thread's state during thread execution), you must create the thread inactive and then explicitly attach to it while the thread is in the 'inactive' state. For example:
// create suspended thread thread = new XGThread(MyThreadProc,arg,0,true); // increment the 'attach' count thread->Attach(); // now run the thread thread->Resume();
This guarentees that the thread's 'attach' count is incremented, even in the (somewhat rare) situation when the unattached thread would execute and complete before the 'thread->Attach()' call is made.
Semaphores
There are several mechanisms out there for providing serialized resource access to important resources on a system. YAAF provides the XGSemaphore object to provide this sort of access.
An XGSemaphore contains a count and an 'enter' and 'leave' method. When the semaphore count reaches zero, calls to 'enter' will wait until another thread calls the 'leave' method first. This provides a means to permit only a limited number of threads access to a particular resource, and also provides a mechanism for providing more complex controlled resource access.
Much of the time, a semaphore is used to control access to a resource to a single thread. For example, consider a routine which permits records to be read and written from a file. If more than one thread is currently trying to read and write data to the same record in that file at the same time, things can be terribly confusing. But by providing a semaphore to serialize access (by locking that resource on entering a subroutine and unlocking on existing that routine), things aren't quite as confusing and prone to trouble.
Because semaphore locking and unlocking often happens on entering and exiting a subroutine or method, the XGCritical class provides a stack-based (and exception-safe) mechanism for entering and leaving a critical section controlled by a semaphore.
Class Declarations
The following three threads comprise YAAF's built in thread support.
This class represents a thread. Constructing one of these constructs a separate process thread. This class can also be used to manipulate the thread once it has been created.
This class represents a semaphore, and can be used to build critical sections of code.
This is a stack-based class which invokes a semaphore's 'EnterCritical' and 'LeaveCritical' methods in an exception-safe manner.