[M3devel] further reducing cloned headers wrt pthread?

Jay jay.krell at cornell.edu
Wed Feb 4 01:06:24 CET 2009


 There are a few possibilities: 
 
 
 Roughly: 
 
 Where there is 
 
 INTERFACE Upthread; 
 
  TYPE  
   pthread_t = ... system specific ...  
   pthread_cond_t = ... system specific ...  
   pthread_mutex_t = ... system specific ...  
 
  PROCEDURE pthread_thread_init_or_whatever(VAR pthread_t);  
  PROCEDURE pthread_mutex_init_or_whatever(VAR pthread_mutex_t);  
  PROCEDURE pthread_cond_init_or_whatever(VAR pthread_cond_t);  

  MODULE PThread;  
  VAR 
    a: pthread_t; 
    b: pthread_cond_t; 
    c: pthread_mutex_t;  

  PROCEDURE Foo() =
  BEGIN
    Upthread.pthread_thread_init_or_whatever(a);  
    Upthread.pthread_cond_init_or_whatever(b);  
    Upthread.pthread_mutex_init_or_whatever(c);  
  END Foo;  

  change to:  

  INTERFACE Upthread;  
 
  TYPE  
   pthread_t = RECORD END;   or whatever is correct for an opaque preferably unique type 
   pthread_cond_t = RECORD END;  ditto 
   pthread_mutex_t = RECORD END;  ditto 

  PROCEDURE pthread_thread_init_or_whatever(VAR pthread_t);  
  PROCEDURE pthread_mutex_init_or_whatever(VAR pthread_mutex_t);  
  PROCEDURE pthread_cond_init_or_whatever(VAR pthread_cond_t);  
 
 
  INTERFACE PThreadC.i3  
 
  PROCEDURE GetA(): UNTRACED REF Upthread.thread_t;  
  PROCEDURE GetB(): UNTRACED REF Upthread.thread_cond_t;  
  PROCEDURE GetC(): UNTRACED REF Upthread.thread_mutex_t;  

 or possibly extern VAR 

  PThreadC.c  
 
  static pthread_t a = PTHREAD_INIT;  
  static pthread_cond_t b = PTHREAD_COND_INIT;  
  static pthread_mutex_t c = PTHREAD_MUTEX_INIT;  

  pthread_t* GetA() { return &a; }  
 
  pthread_cond_t* GetB() { return &b; }  
 
  pthread_mutex_t* GetC() { return &c; }   
 
  MODULE PThread; 
  VAR  
    a := PThreadC.GetA();  
    b := PThreadC.GetB();  
    c := PThreadC.GetA();   

  PROCEDURE Foo() =  
  BEGIN  
    Upthread.pthread_thread_init_or_whatever(a^);  
    Upthread.pthread_cond_init_or_whatever(b^);  
    Upthread.pthread_mutex_init_or_whatever(c^);  
  END Foo;  

  or, again, possibly they are variables and it goes a little smaller/quicker:  

  FROM UPthreadC IMPORT a, b, c;  
  
  
  PROCEDURE Foo() =  
  BEGIN  
    Upthread.pthread_thread_init_or_whatever(a);  
    Upthread.pthread_cond_init_or_whatever(b);  
    Upthread.pthread_mutex_init_or_whatever(c);  
  END Foo;  
 
  I think that is pretty cut and dry, no controversy. 

  What is less clear is what to do with non-statically allocated variables.  

  Let's say:  
 
  MODULE PThread;  
 
  TYPE T = RECORD  
    a:int; 
    b:pthread_t; 
  END;  
 
  PROCEDURE CreateT():T=  
  VAR  
    t := NEW(T)  
  BEGIN  
    Upthread.init_or_whatever(t.b);  
    RETURN t;  
  END;  
 
  PROCEDURE DisposeT(t:T)=  
  BEGIN  
    IF t = NIL THEN RETURN END;  
    Upthread.pthread_cleanup_or_whatever(t.b);  
    DISPOSE(t);  
  END;  

  The desire is something that does not know the size of pthread_t, something like:  
 
  TYPE T = RECORD  
    a:int;
    b:UNTRACED REF pthread_t;  
  END;  
  
  
  PROCEDURE CreateT():T=  
  VAR  
    t := NEW(T);  
  BEGIN  
    t.b := LOOPHOLE(UNTRACED REF pthread_t, NEW(UNTRACED REF ARRAY OF CHAR, Upthread.pthread_t_size));  
    (* Though I really wanted t.b := RTAllocator.MallocZeroed(Upthread.pthread_t_size); *)  
    Upthread.init_or_whatever(t.b^);  
    RETURN t;  
  END;  
 
  PROCEDURE DisposeT(t:T)=  
  BEGIN  
    IF t = NIL THEN RETURN END;  
    Upthread.pthread_cleanup_or_whatever(t.b^);  
    DISPOSE(t.b);  
    DISPOSE(t);  
  END;  
 

  However that incurs an extra heap allocation, which is not great.   
  In at least one place, the pointer-indirection-and-heap-allocation is already there  
  so this isn't a deoptimization. However "reoptimizing" it might be nice. 
  
  
   What I would prefer a pattern I often use in C -- merging allocations, something like,  
   /assuming/ t is untraced, which I grant it might not be.   

  
   And ensuring that BYTESIZE(T) is properly aligned:   

  
   PROCEDURE CreateT():UNTRACED REF T=  
   VAR
     p : ADDRESS;  
     t : UNTRACED REF T;  
   BEGIN  
     (* Again I would prefer RTAllocator.MallocZeroed *)   
     p := NEW(UNTRACED REF ARRAY OF CHAR, Upthread.pthread_t_size + BYTESIZE(T)));  
     t := LOOPHOLE(UNTRACED REF T, p);   
     t.b := LOOPHOLE(UNTRACED REF Upthread.pthread_t, p + BYTESIZE(T));  
     Upthread.init_or_whatever(t.b^);  
     RETURN t;  
   END;  
 
 
  That is -- opaque types, size not known at compile-time, but size known at runtime, and  
  do not incur an extra heap allocation for lack of knowing sizes at compile-time.  
  
  
  For the statically allocated variables I think there is no controversy.   
    There might a tiny bit of overhead in the use, but it'd be very small, and possibly  
    even removable in the future. I'd rather avoid the variables, as all writable
    data is to be avoided. Read only pages are better and all that, but ok..
  
  
  However the value is mainly realized only if statically and dynamically allocated variables are handled.   

  The result of this would be further reduction in platform-specificity when cloning  
  C headers into Modula-3 interfaces. i.e. less work to bring up new platforms.
 
 
  - Jay  


----------------------------------------
> From: hosking at cs.purdue.edu
> To: jay.krell at cornell.edu
> Date: Wed, 4 Feb 2009 09:54:01 +1100
> CC: m3devel at elegosoft.com
> Subject: Re: [M3devel] further reducing cloned headers wrt pthread?
>
> I suggest you come up with a proposal for us to look over before you
> change the code base for this.
>
> On 4 Feb 2009, at 09:05, Jay wrote:
>
>>
>>> 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?
>>
>>
>> I think "the other way around" and same conclusion.
>> malloc should return something "maximally aligned" so that
>>
>> pthread_mutex_t* x = (pthread_mutex_t*)
>> malloc(sizeof(pthread_mutex_t));
>>
>>
>> works. pthread_mutex_t doesn't need the padding, malloc does, so to
>> speak.
>>
>>
>> Just as long as we don't have
>>
>>
>> TYPE Foo = RECORD
>> a: pthread_mutex_t;
>> b: pthread_mutex_t;
>> c: pthread_t;
>> d: pthread_t;
>> e: pthread_cond_t;
>> f: pthread_cond_t;
>> END;
>>
>>
>> and such, ok.
>>
>>
>> malloc on NT returns something with 2 * sizeof(void*) alignment.
>> I think on Win9x only 4 alignment, thus there is _malloc_aligned for
>> dealing with SSE stuff.
>> Something like that.
>>
>>
>> I didn't realize untraced allocations were basically just malloc but
>> indeed they are.
>>
>>
>> I'm still mulling over the possible deoptimizations here.
>> I'm reluctant to increase heap allocations.
>>
>>
>>
>> - Jay
>


More information about the M3devel mailing list