[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