[M3devel] Re: RTHooks CheckStoreTraced and CheckLoadTraced

Tony Hosking hosking at cs.purdue.edu
Thu Apr 19 19:39:55 CEST 2007


On Apr 19, 2007, at 1:09 PM, Darko wrote:

> Thanks, thats very useful. Garbage collection is a bit of a  
> mystery, since I can't imagine myself changing it I've never  
> studied it, but it's on my mind constantly. M3 is so safe whenever  
> something weird happens with my code I know it's because I've  
> trodden on a traced reference, or copied it into a untraced space  
> and back or something.

Yeah, it can be a problem, but if you are careful it should not be  
too hard to avoid.  The idea is not to "hide" a reference where the  
GC cannot find it.

>
> I've relied on the pinning of references that appear in the stack  
> extensively, and I think a lot of other code does too. It a bit of  
> a worry to think that it's possible it might disappear. If we don't  
> have this, short of stopping the collector, what do you do if you  
> want your object to stay still?

In the absence of implicit pinning of references from the stack we  
would need to introduce an explicit pin/unpin API for programmers to  
use.  It is unlikely that CM3 will ever move away from implicit  
pinning unless we were to build a "smart" backend compiler (ie, not  
based on gcc).  So, short answer is that you are probably OK for a  
long time to come.

>
>
> On 19/04/2007, at 3:34 PM, Tony Hosking wrote:
>
>> My immediate reaction is that you probably want to approach reads  
>> slightly differently.  Instead of using your generic InTypeRead  
>> routine to access the appropriate bits/bytes of the value, you  
>> probably want to replace it with some sort of simple addressing  
>> mechanism that computes the raw address of the field.  Traced  
>> reference fields must always be aligned within the heap object  
>> they are stored in so your addressing for these will not need bit  
>> offsets.  Once you have an ADDRESS "addr" for the traced reference  
>> field, so long as you use the idiom I gave earlier, you will get  
>> the correct behavior for reads.  That is,
>>
>> WITH field = LOOPHOLE(addr, UNTRACED REF REFANY) DO
>>   .. use field^ to access the traced reference stored at address  
>> field ..
>> END;
>>
>> The alternative is to invoke "RTHooks.CheckLoadTracedRef(r)" on  
>> the traced reference "r" once you have loopholed it through the  
>> raw field access into a REFANY, before you return it from  
>> InTypeReadRef:
>>
>> PROCEDURE InTypeReadRef (this: T; obj: REFANY; field: CARDINAL):  
>> REFANY =
>>   VAR v: REFANY;
>>   BEGIN
>>     this.check(field, Kind.Ref, obj);
>>     this.read(obj, field, ADR(v), BYTESIZE(v));
>>     RTHooks.CheckLoadTracedRef(v);
>>     RETURN v;
>>   END InTypeReadRef;
>>
>> This is safe for the current CM3 ambiguous roots collector, but  
>> this code would probably break with a fully copying/relocating  
>> collector (if one was ever implemented for Modula-3), since the  
>> raw bytes of the reference might become stale between the point  
>> you load them from memory and the point they are placed in the  
>> typed REFANY variable "v" (effectively, you'd be hiding a  
>> reference from the GC for a brief period, and it would not be able  
>> to update that hidden reference in the face of its target being  
>> moved).
>>
>> I note, generally, that your field addressing mechanisms only work  
>> anyway because we are using an ambiguous roots collector that pins  
>> all objects referenced from the thread stacks (your "obj"  
>> parameters in the accessor methods), which allows the use of  
>> RTHeap.GetDataAdr to provide an address for the object that is  
>> valid while the object reference is held on the stack.
>>
>> When writing references to object fields you will need to invoke  
>> "RTHooks.CheckStoreTraced(o)" on the heap object "o" whose field  
>> is being stored.  Thus, for InTypeWriteRef:
>>
>> PROCEDURE InTypeWriteRef(this: T; obj: REFANY; val: REFANY; field:  
>> CARDINAL) =
>>   BEGIN
>>     this.check(field, Kind.Ref, obj);
>>     WITH f = this.offs.get(field) DO
>>       IF NOT ISTYPE(f, RefField) OR NOT RTType.IsSubtype(TYPECODE 
>> (val), NARROW(f, RefField).tc) THEN RAISE WrongType END;
>>     END;
>>     this.write(obj, field, ADR(val), BYTESIZE(val));
>>     RTHooks.CheckStoreTraced(obj);
>>   END InTypeWriteRef;
>>
>> If you are performing multiple writes of reference fields on the  
>> same object (e.g., to an array) then you can invoke  
>> CheckStoreTraced once for the whole object.
>>
>> Hope this helps.
>>
>> On Apr 18, 2007, at 10:49 PM, Darko wrote:
>>
>>> Thanks, that's very helpful. I agree I shouldn't need it but it's  
>>> exactly because I'm not using the compiler is why I need to know  
>>> about it. The code is attached, it's still in development and  
>>> unfinished but you'll get the idea.
>>>
>>> Basically the module allows you to programatically access  
>>> structure fields. It allows for applications like being able to  
>>> create indexes (using an adaptation of the Table code) of objects  
>>> using arbitrary fields as keys. It makes dynamic programming a  
>>> bit more dynamic. The code is intended to be completely safe, but  
>>> there's more checks I need to do.
>>>
>>>
>>>
>>> <InType.m3>
>>> <InType.i3>
>>>
>>>
>>>
>>>
>>> On 19/04/2007, at 4:05 AM, Tony Hosking wrote:
>>>
>>>>
>>>> On Apr 18, 2007, at 9:37 PM, Darko wrote:
>>>>
>>>>> Not actually using that code, but I did see your helpful commit  
>>>>> on it some time ago. It seems that the problem I described  
>>>>> earlier is due to another bug. The code I'm working on does a  
>>>>> lot of reading and writing of traced references and I was  
>>>>> hoping to get a better understanding of situations I need to be  
>>>>> aware of and how to use those RTHooks calls more efficiently.
>>>>
>>>> You SHOULD NOT need to use these calls so long as you are  
>>>> accessing using properly typed references (as in my example  
>>>> earlier).  This is not an issue for SAFE code, only in UNSAFE  
>>>> modules must you be careful how you use LOOPHOLE to cast  
>>>> references.
>>>>
>>>>> So say I have two traced objects and I am copying a traced  
>>>>> reference from a field in one to the other. What set of calls  
>>>>> would be normally required there?
>>>>
>>>> How are you copying the references?  I would like to see your  
>>>> code for this.  So long as you properly type things then the  
>>>> calls to RTHooks.CheckLoadTracedRef and RTHooks.CheckStoreTraced  
>>>> will be generated by the compiler.
>>>>
>>>> Anyway...
>>>>
>>>> RTHooks.CheckLoadTracedRef is generated by the compiler whenever  
>>>> you access a traced reference in memory (e.g., via dereference  
>>>> or from a shared variable).  Its job is to prevent mutators from  
>>>> acquiring references to "gray" objects (that are reachable but  
>>>> still to be scanned for the traced references that they  
>>>> contain).  The check turns the object from gray to black by  
>>>> scanning its references and making sure none of them refer to  
>>>> white objects (that are not known to be reachable by the  
>>>> collector).
>>>>
>>>> RTHooks.CheckStoreTraced is generated by the compiler whenever a  
>>>> traced reference is stored (or might be stored, e.g., for VAR)  
>>>> into a traced object.  The check makes sure that the  
>>>> generational GC knows that the object has been modified.  It  
>>>> needs to know this for objects in the older genration.
>>>>
>>>> Again, I reiterate that it is not my intention that programmers  
>>>> actually call these runtime hooks explicitly!  I am certain I  
>>>> can rewrite any code you might have so that you do not need to  
>>>> call them explicitly.  Instead, properly typed references will  
>>>> result in the checks being generated for you by the compiler.   
>>>> Please feel free to send me code snippets for review.
>>>>
>>>>> I'm sure you haven't got time to go into the minutiae of the  
>>>>> runtime system, but any hints would be appreciated.
>>>>>
>>>>> Cheers,
>>>>> Darko.
>>>>>
>>>>>
>>>>> On 18/04/2007, at 4:34 PM, Tony Hosking wrote:
>>>>>
>>>>>> I assume you have an apply method for your RTTypeMap.Visitor  
>>>>>> that takes "field: ADDRESS" and treats it as "REF REFANY".    
>>>>>> This is wrong.  When reading a REF field you should use the  
>>>>>> following idiom:
>>>>>>
>>>>>> WITH ref = LOOPHOLE(field, UNTRACED REF REFANY) DO
>>>>>>   ... access field via ref^ ...
>>>>>> END;
>>>>>>
>>>>>> This will automatically insert a call to the appropriate  
>>>>>> runtime routines on accessing the reference field.
>>>>>>
>>>>>> There should be no need for you to call the runtime routines  
>>>>>> directly.
>>>>>>
>>>>>> On Apr 17, 2007, at 9:51 PM, Darko wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> Wondering if you can explain the use of these calls a little  
>>>>>>> more. I'm currently using type maps to read and write fields  
>>>>>>> from traced objects. Reading a traced reference from inside a  
>>>>>>> traced object into a local variable is not working as it  
>>>>>>> should. Should I use CheckLoadTraced and if so when and how?  
>>>>>>> Looking at your changes to RTTypeMap, writing references into  
>>>>>>> objects means you need to call CheckStoreTraced on the object  
>>>>>>> written inside of, before it is written?
>>>>>>>
>>>>>>> Cheers,
>>>>>>> Darko.
>>>>>>
>>>>
>>>
>>




More information about the M3devel mailing list