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
[email protected]
http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus



--
_______________________________________________
Lazarus mailing list
[email protected]
http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply via email to