* * *

Author Topic: "exception class 'External :?'."  (Read 2242 times)

rvk

  • Hero Member
  • *****
  • Posts: 3428
Re: "exception class 'External :?'."
« Reply #15 on: February 12, 2018, 05:08:54 pm »
Something like this:
(I changed all IFDEFs for FPC to the MSWINDOWS counterpart so that RTLEvent isn't used anymore.

Code: [Select]
diff --git "a/C:\\Users\\Rik\\Downloads\\cromis original\\testeCromis\\cromis\\Cromis.Scheduler.pas" "b/C:\\Users\\Rik\\Downloads\\testeCromis\\cromis\\Cromis.Scheduler.pas"
index ae75dbc..eb9d403 100644
--- "a/C:\\Users\\Rik\\Downloads\\New folder (2)\\testeCromis\\cromis\\Cromis.Scheduler.pas"
+++ "b/C:\\Users\\Rik\\Downloads\\testeCromis\\cromis\\Cromis.Scheduler.pas"
@@ -1068,9 +1068,9 @@ begin
     FRunning := False;
 
     // set the terminate event, so that the thread can safely exit
-    {$IFDEF FPC}RTLEventSetEvent{$ELSE}SetEvent{$ENDIF}(@FTermEvent);
+    {$IFNDEF MSWINDOWS}RTLEventSetEvent{$ELSE}SetEvent{$ENDIF}(FTermEvent);
 
-    {$IFNDEF FPC}
+    {$IFDEF MSWINDOWS}
       // wait and block until the scheduling thread is finished
       AResult := WaitForSingleObject(FSchThread.Handle, cShutdownTimeout);
 
@@ -1084,7 +1084,7 @@ begin
     {$ENDIF}
 
     // close the event handle and free the thread
-    {$IFDEF FPC}RTLEventDestroy{$ELSE}CloseHandle{$ENDIF}(@FTermEvent);
+    {$IFNDEF MSWINDOWS}RTLEventDestroy{$ELSE}CloseHandle{$ENDIF}(FTermEvent);
     FreeAndNil(FSchThread);
 
     // fire the on schedule stop event

But there are still some problems in your own code.
For example if you exit your program you first need to end your ThreadTeste thread and wait for it to finish.
Also, there could then still be calls to Form1 but that one is already up by then so they shouldn't execute if Form1 is already freed.

BIANCO

  • New member
  • *
  • Posts: 9
  • O Rio!? o Rio é um mundo cara!
Re: "exception class 'External :?'."
« Reply #16 on: February 12, 2018, 05:19:57 pm »
Yes, it had a public repository, but it's off the air, the lib is larger, I've just added example to the units I use, the original contains parts of a commercial project, I'm waiting for its response to publish in a git original and put here, to see what we can do to make this lib compilable in fpc on windows and linux then.

This is interesting that uses the cron mechanism similar to linux, I have not seen another equal in Pascal or Delphi.

rvk

  • Hero Member
  • *****
  • Posts: 3428
Re: "exception class 'External :?'."
« Reply #17 on: February 12, 2018, 05:22:38 pm »
Here are two patch files with changes which worked for me (attached).

You see I free the ThreadTeste in FormClose (which means a 5 second delay before the thread responds before closing).
The ThreadTeste.Free is done in FormClose so you need to set FreeOnTerminate to false.

As code:
Code: [Select]
--- testeCromis.org/cromis/Cromis.Scheduler.pas zo feb 11 01:56:48 2018
+++ testeCromis/cromis/Cromis.Scheduler.pas ma feb 12 17:15:50 2018
@@ -1071 +1071 @@ begin
-    {$IFDEF FPC}RTLEventSetEvent{$ELSE}SetEvent{$ENDIF}(@FTermEvent);
+    {$IFNDEF MSWINDOWS}RTLEventSetEvent(@FTermEvent){$ELSE}SetEvent(FTermEvent){$ENDIF};
@@ -1073 +1073 @@ begin
-    {$IFNDEF FPC}
+    {$IFDEF MSWINDOWS}
@@ -1087 +1087 @@ begin
-    {$IFDEF FPC}RTLEventDestroy{$ELSE}CloseHandle{$ENDIF}(@FTermEvent);
+    {$IFNDEF MSWINDOWS}RTLEventDestroy(@FTermEvent){$ELSE}CloseHandle(FTermEvent){$ENDIF};

Code: [Select]
--- testeCromis.org/unprincipal.pas zo feb 11 13:06:35 2018
+++ testeCromis/unprincipal.pas ma feb 12 17:23:45 2018
@@ -71,0 +72 @@ end;
+    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
@@ -164 +165 @@ begin
-  ThreadTeste.FreeOnTerminate := True;
+  ThreadTeste.FreeOnTerminate := false;
@@ -173,0 +175,10 @@ end;
+procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
+begin
+  if Assigned(ThreadTeste) then
+  begin
+    ThreadTeste.Terminate;
+    ThreadTeste.WaitFor;
+    ThreadTeste.Free;
+  end;
+end;
+
@@ -208 +219 @@ begin
-  Self.Terminate;
+  //Self.Terminate;
@@ -214 +225 @@ begin
-  try
+  //try
@@ -216,2 +227,2 @@ begin
-    LoadCronTab;
-    DisplayCronInfo;
+    Synchronize(@LoadCronTab);
+    Synchronize(@DisplayCronInfo);
@@ -220,3 +231,3 @@ begin
-  finally
-    OnTerminate(self);
-  end;
+  //finally
+  //  OnTerminate(self);
+  //end;
« Last Edit: February 12, 2018, 05:25:42 pm by rvk »

runner

  • New member
  • *
  • Posts: 12
Re: "exception class 'External :?'."
« Reply #18 on: February 12, 2018, 06:05:41 pm »
Hello people.

I am the original author of the Cromis library. The CRON scheduler is a part of it. So if I can help in any way, I will be glad to. Its quite some time since I used Lazarus / FPC for anything serious but still.

First lets clear the IFDEFS issue. They were added quite some time ago as someone asked me for a Linux FPC support. So I added it. I don't think I tested any of it in full extent. Under Delphi and Windows the unit is tested and use in production environment daily.

But looking over the IFDEFS at a glance I think they are correct. Basically the idea is to call windows API calls like SetEvent, WaitForSingleObject etc under Windows and Linux conterparts (RTL*) under Linux. So using  {$IFDEF WINDOWS} seems correct to me.

Naturally it would be even better to use TEvent and other now build in cross platform objects that Delphi exposes (and I am sure Lazarus RTL has them), but I don't have time to update the code atm.

Will check the code BIANCO sent me under Delphi and Lazarus to see where the issues are.

rvk

  • Hero Member
  • *****
  • Posts: 3428
Re: "exception class 'External :?'."
« Reply #19 on: February 12, 2018, 06:21:15 pm »
First lets clear the IFDEFS issue. They were added quite some time ago as someone asked me for a Linux FPC support. So I added it. I don't think I tested any of it in full extent. Under Delphi and Windows the unit is tested and use in production environment daily.

But looking over the IFDEFS at a glance I think they are correct. Basically the idea is to call windows API calls like SetEvent, WaitForSingleObject etc under Windows and Linux conterparts (RTL*) under Linux. So using  {$IFDEF WINDOWS} seems correct to me.
Yes, for Delphi under Windows and FPC under Linux the IFDEFs are fine. But for FPC under Windows it is not. For example, like I already said, the IFDEFs for MSWINDOWS and FPC are mixed (MSWINDOWS is also defined in FPC for Windows). The result is that FTermEvent is a THandle and it is created with CreateEvent (for Windows) but in .Stop there are several calls to RTLEvent because FPC is used in the define. Look for all FPC} in the source. They should NOT be there for FPC under Windows because FTermEvent is already a THandle due to the IFDEF MSWINDOWS.

