Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How/Why does ref return for instance members

I'm trying to understand why/how does ref-return for the case of returning refs to members of a class. In other words, I want to understand the internal workings of the runtime that guarantee why ref-return of a instance member works, from the memory safety aspect of the CLR.

The specific feature I'm referring to is mentioned in the ref-return documentation which specifically states:

The return value cannot be a local variable in the method that returns it; it must have a scope that is outside the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."

Here's a code snippet that cleanly compiles and runs and demonstrates returning an instance field as ref return:

using System;
using System.Diagnostics;

namespace refreturn
{
    public struct SomeStruct {
        public int X1;
    }

    public class SomeClass {
        SomeStruct _s;
        public ref SomeStruct S => ref _s;
    }

    class Program
    {
        static void Main(string[] args)
        {
            var x = new SomeClass();                     
            // This will store a direct pointer to x.S
            ref var s = ref x.S;               
            // And now the GC will be free to re-use this memory
            x = null;          
            // Why should s.X1 be considered safe?
            Console.WriteLine(s.X1 + 0x666);
        }
    }
}

My question for this code is: What is the underlying mechanism in the GC that ensures that it keeps tracking the SomeClass instance after that last refernce to it had supposedly been set to null? More precisely...:

Given that the local s stores a direct pointer to the _s member of the SomeClass instance (disassembly from windbg below) whose last "explicit" reference is overwritten with null in the following line (x = null), How does the GC keep track of the live-root to the SomeClass instance which would prevent this program from crashing...?

Windbg Disassembly:

007ff9`e01504dc e8affbffff      call    00007ff9`e0150090 (refreturn.SomeClass.get_S(), mdToken: 0000000006000001)
                                  //rbp-30h stores the pointer to the struct
00007ff9`e01504e1 488945d0        mov     qword ptr [rbp-30h],rax
                                  // Now it's copied to rcx
00007ff9`e01504e5 488b4dd0        mov     rcx,qword ptr [rbp-30h]
                                  // And now copied to rbp-20h
00007ff9`e01504e9 48894de0        mov     qword ptr [rbp-20h],rcx
00007ff9`e01504ed 33c9            xor     ecx,ecx
                                  // The last reference is overwritten with null
00007ff9`e01504ef 48894de8        mov     qword ptr [rbp-18h],rcx
                                  // rbp-20h is copied to rcx again
00007ff9`e01504f3 488b4de0        mov     rcx,qword ptr [rbp-20h]
                                  // Isn't this a possible boom?!?!?
00007ff9`e01504f7 8b09            mov     ecx,dword ptr [rcx]
00007ff9`e01504f9 81c19a020000    add     ecx,29Ah
00007ff9`e01504ff e85c634c5d      call    mscorlib_ni+0xd56860 (00007ffa`3d616860) (System.Console.WriteLine(Int32), mdToken: 0000000006000b5b)
00007ff9`e0150504 90              nop
like image 862
damageboy Avatar asked Dec 20 '17 21:12

damageboy


People also ask

What is ref return?

A reference return value allows a method to return a reference to a variable, rather than a value, back to a caller. The caller can then choose to treat the returned variable as if it were returned by value or by reference.

Why we use ref keyword in c#?

When used in a method's parameter list, the ref keyword indicates that an argument is passed by reference, not by value. The ref keyword makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument.

How to use ref parameter in c#?

The ref keyword in C# is used for passing or returning references of values to or from Methods. Basically, it means that any change made to a value that is passed by reference will reflect this change since you are modifying the value at the address and not just the value.

Are lists passed by reference in C#?

Your List is an object created on heap. The variable myList is a reference to that object. In C# you never pass objects, you pass their references by value.


2 Answers

 // This will store a direct pointer to x.S
 ref var s = ref x.S; 

It stores a managed interior pointer to a heap variable; the pointer is stored to a location on the short term store. The short term store is a root of the GC.

// And now the GC will be free to re-use this memory
x = null;   

Good heavens no. There's a living managed interior pointer in a GC root.

How does the .NET GC / runtime make sure that this would never result in an access violation or reading wild pointers after the SomeClass backing memory has been re-used for something else?

By not releasing that memory until after the internal managed pointer is no longer a root of the GC. Or, to put it another way: by implementing a correct garbage collector.

I can't figure out what question you're really asking here. The GC prevents the error because that is its only job and it was implemented correctly.

like image 130
Eric Lippert Avatar answered Oct 30 '22 14:10

Eric Lippert


How does the .NET GC / runtime make sure that this would never result in an access violation or reading wild pointers after the SomeClass backing memory has been re-used for something else?

For the exact same reason that x won't reference an object that has already been cleaned up by the GC. You know this because the GC isn't allowed to clean up an object if it's accessible through managed code. The object is reachable both through x until you clear it, and also s. s is a reference to that object, and needs to be treated as such by the GC when determining if that object is allowed to be collected.

like image 26
Servy Avatar answered Oct 30 '22 14:10

Servy