Thread overview
Runtime error when calling a callback in a parallel Task
Sep 15, 2015
BBasile
Sep 16, 2015
BBasile
Sep 16, 2015
Ali Çehreli
Sep 16, 2015
BBasile
Sep 16, 2015
Ali Çehreli
Sep 17, 2015
BBasile
Sep 17, 2015
Kagamin
September 15, 2015
Under Windows this works fine but under Linux I got a runtime error.

this could be reduced to :

---
import std.parallelism;

alias CallBack = void function(void*);

class Foo
{
    CallBack clbck;
    void* param;
    void dotask()
    {
        // some heavy processing
        // tells the caller that some fresh data are available
        if(clbck) clbck(param);  // debugger breaks HERE
    }

    void call()
    {
        task(&dotask).executeInNewThread;
        // returns directly but the caller will get a notif when finished
    }
}
---

more info about the environment:
- linux i386
- the D program is actually a .so and the main thread is the exe that loads this .so.
- If i don't use a Task then the program works **fine**.

Is it possible to achieve this in a cross platform-way ?
How can i get in phase with the main big thread at the end of my task ?
September 16, 2015
On Tuesday, 15 September 2015 at 23:49:23 UTC, BBasile wrote:
> Under Windows this works fine but under Linux I got a runtime error.
>
> this could be reduced to :
> [...]
If it can help to understand the problem, here is the unreducted case:

https://github.com/BBasile/Coedit/blob/master/cedast/src/ast.d#L343
September 16, 2015
On 09/15/2015 04:49 PM, BBasile wrote:
> Under Windows this works fine but under Linux I got a runtime error.

Can it be because 'param' is invalid at the time clbck is called? The following program works under Linux. However, removing thread_joinAll() is a bug:

import std.parallelism;
import std.stdio;

alias CallBack = void function(void*);

class Foo
{
    CallBack clbck;
    void* param;
    void dotask()
    {
        // some heavy processing
        // tells the caller that some fresh data are available
        if(clbck) clbck(param);  // debugger breaks HERE
    }

    void call()
    {
        task(&dotask).executeInNewThread;
        // returns directly but the caller will get a notif when finished
    }
}

void handler(void* p)
{
    writefln("Finishing with %s at %s", *(cast(int*)p), p);
}

void main()
{
    auto foo = new Foo();
    foo.clbck = &handler;
    int i = 42;
    foo.param = &i;
    foo.call();

    import core.thread;
    thread_joinAll();
}

Ali

September 16, 2015
On Wednesday, 16 September 2015 at 18:19:07 UTC, Ali Çehreli wrote:
> On 09/15/2015 04:49 PM, BBasile wrote:
>> Under Windows this works fine but under Linux I got a runtime error.
>
> Can it be because 'param' is invalid at the time clbck is called?

No the callback and its user parameter are set at the same time.

> The following program works under Linux. However, removing thread_joinAll() is a bug:

I got to try `thread_joinAll`.

The main thread is not a D program so i cant call `thread_joinAll` that simply. Maybe as an additonal dll export but in this case if `thread_joinAll` does something with the Runtime (?) it's quite probable that it won't have an effect. :/
September 16, 2015
On 09/16/2015 02:01 PM, BBasile wrote:
> On Wednesday, 16 September 2015 at 18:19:07 UTC, Ali Çehreli wrote:
>> On 09/15/2015 04:49 PM, BBasile wrote:
>>> Under Windows this works fine but under Linux I got a runtime error.
>>
>> Can it be because 'param' is invalid at the time clbck is called?
>
> No the callback and its user parameter are set at the same time.

I don't want to sound like insisting on my idea but I was more concerned about the time when param's life ended. The callback is just a function pointer. Functions never die, so there is no concern with that. However, the actual variable that 'param' is pointing at may have been gone before the callback is executed.

>> The following program works under Linux. However, removing
>> thread_joinAll() is a bug:
>
> I got to try `thread_joinAll`.
>
> The main thread is not a D program so i cant call `thread_joinAll` that
> simply. Maybe as an additonal dll export but in this case if
> `thread_joinAll` does something with the Runtime (?) it's quite probable
> that it won't have an effect. :/

In my code, thread_joinAll() simply made main() wait until the thread finished. Otherwise, if main() ended before the thread, the int data would be invalid when the callback was using a pointer to its (old) location.

Ali

September 17, 2015
On Wednesday, 16 September 2015 at 22:30:26 UTC, Ali Çehreli wrote:
> On 09/16/2015 02:01 PM, BBasile wrote:
> > On Wednesday, 16 September 2015 at 18:19:07 UTC, Ali Çehreli
> wrote:
> >> On 09/15/2015 04:49 PM, BBasile wrote:
> >>> Under Windows this works fine but under Linux I got a
> runtime error.
> >>
> >> Can it be because 'param' is invalid at the time clbck is
> called?
> >
> > No the callback and its user parameter are set at the same
> time.
>
> I don't want to sound like insisting on my idea but I was more concerned about the time when param's life ended. The callback is just a function pointer. Functions never die, so there is no concern with that. However, the actual variable that 'param' is pointing at may have been gone before the callback is executed.
>
> >> The following program works under Linux. However, removing
> >> thread_joinAll() is a bug:
> >
> > I got to try `thread_joinAll`.
> >
> > The main thread is not a D program so i cant call
> `thread_joinAll` that
> > simply. Maybe as an additonal dll export but in this case if
> > `thread_joinAll` does something with the Runtime (?) it's
> quite probable
> > that it won't have an effect. :/
>
> In my code, thread_joinAll() simply made main() wait until the thread finished. Otherwise, if main() ended before the thread, the int data would be invalid when the callback was using a pointer to its (old) location.
>
> Ali

No, the param is fine. As said initially:

> If i don't use a Task then the program works **fine**.

There is a synchronization problem and only under Linux.
Here is a small program that illustrates better the pattern (based on your previous sample):

---
import std.parallelism;

alias CallBack = void function(void*);

class Foreground
{
    private Background back;
    bool dataAvailable;
    this()
    {
        back = new Background;
        back.clbck = &backgroundFinished;
        back.param = cast(void*) this;
    }

    public void something()
    {
        dataAvailable = false;
        back.call;
    }

    private static void backgroundFinished(void* param)
    {
        with (cast(Foreground) param) dataAvailable = true;
    }

    // lock the access until the background thread notifies that
    // interestingData is ready.
    Background access()
    {
        if (dataAvailable)
            return back;
        else
            return null;
    }
}

class Background
{
    CallBack clbck;
    void* param;
    private void dotask()
    {
        // processing on interestingData
        if(clbck) clbck(param);  // debugger breaks HERE
    }

    void call()
    {
        task(&dotask).executeInNewThread;
    }

    public uint interestingData;
}

void main()
{
    auto fore = new Foreground();
    import std.random;
    while (true) // you'll have to kill by hand !
    {
        // maybe access will be locked
        if (uniform(0,100) > 95)
            fore.something;
        // try to see if access is readable
        if (uniform(0,100) > 20)
            if (fore.access) {/*can use fore.access.interesting data*/}
    }
}
---

a class 'A' operating in the main thread is linked to a background class 'B' that makes some threaded updates. Other classes operating in the main thread can also have an access to the backgound class B but only through 'A' and if 'A' doesn't lock the access. The access is locked when 'B' is updating in a Thread and until 'B' notifies 'A' that the data are ready.

I use a notification because I'm afraid of the results that other classes could get when exploiting the 'B' interstingData. They only **read** them.
September 17, 2015
Maybe compiler generates wrong code, try to debug at instruction level.