I've created a patch where all IFDEF FPC is changed to IFNDEF MSWINDOWS (and IFNDEF FPC in IFDEF MSWINDOWS). In that case, for FPC under Windows the Windows-events are used and for FPC under Linux the RTLEvents are used.

Note: Under FPC for Windows both MSWINDOWS and FPC are defined. So use one or the other but don't mix them. If the source should run under Delphi for OSX or LINUX then you've got another problem but I don't think that's a target
« Last Edit: February 12, 2018, 06:23:26 pm by rvk »

BIANCO

  • New member
  • *
  • Posts: 9
  • O Rio!? o Rio é um mundo cara!
Re: "exception class 'External :?'."
« Reply #20 on: February 12, 2018, 06:33:20 pm »
\ O /

Your patches worked for the daughter threads correctly, now it's killing the Threas, meaning the events are not being triggered any more.

I'm just having the error if I try to kill the main Thread, from TForm1, can you attach your whole code?

runner

  • New member
  • *
  • Posts: 12
Re: "exception class 'External :?'."
« Reply #21 on: February 12, 2018, 06:40:20 pm »
First lets clear the IFDEFS issue. They were added quite some time ago as someone asked me for a Linux FPC support. So I added it. I don't think I tested any of it in full extent. Under Delphi and Windows the unit is tested and use in production environment daily.

