[M3devel] FW: eliminate Target.Global_handler_stack?

Jay jayk123 at hotmail.com
Tue May 13 03:09:12 CEST 2008


 truncated yet again..
Olaf is there anything in any mail log on your end?
 
 - Jay


From: jayk123 at hotmail.comTo: hosking at cs.purdue.eduCC: m3devel at elegosoft.comSubject: RE: [M3devel] eliminate Target.Global_handler_stack?Date: Tue, 13 May 2008 00:23:43 +0000


Tony, Yep, I got my true/false backwards at least once.  > I have no  objection.  Thanks. Expect a few deleted lines "soon". 
 > When does the GC currently create a thread? I'd have to double check. I thought it was in RTAllocator's module initialization. But maybe not.No mystery here, just that I'm out of Modula-3 mode for a few hours.It might be on-demand actually -- which would then have to be synchronized -- so doing it early might be preferable..er...well...actually..problem either way I now realize, depending.. There is this interesting useful but problematic aspect of .dlls on Windows..dlls have an "entry point", a "main" if you will. Usually called DllMain, but the name can be anything. It isn't exported, it's a field in the file format. The signature isBOOLEAN DlMain(int reason, ADDRESS opaqueHandleToSelf, ADDRESS additionalBit); The reasons are  process attach, process detach, thread attach, thread detach   the opaqueHandleToSelf can be used for example to load "resources" from yourself  additionalBit has a null vs. non-null meaning    when reason == process detach, the nullness indicates if the .dll is being unloaded because the process is going away, or it is being unloaded but the process is staying around -- if the process is going away, basically nothing should be done, if the process is not going away, cleanup any process-global resources  Process attach means the .dll was "just" loaded. If you return false for failure from process attach, then the process will fail to launch or the LoadLibrary (dlopen) will fail, depending on if you are a static dependency of the .exe, or dynamically loaded via LoadLibrary (dlopen). I believe the return values from all the other reasons are ignored. There are many oddities here. Including the fact that threads can be created before the .dll loads and destroyed after it unloads, so the interface of thread attach/detach isn't what it seems -- you don't necessarily get the calls, you only get the calls for threads created/destroyed while you are already loaded (and perhaps one extra when you get loaded/unloaded?) Folks who think they can do their per-thread cleanup in thread detach are sorely wrong.What you have to do is keep all your thread-locals in a global list and free them in process detach.Or just don't have any, that's the best policy. Now, the important aspect here is that there is a lock around all DllMain calls, be they the process or thread ones. This is "useful" because it means you can initialize your globals without worrying about synchronization.Now, because of the lock, there isn't much you can do without risking deadlock.But you would typically limit your activities to heap allocation, TlsAlloc (pthread_threadspecific_allocate_key or such), and critical section initialization. Win32 critical sections sadly require an initialization call, rather than just being initialized to all zeros. There's an API in Vista to allow static initialization.(Cygwin's pthreads stuff also doesn't have zero initialization, but at least static). On-demand initialization outside of DllMain must be synchronized, as threads can be created fairly arbitrarily early.If you create a thread in DllMain, it essentially won't start until all the DllMains return.It is possible though to have multiple threads running concurrently with "main". And just because "you" don't create a thread in your app/.exe, doesn't mean some .dll you loaded didn't create some.There's no such thing as a single-threaded process. I suspect there is therefore a problem here in Modula-3. Or at least some double checking needed -- around the thread safety of various initialization. A common pattern in Win32 is /ROUGHLY/: T* g_pt; // global pointer to a T T* GetTheT(void){  if (g_pt != NULL)    return g_pt;  T* pt = new T();  MemoryBarrier(); // ensure T's constructor finished before storing the global pointer  if (InterlockedCompareExchangePointer(&g_pt, pt, NULL) != NULL)   {     // somebody else won the race    delete pt;   }   return g_pt;} This way, initialization is done on-demand and thread safely.This code assumes that race conditions will be rare vs. the expense of creating a T that is thrown away, and that creating, multiple T's in the event of a race, only to cleanup all but one right away, is ok. If you must not ever create more than one T, then other code is needed.Easiest is to initialize a critical section in DllMain and then just use that. Or implement a little spin lock, assuming initialization of T is cheap. In Vista there is a "once" synchronization object for handling this.It is very disappointing that this wasn't introduced 10+ years ago.As well as statically initializable locks and reader/writer locks. I keep tending to think that Modula-3 module intializers have this same lock, at least on Windows.But that is definitely NOT necessariiy true, at least in the face of static linking, and probably even with dynamic linking, since Modula-3 implements this stuff itself. I'll have to review the code. Maybe it is just fine already. If it isn't, well, there are a few easy solutions.As well, this could be a problem on all platforms.So it might make sense for the runtime to recognize when it is calling module initializers and endeavor to "not start" any threads while they are running. Corrallary, on Win32, and in this scheme, that if you create  a thread in DllMain/thread initializer and then wait for it to make progress, you deadlock. Gotta run, - Jay


CC: m3devel at elegosoft.comFrom: hosking at cs.purdue.eduTo: jayk123 at hotmail.comSubject: Re: [M3devel] eliminate Target.Global_handler_stack?Date: Mon, 12 May 2008 19:43:35 -0400

On May 12, 2008, at 7:35 PM, Jay wrote:

Target.Global_handler_stack is FALSE for targets that ever use kernel threads.Target.Global_handler_stack is TRUE for targets that never use kernel threads.Targets that can go either way set it to FALSE. When Global_handler_stack is TRUE, inline code is generated to,  I guess, manipulate a per-thread linked list of stack frames. When Global_handler_stack is FALSE, function calls are generated  to do same. That is, Global_handler_stack is TRUE is a little more efficient. Given that Global_handler_stack is TRUE for all "recent", "active", "interesting"  targets, with the presumably temporary exception of PPC_LINUX, how about  we remove Global_handler_stack as a variable and just act as if it is always FALSE?
Don't you mean "false" for all current targets (i.e., using threads)?


(I have some suspicion that this linked list is absent on targets with a stack walker, so it'd make no difference on them. I'll check later..NT386 ought use fs:0 but it doesn't.)

Indeed, the list is absent on such targets.

I figure target-specificity should be minimized.Make it easier to bring up new targets -- not that the code isn't pretty  well commented and easy to understand, so it's really no easier without this.
I have no  objection. 


The counterpoints would be:  Even if it is target-specific, it does work and isn't complicated, so leave it alone.  If those old targets are alive, and lack pthreads, it is slightly faster this way.  If people want current/new targets to have a "faster" single threaded mode, this    could be part of that. Note though that single threaded apps can't/don't exist,   unless maybe if you never heap allocate, since the heap allocator/garbage collector   creates a thread at initialization (shouldn't it wait for a heap allocation? Maybe.   If there really is a chance it won't occur or won't occur for a while).

When does the GC currently create a thread?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20080513/30742566/attachment-0002.html>


More information about the M3devel mailing list