Sign in to follow this  
Followers 0
monoceres

Callback from another thread.

8 posts in this topic

#1 ·  Posted (edited)

I'm having troubles with having a custom made dll call callbacks to my au3 script. The thing that makes this error prone is that the dll calls the autoit callback from a new thread.

To illustrate the problem I'm having I made the following sources.

Dll source:

#include <windows.h>

typedef void CALLBACK AUTOITCALLBACK(int);

DWORD WINAPI DoSomething(void* param)
{
    AUTOITCALLBACK *callback=((AUTOITCALLBACK*)param);
    Sleep(500); // Do something that takes a while..
    callback(1337);
    return 0;
}


extern "C" __declspec(dllexport) void asyncjob(AUTOITCALLBACK *callback)
{
    CreateThread(NULL,0,DoSomething,(void*)callback,0,NULL);
    Sleep(25); // Gives the thread time to copy the callback ptr
}

Au3 source:

Global $runloop=True
$cb=DllCallbackRegister("f","none","int")

$dll=DllOpen("testbug.dll")

DllCall($dll,"none:cdecl","asyncjob","ptr",DllCallbackGetPtr($cb))


While $runloop; Never quits
    Sleep(10)
WEnd


Func f($x)
    MsgBox(0,"",$x); Works ok.
    box(); Doesn't execute
    $runloop=false; Fails to change $runloop
EndFunc

func box()
    MsgBox(0,"","")
EndFu

I'm guessing this have to do with autoit not being thread safe, and if that's the case how should I solve my problem?

Thanks.

Edited by monoceres

Broken link? PM me and I'll send you the file!

Share this post


Link to post
Share on other sites



So to summarize the whole thing, why can't I modify variables and call user defined functions when a callback is running in the context of a different thread? Reading variables and calling built in functions works as expected.


Broken link? PM me and I'll send you the file!

Share this post


Link to post
Share on other sites

I think you're making a deadlock of some sort ;] can't be sure. This for example return correctly and the script exits eventually. I know it's not sane to compute how much time the called function requires...

Global $runloop=True
$cb=DllCallbackRegister("f","none","int")

$dll=DllOpen("test.dll")

DllCall($dll,"none:cdecl","asyncjob","ptr",DllCallbackGetPtr($cb))


While $runloop; Never quits
    Sleep(10)
WEnd


Func f($x)
    MsgBox(0, '', $x)
    ;ConsoleWrite($x & @LF); Works ok.
    ;box(); Doesn't execute
    $runloop=false; Fails to change $runloop
EndFunc

func box()
    MsgBox(0, '', '')
    ;ConsoleWrite('Data' & @LF)
EndFuncoÝ÷ Ø«zZr)ànZ)àq©e³*.®Ç+{,-¡×¢Énuî´Â)Ý£!ëh§r[{­-r«­­ÊyûèÐ,°@ LâËãH§·CXäCX@

#include <windows.h>
#include <tchar.h>

typedef void CALLBACK AUTOITCALLBACK(int);

DWORD WINAPI DoSomething(void* param)
{
    AUTOITCALLBACK *callback=((AUTOITCALLBACK*)param);
    HANDLE hSemaphore = CreateSemaphore(NULL, 1, 1, _T("Semama"));
    Sleep(500); // Do something that takes a while..
    callback(1337);
    WaitForSingleObject(hSemaphore, INFINITE);
    CloseHandle(hSemaphore);
    MessageBox(NULL, _T("Text"), _T("Title"), MB_OK | MB_ICONINFORMATION);
    ExitThread(0);
}


extern "C" __declspec(dllexport) void asyncjob(AUTOITCALLBACK *callback)
{
    CreateThread(NULL,0,DoSomething,(void*)callback,0,NULL);
    Sleep(50); // Gives the thread time to copy the callback ptr
}

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

What happens if you don't use MsgBox()? What about using ConsoleWrite()? Please try the original DLL from the first post as well as the original code except replace the MsgBox() calls with ConsoleWrite(). The synchronization code should not be necessary.

Edit: It's important that you use ConsoleWrite() and not something else because I know the code execution path for that particular function.

Edited by Valik

Share this post


Link to post
Share on other sites

Very interesting, it's indeed working fine without the MsgBox() calls. Any ideas why MsgBox makes the callback stop executing and more important, what other functions will produce the same freeze? Is this perhaps linked with the fact that autoit creates an extra thread for the call to MessageBox?


Broken link? PM me and I'll send you the file!

Share this post


Link to post
Share on other sites

Simple way to test: Try InetGet(). It creates another thread to do the download. However, I suspect it's something else. I'm trying to remember the circumstances but I've dead-locked stuff before by using a GUI (probably MessageBox()) too early before. I can't remember the exact circumstances, though.

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Don't know if you mind but even without blocking functions or popping up a GUI you can easily lock the script. Consider this for example, you can change it with a sleep(500) call or other time consuming function execution:

#include <Array.au3>

Global $runloop=True
Global $avArray[3] = ['1', '2', '3']
$cb=DllCallbackRegister("f","none","int")

$dll=DllOpen("test.dll")

DllCall($dll,"none:cdecl","asyncjob","ptr",DllCallbackGetPtr($cb))


While $runloop; Never quits
    Sleep(10)
WEnd


Func f($x)
    Local $j = 0
    For $i = 1 To 0x10000
        $j = $i*BitXOR($i, $j); Works ok.
    Next
    
    $runloop=false; Fails to change $runloop
EndFunc

It still locks the entire script somewhere in the For..Next loop. So you can't say that ConsoleWrite will solve the case. It's an asynchronous case here. Correct me where I'm wrong.

Edited by Authenticity

Share this post


Link to post
Share on other sites

So you can't say that ConsoleWrite will solve the case.

I don't recall saying that it would solve the problem. It was a test to try to isolate the problem, nothing more.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0