But looking over the IFDEFS at a glance I think they are correct. Basically the idea is to call windows API calls like SetEvent, WaitForSingleObject etc under Windows and Linux conterparts (RTL*) under Linux. So using  {$IFDEF WINDOWS} seems correct to me.
Yes, for Delphi under Windows and FPC under Linux the IFDEFs are fine. But for FPC under Windows it is not. For example, like I already said, the IFDEFs for MSWINDOWS and FPC are mixed (MSWINDOWS is also defined in FPC for Windows). The result is that FTermEvent is a THandle and it is created with CreateEvent (for Windows) but in .Stop there are several calls to RTLEvent because FPC is used in the define. Look for all FPC} in the source. They should NOT be there for FPC under Windows because FTermEvent is already a THandle due to the IFDEF MSWINDOWS.

I've created a patch where all IFDEF FPC is changed to IFNDEF MSWINDOWS (and IFNDEF FPC in IFDEF MSWINDOWS). In that case, for FPC under Windows the Windows-events are used and for FPC under Linux the RTLEvents are used.

Note: Under FPC for Windows both MSWINDOWS and FPC are defined. So use one or the other but don't mix them. If the source should run under Delphi for OSX or LINUX then you've got another problem but I don't think that's a target

Yes you are correct. I examined the code and there is indeed the problem. The original DEFINES were fine, but I think that I added the schedule stop procedure code later and messed up the defines. The corrected procedure should be (rvk that are basically your changes, I am just posting the full code for clarity):

Code: Pascal  [Select]
  1. procedure TScheduledEvent.Stop;
  2. {$IFDEF MSWINDOWS}
  3. var
  4.   AResult: Cardinal;
  5. {$ENDIF}
  6. begin
  7.   if FRunning then
  8.   begin
  9.     // dealocate the event handler
  10.     FSchThread.OnTerminate := nil;
  11.     FRunning := False;
  12.  
  13.     // set the terminate event, so that the thread can safely exit
  14.     {$IFNDEF MSWINDOWS}
  15.       RTLEventSetEvent(@FTermEvent)
  16.     {$ELSE}
  17.       SetEvent(FTermEvent)
  18.     {$ENDIF};
  19.  
  20.     {$IFDEF MSWINDOWS}
  21.       // wait and block until the scheduling thread is finished
  22.       AResult := WaitForSingleObject(FSchThread.Handle, cShutdownTimeout);
  23.  
  24.       // check if we timed out
  25.       if AResult = WAIT_TIMEOUT then
  26.         TerminateThread(FSchThread.Handle, 0);
  27.     {$ELSE}
  28.       // wait and block until the scheduling thread is finished
  29.       RTLEventWaitFor(FSchThread, cShutdownTimeout);
  30.       FSchThread.Terminate;
  31.     {$ENDIF}
  32.  
  33.     // close the event handle and free the thread
  34.     {$IFNDEF MSWINDOWS}
  35.       RTLEventDestroy(@FTermEvent);
  36.     {$ELSE}
  37.       CloseHandle(FTermEvent);
  38.     {$ENDIF}
  39.     FreeAndNil(FSchThread);
  40.  
  41.     // fire the on schedule stop event
  42.     if Assigned(FOnScheduleStop) then
  43.       FOnScheduleStop(Self);
  44.   end;
  45. end;
  46.  

