[M3devel] further reducing cloned headers wrt pthread?

Tony Hosking hosking at cs.purdue.edu
Mon Feb 2 00:27:46 CET 2009


On 2 Feb 2009, at 10:10, Jay wrote:

>
> I meant translate Modula-3 to C -- C because it knows size and  
> alignment.
> I didn't presume I could just call malloc.
> Even if untraced, I thought calling "the allocator" might
> have some positive interaction with the garbage collector.
> Maybe not?
> (I should look at the code....could be untraced really
> does just call malloc and no gc interaction. Oops.)

No GC interaction at all.  Just user-level thread interaction to make  
malloc atomic w.r. to thread switches.

>> array := NEW(UNTRACED REF ARRAY OF CHAR, sizeof_pthread_mutex_t)
>
>
> That is good. I must have had some hangup about it.
> NEW is guaranteed to be "very aligned" so there is no alignment  
> issue here?

Hmm, yes, you are right that there is a possible alignment issue.   I  
am used to pthread_mutext_t being a simple reference.  But surely in C  
the type of the pthread_mutex_t struct would have appropriate  
alignment padding anyway so as to allow allocation using malloc(sizeof  
pthread_mutex_t)?  So, it all should just work right?

>> Sure, except that now you smear code across the C and Modula-3
>> universes. Lack of clarity...
>
>
> I agree. I'm conflicted here.
> Look at for example at the dealings with "ackSem".
> It is a "simple transform" but "transform" is the suspicious
> word there, not wholly exonerated by "simple".

I don't have your code in front of me at the moment, but I am leery.

> The static variables may also have an issue on Darwin -- can't have
> non-static variables in a shared object or somesuch. So I'll hold off.
> (no access to Darwin past few days)
> Solution could be wrapping the variables up in functions, or every
> operation on them, like for ackSem. Both kind of not ideal though.

Function call would be fine.  You could cache the value in a module  
variable as needed.

> On the other hand, the RTThread / RTThreadStk / ThreadPosix
> split is also a bit smearing/unclear -- you know, FreeStack vs.  
> DisposeStack.
> But two wrongs don't make a right, I understand.

Indeed, but they originally encapsulated unrelated things.

> I do consider the user thread code to be a second class citizen.

That's still no reason to muddy it's implementation... :-)

>>> Why not embed it "by value"?
>>
>> Because it is difficult to embed by value when the type is opaque.
>
> Why?
> I understand issues of "binary compatibility + dynamic linking".
> "Pointers are good because they never change size."
> "Pointers are good for opaque types because they never change size.
> Better than an int because int often can't store a pointer."
>
> But I think we ignore these issues in Modula-3. I know I have been.

I would like to think that we don't ignore these issues and as a  
result we get a cleaner system.  Err on the side of promoting elegance  
rather than lowering ourselves to  the standards of legacy code.

