Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perversion with unsafe C#, memory stack allocation

Here I'm trying to work with unsafe features of C#: http://ideone.com/L9uwZ5

I know, that such way in C# is worst, and I want to admit, that there is some info in the topic. Look at the word "perversion".

I would like to implement quick sort in C# like pure-C way (not even C++). It could be crazy, but just want to look deep at the possibilities of unsafe C#.

I was always trying to use stackalloc operator. I know, that it's an allocation from stack, not from heap, and that's why I get failure with executing of my program.

But I was confused when I haven't seen any exception/error in this program.

  • Why didn't I get any explicit exceptions/errors?

Also, as you see the commented part of code:

struct Header
{
    internal int* data;
};

Header* object_header = stackalloc Header[sizeof(Header)];
object_header->data = stackalloc int[length];

I can't compile it with the last line. C# compiler tells, that in this expression stackalloc couldn't be used. Why? data is int* type, so why did error occur here?

I want just to use stack frame and not to use heap. I know, that there is another way, but it's an allocation from heap.

int*[] data = new int*[length * sizeof(int)];
IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(length * sizeof(int)));
Marshal.WriteInt32(result, 0);
for(int i = 0; i < length * sizeof(int); i++) d[i] = (int*)result;

For example, but it's not stack allocation.

How could I solve my perversion task, explicitly with the stack-allocation and pure-C style syntax in C# language.

That C# wasn't created for such aims and such features are silly - I know, but the main question is not about significance, it's about such features.

like image 756
Secret Avatar asked Nov 26 '12 12:11

Secret


2 Answers

Marc showed the workaround, I'll try to explain why this is required. You are writing, in effect, unmanaged code but the method is still very much a managed method. It gets compiled from IL into machine code and its stack frame and cpu registers will be searched by the garbage collector for object references.

The jitter performs two important duties when it compiles a method. One is obvious and highly visible, translating the IL to machine code. But there's another very important task and it is completely invisible, it generates metadata for a method. A table that shows what parts of the stack frame contains object references and what parts store pointers and value type values. And at which points in the code a cpu register will store an object reference. Also, at what point in the method code an object reference goes out of scope. The reason for GC.KeepAlive(), a pretty unique method that generates no code at all.

The garbage collector needs that table to reliably find object references. This table however has only one level of indirection. It can describe the stack space allocated for object_header and mark the pointer and the pointed-to stack area as "do not scan for object references". It cannot describe the chunk of stack space when you directly assign object_header->data. It doesn't have the extra indirection to sub-divide the stack into smaller sections and describe Header. Using the dummy local variable solves the problem.

like image 187
Hans Passant Avatar answered Nov 06 '22 01:11

Hans Passant


stackalloc wants to assign to a variable. The following works but you would have to be really careful to unassign that before leaving the method - if you leave object_header->data point to a location in the stack: bad things:

    int* ptr = stackalloc int[length];
    object_header->data = ptr;

The fact that it must be assigned to a local variable is explicit in the specification:

local-variable-initializer:

stackalloc-initializer

stackalloc-initializer:

stackalloc unmanaged-type [ expression ]

like image 39
Marc Gravell Avatar answered Nov 06 '22 02:11

Marc Gravell