Basically it should all be WINDOWS based and not FPC based. As I said the DEFINES were made by request from a third party and I never really tested them under FPC or Linux. With this changes there are no more exceptions and the schedule stops as it should. As for the code being cross platform, I never claimed it is :) If it works fine, but I only maintain the Windows Delphi version of it. I really need to put the code into public versioning system so others can contribute.

Also BIANCO I checked your test example and it has several problems. You call GUI from threads without synchronize as others already stated and that is a no-go. Futhermore you don't really need your "TTheThread" class as scheduler already runs in its own thread and you have definied the callback as thread based. So in this case the schedule creates a new anonymous thread under which the task is then executed. Note that all code inside the callback (event) must also be made threadsafe, so any calls to GUI without sync are a no-go.

Also you need to check how to manage threads as you have several problems there also. Not criticising, just pointing it out, as multithreading is not easy and takes a lot of practise. I am doing it for almost 10 year already (heavy multithreading and multi processing) and still get bitten by it :)

« Last Edit: February 12, 2018, 06:42:14 pm by runner »

BIANCO

  • New member
  • *
  • Posts: 9
  • O Rio!? o Rio é um mundo cara!
Re: "exception class 'External :?'."
« Reply #22 on: February 12, 2018, 07:09:32 pm »
Thankfully for the remarks, I fully agree, others have already mentioned and made the amendments.

But, I did not check the Thread GUI, I

property ThreadTeste: TTheThread ......

as "main thread".

I will carry this functionality to a Daemon / Service so there will be no GUI, this was created
to validate the features.

Once again, thank you very much, even to all!

especially @runner and @rvk.

@Runner, your lib is great already said by email, it would be nice to come to github. :)

rvk

  • Hero Member
  • *****
  • Posts: 3428
Re: "exception class 'External :?'."
« Reply #23 on: February 12, 2018, 10:32:05 pm »
I'm just having the error if I try to kill the main Thread, from TForm1, can you attach your whole code?
I already showed you a patch of what I changed. I didn't change anything else.
I didn't even use Synchronize (which you should) but that shouldn't prevent the task from running.

Maybe you can show you code again.

But, I did not check the Thread GUI, I
property ThreadTeste: TTheThread ......
as "main thread".
ThreadTeste is NOT the main thread. The application itself (TApplication and the main form) is the main thread. Any other thread you use is another thread. Accessing components in any thread that are in other threads need to be done via Synchronize or Queue.
« Last Edit: February 12, 2018, 10:35:43 pm by rvk »

BIANCO

  • New member
  • *
  • Posts: 9
  • O Rio!? o Rio é um mundo cara!
Re: "exception class 'External :?'."
« Reply #24 on: February 12, 2018, 11:49:19 pm »
You're right! I expressed myself poorly

--------------

Of course, I'm attaching the final code.

https://drive.google.com/file/d/1MJiFqibY6JvzJdrZtbmI3XnVD-HxRwwb/view?usp=sharing

I think we can end this topic as resolved!

I kept creating a  "ThreadTeste" as property of TForm1 because
is part of the concept that I intend to use, a Thread just to monitor and inform the state of the Threads daughter
to a "console".

I do not know if this architecture is the best or the right one, so I'm going to use it anyway because it helps me
to know exactly what is happening with the program, the logs that I send today to the memo,
will be sent to a socket channel that I will use to monitor the daemon / service.

If you look at the example, it was not necessary to put the OnClose of Form1 to the destruction of ThreadTeste,
I kept the "Stop Thread" button, without errors, without memory leaks!

The auxiliary events,

OnScheduleRun,
OnScheduleStop,
OnScheduleAbort,
OnScheduleInvalid,
OnMaxEventsExecuted

are not triggered inside the Synchronize, do not belong to this time and therefore had no problem in uncommenting.

The current solution already caters me, in the future I will better study the management of this list of tasks including
and deleting items without having to stop the "ThreadTeste", which controls the logs.

I will probably put another method in the Synchronize to re-organize the list before entering the log, but this I will try to do first, its I can not get help from the forum.

I loved it, you were 1000!
« Last Edit: February 13, 2018, 01:01:20 am by BIANCO »

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus