[Lazarus] Mutlithreaded Dll Callback crashes my Application
Maik Wojcieszak
maikwo at googlemail.com
Fri Aug 27 08:28:50 CEST 2010
Hi,
What about the callback from the dll. I think this should at least be
mentioned as a possible solution.
Here is the code:
type
PVCMTThread = Pointer;
TVCMTThreadFunc = function (user_data:Pointer):Pointer;cdecl;
PCdeclThreadFunc = ^TCdeclThreadFunc;
TCdeclThreadFunc = record
Func: TVCMTThreadFunc; //cdecl function
Data: Pointer; //orig data
end;
{ C to pascal wrapper function.
BeginThread expects a 'register' function. Vortex supplies a 'cdecl'
function.}
function C2P_Translator(FuncData: pointer) : ptrint;
var
ThreadData: TCdeclThreadFunc;
begin
//Copy the supplied info
ThreadData := PCdeclThreadFunc(FuncData)^;
//Release memory allocated in fpc_vortex_thread_create()
dispose(PCdeclThreadFunc(FuncData));
//Finally we run the thread function
Result := ptrint(ThreadData.Func(ThreadData.Data));
end;
{ FPC specific thread create function to replace Vortex's thread create.
This is required because FPC doesn't work when C libraries create their own
threads}
//{$WARNINGS OFF} //For "cdecl'd functions have no high parameter"
function fpc_vcmt_thread_create(thread_def : PVCMTThread;
func : TVCMTThreadFunc;
user_data : Pointer):longint; cdecl;
var
ThreadData: PCdeclThreadFunc;
begin
//Pass thread information to C to Pascal wrapping function
New(ThreadData);
ThreadData^.Func := func;
ThreadData^.Data := user_data;
{$IFDEF WINDOWS}
//Start thread
TThreadID(thread_def^) := BeginThread(@C2P_Translator, ThreadData );
if TThreadID(thread_def) > 0 then
//Don't free memory here, it is done in the thread function
Result := 1
else
begin
//Free memory
dispose(ThreadData);
Result := 0;
end;
{$ENDIF}
//{$IFDEF UNIX}
// //Start thread
// BeginThread(@C2P_Translator, ThreadData, TThreadID(thread_def^) );
//
// if TThreadID(thread_def) > 0 then
// //Don't free memory here, it is done in the thread function
// Result := True
// else
// begin
// //Free memory
// dispose(ThreadData);
// Result := False;
// end;
//{$ENDIF}
end;
{$WARNINGS ON}
{ FPC specific thread destroy function to replace Vortex's thread destroy.
This is required because FPC doesn't work when C libraries create their own
threads}
function fpc_vcmt_thread_destroy(thread_def : PVCMTThread;
free_data : boolean):longint; cdecl;
var
Err: integer;
begin
{$IFDEF WINDOWS}
//Wait for thread
Err := WaitForThreadTerminate(TThreadID(thread_def), 0);
//Free resources
//if free_data then
// vcmtdll_Sys_Free(thread_def);
{$ENDIF}
//{$IFDEF UNIX}
// //Wait for thread
// Err := WaitForThreadTerminate(TThreadID(thread_def^), 0);
//
// //Free resources
// if 1 = free_data then
// tml_Sys_Free(thread_def);
//{$ENDIF}
if 0 = Err then
Result := 1
else
Result := 0;
end;
initialization
{$IFDEF FPC}
// Set pascal specific thread creators
vcmtdll__Thread_Set_OnCreate(@fpc_vcmt_thread_create);
vcmtdll__Thread_Set_OnDestroy(@fpc_vcmt_thread_destroy);
{$ENDIF}
The example with my dll can be downloaded for testing:
Server : support-ftp.wobe-team.com
User : lazarus
Pwd : test
It works with communication to the GUI baed on events (PostMessage). This example crashed before.
Is there any hint that the FPC team will provide a fix for this in the near future ?
Thanks
Maik
Alexander Grau schrieb:
> Sven Barth schrieb:
>> Am 25.08.2010 23:07, schrieb José Mejuto:
>>>>> On the other hand, if Unix needs one thread creation then it
>>>>> should be
>>>>> automagically done in the initialization section of cthreads, do
>>>>> not ?
>>> SB> This might indeed be a solution.
>>>
>>> If windows have the same problem, threading system should be
>>> initialized in... somewhere automagically as windows multithread is
>>> always present.
>>>
>>
>> This only solves one point (but is needed nevertheless).
>>
>> As I've showed in the last message to Alexander yesterday evening you
>> need to initialize some things PER THREAD and I don't know whether
>> this can be automised. We might need to ask the FPC devs for help
>> regarding this. Perhaps the first time a threadvar in this new thread
>> is accessed might be a good possibilty, but I don't know (yet) how we
>> could track whether this is the first call, because threadvars are
>> not yet allocated (hen-egg-problem). I'll have to take a deeper look
>> into the threading system on Win32 :)
>>
>> Regards,
>> Sven
>
> I'm currently trying to summarize all aspects we discussed to add them
> to the FreePascal Multithreading wiki page (see below) - did I
> forget something that should be mentioned?
>
> Thanks,
> Alexander
>
>
>
> External threads
>
>
> To make Free Pascal's threading system to work properly, each newly
> created FPC thread needs to be initialized (more exactly, the thread
> local storage per thread needs to be initialized so threadvars and
> heap are working). That is fully automatically done for you if you use
> BeginThread (or indirectly by using the TThread class). However, if
> you use threads that were created without BeginThread (i.e. external
> threads), additional work (currently) might be required. External
> threads also include those that were created in external C libraries
> (.DLL/.so).
>
> Things to consider when using external threads (might not be needed in
> all or future compiler versions):
>
>
> A) Initialize the FPC's threading system by creating a dummy thread:
>
> { initialise threading system }
> with tc.create(false) do
> begin
> waitfor;
> free;
> end;
> B) Make the runtime aware that your application uses multiple threads
> in the first line of your application (.dpr):
> IsMultithread:=true;
>
> (This will make FPC routines to perform locks here and there.)
> C) Do not use external threads at all - use FPC threads.
>
> D) If for some reason this doesn't work for you, try this code in your
> external thread function:
>
> function ExternalThread(param: Pointer): LongInt; stdcall;
> var
> tm: TThreadManager;
> begin
> GetThreadManager(tm);
> tm.AllocateThreadVars;
> InitThread(100000000);
> someFunc(param);
> Result:=0;
> end;
>
>
> How to identify if your code is executed in an external thread context
>
> 1. Ask the OS for the ID of the current thread at your application's
> start
>
> Win32: WriteLn('Thread ID=', GetCurrentThreadID);
> Darwin: WriteLn('Thread ID=', GetThreadID); Linux: Writeln('Thread
> ID=', TThreadID(pthread_self) );
>
> 2. Ask again for the ID of the current thread inside the thread
> function and compare this by the result of step 1.
>
>
> --
> _______________________________________________
> Lazarus mailing list
> Lazarus at lists.lazarus.freepascal.org
> http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
>
More information about the Lazarus
mailing list