Hi marcus,
-edit-
Is it a problem that the application running on GLFW cant use the tc_UserData if you use it? I thought the point was to avoid system dependencies. Just use your own GLFW threads within a multthreaded GLFW program, surely. As I see it, your GLFW threads are an interface. If you add your own TLS to it then the users of your framework will just use that instead.
-end edit-
Signalling in my system is realitvely straightforward. Since I have threadable objects, as opposed to just seperate threads running through some arbitrary code, I just perform a method for that object. If that method changes internal data, the thread will be aware of that automatically (having acces to the protected level internals). Methods which require synchronised access can simpy use a Lockable object that is bound to the internal thread (you can't lock it until the internal thread is done with it). Lockable is a service class that encapsulates the Semaphore mechanism.
The actual Amiga task, running within the context of the object, can always see the (protected) state information. Due to this, the only real signalling I need is to be able to go to sleep and wait for an event, or a time out. The theadable service provides a delay timer feature too, using the DelayTimer class (itse;f an encapsulation of the timer.device).
There is a sleep() method for theadable objects that actually uses the amiga Wait()/Signal() system.
So the internal thread can literally go to sleep. When you then kick the object by invoking the wake() method, the appropriate exec level signal is sent to the internal task which is then woken up and carry on.
So really I don't use a lot of different signalling, just sleeping and waking. All other state info is actually part of the object definition. There is also a shutdown signal defined which basically tells the internal thread to remove itself. The internal thread code can simply call the method that checks for a shutdown call and then do whatever is required to finish and exit.
It does this(cleanly) by a return from the run() method.
The thread which invoked the stop() method (which may be part of the destruction for example) is then forced to wait on the internal thread to finish.
The only other thing is that a call to shutdown() will wake up the thread if it is waiting for something already. This allows threads to respond quickly to getting told to finish up.
It's a robust system and the interface ensures that youd have to especially set out to break it in order to screw it up.
Of course, critical sections (Forbid/Permit) is used wherever necessary
Try to avoid this. If you can, use semaphore locking for shared resources, its much friendlier - especially if your going all multithreaded...
The only place I use this is inside the start() method that creates the task. With task switching momentarily disabled, I write the tc_UserData to point to the object and thats it.
I don't use it anywhere else and would rather avoid it all together. If you ever do a WarpOS version you'll see there is no ForbidPPC()/PermitPPC()...
-threaded graphics-
Agreed. In most cases a multithreaded approach to rendering on a single processor system is pointless.
However, the double buffered rasterizer I wrote works reasonably well because on the current hardware, rendering takes time and does force the calling code to wait (we are talking direct Warp3D level stuff here, not OpenGL). Only the simple pre-v4 Warp3D calls are asynchronous. The v4 vertex array calls (which I use for efficiency/flexibility reasons) are not (well there may be some parallelism at the hw level). The cache thrashing issue isn't much of a problem in my code since the buffers aren't very large anyway.
So, in my case whilst drawing isnt physically any faster, the setup stage can continue so time is not wasted. The threaded double buffering adds the asynchonicity you would expect from a 'decent' OpenGL implementation.
I don't have a high level 3D system apart from a simple transforamtion / shading engine. I have no interest in trying to compete with OpenGL :-)
Anyway, if you use multithreading under Windows, you'll love it on AmigaOS. Task switch times are miniscule :-)