>> hack
>
> Seemed a good way to generalize what I'm thinking about for pthread/ 
> mutex/etc.
>
> - Jay
>
>
> ----------------------------------------
>> From: hosking at cs.purdue.edu
>> To: jay.krell at cornell.edu
>> Date: Mon, 2 Feb 2009 09:54:27 +1100
>> CC: m3devel at elegosoft.com
>> Subject: Re: [M3devel] further reducing cloned headers wrt pthread?
>>
>> On 2 Feb 2009, at 01:44, Jay wrote:
>>
>>> I'd like to consider further reducing "system dependent cloned
>>> headers".
>>> Upthread.i3 in particular.
>>>
>>>
>>> We have for example:
>>>
>>>
>>> pthread_mutex_t = ARRAY[1..6] OF INTEGER;
>>>
>>>
>>> Now, pthread_mutex_t is used in two ways.
>>>
>>>
>>> Some statically allocated variables:
>>>
>>>
>>> VAR
>>> activeMu := PTHREAD_MUTEX_INITIALIZER; (* global lock for list of
>>> active threads *)
>>> slotMu := PTHREAD_MUTEX_INITIALIZER; (* global lock for thread
>>> slot table *)
>>> initMu := PTHREAD_MUTEX_INITIALIZER; (* global lock for
>>> initializers *)
>>>
>>>
>>> Some untraced heap allocated variables:
>>>
>>>
>>> PROCEDURE InitMutex (m: Mutex) =
>>> VAR mutex: UNTRACED REF pthread_mutex_t;
>>> BEGIN
>>> TRY
>>> WITH r = Upthread.mutex_lock(initMu) DO END;
>>> IF m.mutex # NIL THEN RETURN END;
>>> mutex := NEW(UNTRACED REF pthread_mutex_t);
>>> WITH r = Upthread.mutex_init(mutex^, NIL) DO END;
>>> m.mutex := mutex;
>>> FINALLY
>>> WITH r = Upthread.mutex_unlock(initMu) DO END;
>>> END;
>>> RTHeapRep.RegisterFinalCleanup (m, CleanMutex);
>>> END InitMutex;
>>>
>>>
>>> The statically allocated variables can just be moved to C, trivial.
>>> And their initialization then kept efficient.
>>
>> Sure, except that now you smear code across the C and Modula-3
>> universes. Lack of clarity...
>>
>>> What is the right way to translate the untraced allocation?
>>
>> Not sure what you mean by "translate". We are writing Modula-3 in
>> Modula-3 here, so why translate it? I suppose you could invoke some C
>> code to allocate the mutex and return it, but then you have a call to
>> malloc that might not play nice with the Modula-3 code. In the old
>> days this was certainly the case (on user-level thread systems we  
>> need
>> malloc to be atomic with respect to thread transfer). Thus,
>> NEW(UNTRACED REF ...) does bottom out at malloc, but
>> RTAllocator.GetUntracedRef invokes DisableSwitching/EnableSwitching
>> around the call to malloc (these disable switching on user-thread
>> platforms, but are no-ops on system threading platforms where malloc
>> should already be thread-safe). How about simply having some C
>> variable that records sizeof(pthread_mutex_t) and use that in a call
>> to new:
>>
>> array := NEW(UNTRACED REF ARRAY OF CHAR, sizeof_pthread_mutex_t)
>> mutex := ADR(array[0])
>>
>>> OR, here is kind of the opposite question:
>>>
>>>
>>> Why is the mutex heap allocated anyway?
>>>
>>> Why not embed it "by value"?
>>
>> Because it is difficult to embed by value when the type is opaque.
>>
>>> Granted, that makes it more difficult to smush out platform-
>>> specificity.
>>
>> Indeed.
>>
>> And the rest of what you say here is a pretty disgusting hack.
>> Modula-3 has pretty clean type semantics and you are compromising  
>> that
>> in a rather gross way.
>>
>>>
>>> And then to address that:
>>>
>>>
>>> I would kind of like the idea of an "extern type".
>>> An extern type is a type whose instances may only be embedded within
>>> heap allocated objects,
>>> possibly only at the end, possibly with an implied
>>> alignment=maximum, their size
>>> would be exposed via constant C variable, probably they would have a
>>> designated initialization
>>> function, or be zeroed. They could also be "standalone" heap
>>> allocated objects.
>>>
>>>
>>> Specifically, something like this:
>>>
>>>
>>> TYPE pthread_t;
>>>
>>>
>>> TYPE Thread.T = RECORD
>>> (* Modula-3 stuff *)
>>> pthread: pthread_t;
>>> END;
>>> Thread := NEW(Thread.T);
>>>
>>>
>>> roughly equiv to
>>> Thread = malloc(offsetof(Thread.T, pthread_t) + sizeof(pthread_t));
>>>
>>>
>>> pthread.c:
>>>
>>>
>>> extern const size_t pthread_t_size = sizeof(pthread_t);
>>>
>>>
>>> You know -- so that they can be implemented "portably".
>>> Given that they are already opaque and all the Modula-3 code knows
>>> is their size, alignment, and what functions they can be passed to.
>>>
>>>
>>> - Jay
>>




More information about the M3devel mailing list