On Thursday, 9 June 2016 at 23:53:33 UTC, BBasile wrote:
On Thursday, 9 June 2016 at 21:02:26 UTC, maik klein wrote:
I am currently writing a task system and I have this case where I want to send a delegate to a different thread but this delegate also captures a variable. I use this to implement a "future".

Now as far as I know this delegate will allocate GC memory and I just wanted to avoid that, just for fun.

Here is the code https://dpaste.dzfl.pl/cd77fce99a5b

I have only worked on it a couple of hours and I am sure there are many problems with it.

Basically the idea is that you can use normal lambda syntax. If you want a function that returns and int and takes an int, you can write it like this:

(int i) => i

If you want a function that returns an int, takes an int, but also captures and int you would write it like this

(int i, int captured) => i + captured

But you also have to declare the base function type without the captured variables beforehand.

Fn!(FnType!(int, int), (int i, int captured) => i + captured)(42);

That is how I know what the captured variables are.

The only part that is currently missing are polymorphic delegates. They are not too useful if I can't pack them into the same array.

I guess I have to do this with classes/interfaces.

Thoughts?

Has this been done before?

What you have here is more functor, to the extent that you can memorize the parameters. The problem is that it's not compatible with the delegates as defined in the language.

Actual D delegates get collected when we take the address and the context of a member function with "&". To allocate the delegate itself in the non GC heap is easy (with a struct that contains two pointers).

Your message has motivated me to make this, thanks to a union true nogc delegates are possible:

=========================================
module runnable;

union Delegate(FT)
{
    // delegate layout as defined in the D ABI
    struct DgMembers
    {
        void* ptr;
        FT funcptr;
    }
    DgMembers members;

    // the 2nd member is "true" D delegate type
    import std.array: replace;
mixin("alias DT = " ~ FT.stringof.replace("function", "delegate") ~ ";");
    DT dg;
}

void main() @nogc
{
    static struct Foo
    {
        @nogc string delegate() test;
        @nogc string source() {return "test";}
    }

    import std.experimental.allocator.mallocator;
    import std.experimental.allocator;

    Foo foo;
    alias DelegateT = Delegate!(typeof(&Foo.source));

// &Foo.source takes the static address, i.e in the data segment
    DelegateT* dg = make!DelegateT(Mallocator.instance);
    dg.members.ptr = &foo;
    dg.members.funcptr = &Foo.source;

    foo.test = dg.dg;
    assert(foo.test() == "test");
}
=========================================

Here you have a true D delegate that's conform with the language.

The problem is I currently do this

struct LocalTaskQueue{
...
    Array!(Box!Fiber) work;
    Array!(Box!Fiber) queuedWork;
...
}

This is where I submit my delegates to, but before that I also wrap them inside a fiber. Btw "Box" is my version of Unique.

I execute a fiber inside work and if it is done I want to delete it, because it is not needed anymore. If it is on hold it transfer it from "work" to queuedWork.

That means I need be able to put my delegates inside a fiber, this is the constructor for a Fiber currently.


    this( void delegate() dg, size_t sz = PAGESIZE*4 ) nothrow
    in
    {
        assert( dg );
    }
    body
    {
        allocStack( sz );
        reset( dg );
    }

I mean I can easily convert my "Fn" version to delegate with &foo.opCall, but then I would not know how long my "Fn" should stay alive.

I think what I can do is switch my struct Fn to a class and implement my own function polymorphism.

I would then do

Array!(Box!Fiber) work;
Array!(Box!PolymorphicFn!(void, void)) rawFunction;

and when I delete a fiber from work I can delete it from rawFunction.

But that seems to be so much trouble just to avoid a bit of GC.





Reply via email to