int count=0;
void func(void) { ... Some stuff... do { CritSect sect; count++; } while(0); ... More stuff ... }
Example
MutexSemaphore sema; // Initializes itself on its own. void func(void) { do { MutexSemaphoreLock lock(sema); ... Do your private stuff with the semaphore locked... } while(0); ... Now semaphore is automagically unlocked... }But, as it is recursively lockable, even the following code would have worked without hanging the machine.
MutexSemaphore sema; void func(void) { do { MutexSemaphoreLock lock(sema); do_stuff(); } while(0); // The semaphore is unlocked only here, as one //might expect. } void do_stuff(void) { MutexSemaphoreLock lock(sema); // The semaphore was already locked! ... Whatever this function does... } // And is NOT unlocked here!
CountSemaphore monsters_sema; void monster(void *unused) { monsters_sema.Wait(); // Wait for ok from theGame() ...Boo! Eat the player! ... } main() { ... Multithreading is initialised, all your stuff is done... if(level == 1 ) // Spawn 10 (sleeping) monsters. { for(int i = 0; i < 10; i++) lwp_spawn(monster,(void*)0,8192,1); } else // Spawn 20 (sleeping) monsters. { for(int i = 0; i < 20; i++) lwp_spawn(monster,(void*)0,8192,1); }; ... At a certain point, you call the main game loop ... theGame(); } void theGame(void) { ... You want to materialize a certain number of monsters ... if(level == 1) { monsters_sema.Clear(); // Let one process pass on... } else { monsters_sema.Clear(2); // Wake up two processes! }; }
Example: (see the file test/example6.cc too)
If in that game you wanted to introduce a "pause" key, you could write:
Gate pauseGate(1); // Initialize it to be open! main() { ... Some stuff ... if( pause_key_pressed() ) { pauseGate.Close(); // Stop all the other game threads! while(!kbhit()) lwp_yield(); // Wait for another key pressure... pauseGate.Open(); // Re-open the gate } ... Rest of the game ... } void game(void) { ... There is some code... while(!game_over) { pauseGate.Wait(); // Wait for the gate to be opened, or pass on if it // is already open. ... Play ... }; } void monster(void) { ... There is some code to draw the monster ... while(alive) { pauseGate.Wait(); // Wait for the gate to be opened, or pass on // if it is already open. ... Move the monster ... }; ... Make the monster disappear ... }
See the file test/example9.cc for an example of using the Event class.
Interface to this class is very simple, as the example shows:
Example (see the file test/example5.cc)
ReadWriteSemaphore sema; int ToBeProtected; void thread1(void) { int tmp; ... Some stuff ... do { ReadLock lock(sema); tmp = ToBeProtected; } while(0); ... Rest of the stuff ... } void thread2(void) { int tmp; ... Some stuff ... do { WriteLock lock(sema); ToBeProtected++; } while(0); ... Rest of the stuff ... }ATTENTION: Readers/Writers lock ARE NOT FAIR LOCKS! If there are many writers, readers will probably starve, as Writers have precedence on Readers!
class MyThread : public Thread { public: MyThread(unsigned int stacksize) : Thread(stacksize) { }; virtual void prepare(void); // Starts as soon as the thread is created virtual void execute(void); // Starts when the thread is "start()"ed virtual void cleanup(void); // Starts when execute() returns }The three virtual methods prepare(), execute() and cleanup(), overloaded, provide the actual thread code. They default to doing nothing. The reason they are three is to make life easier to programmers. In fact, they are all called at very specific times.
But Thread object don't provide only these services: their most important role is allowing threads to exchange messages. A message is simply a pointer to void, that you "post()" to another thread. The Thread objects provides two methods to do it:
void postMessage(void *) and
static void postMessage(void *,Thread& dest).
The first can be used so:
myThread.postMessage("Hello!");Sends the message "Hello!" to the thread myThread. In the same way, you might call
Thread::postMessage("Hello",myThread);These two forms are synonyms.
Receiving a message is very simple too, but one thing must be understood: you send just void*, but you receive Message objects, containing another important datum, i.e. the sender. Thus, to receive that message, and print all the relevant data, you should write:
void myThread::execute(void) { do { Message theMessage = getMessage(); // Inside execute(), you have // access to member functions! if(theMessage.Contents() == 0) break; printf("Message is: %s from thread with pid(%i)\n", (char*)(theMessage.Contents()), theMessage.Source()->getPid()); } while(1); }NOTES:
void InitLwp(speed)which uses IRQ 8, the most programmable IRQ, to give/receive control, and destroyed by
void DoneLwp(void)that should be more or less the first and the last thing you do in your main().