www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Runtime error when calling a callback in a parallel Task

reply BBasile <bb.temp gmx.com> writes:
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 ?
Sep 15 2015
next sibling parent BBasile <bb.temp gmx.com> writes:
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
Sep 16 2015
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Sep 16 2015
parent reply BBasile <bb.temp gmx.com> writes:
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. :/
Sep 16 2015
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Sep 16 2015
parent reply BBasile <bb.temp gmx.com> writes:
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.
Sep 16 2015
parent Kagamin <spam here.lot> writes:
Maybe compiler generates wrong code, try to debug at instruction 
level.
Sep 17 2015