Recent

Author Topic: Strange Delay() behavior  (Read 5406 times)

nfol

  • New Member
  • *
  • Posts: 17
Strange Delay() behavior
« on: November 14, 2018, 01:26:34 pm »
I made this very simple procedure:

--------------------------------
procedure tform1.btnlukclick(sender : tobject);

begin
  lblMessage.Caption := 'Message 1';
  Delay(2000);

  lblMessage.Caption := 'Message 2';
  Delay(2000);

  lblMessage.Caption := 'Message 3';
end; 
--------------------     

To use Delay(),  I included the unit CRT.

I expected this procedure to show 'Message 1' for 2 seconds, then 'Message 2' for 2 seconds, and finally 'Message 3'.

However, nothing happens for 4 seconds. Then 'Message 3' is shown.


If I comment the last line out, nothing happens for 4 seconds. Then 'Message 2' is shown.

If I comment the two last lines out, nothing happens for 2 seconds. Then 'Message 2' is shown.

If I comment the three last lines out, nothing happens for 2 seconds. Then 'Message 1' is shown.

If I comment all lines but the first line out, 'Message 1' is shown immediately as expected.


I do not understand this strange behaviour. It is like the Delay() statements comes to effect earlier than the statment before them, and that some Caption statements are ignored.

What am I missing?

Best regards,

Niels
 

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Strange Delay() behavior
« Reply #1 on: November 14, 2018, 01:35:34 pm »
I wouldn't recommend using Delay() that way (I'd use a TTimer instead, probably) but you can try this:

Code: Pascal  [Select][+][-]
  1. procedure tform1.btnlukclick(sender : tobject);
  2.  
  3. begin
  4.   lblMessage.Caption := 'Message 1';
  5.   Application.ProcessMessages;
  6.   Delay(2000);
  7.  
  8.   lblMessage.Caption := 'Message 2';
  9.   Application.ProcessMessages;
  10.   Delay(2000);
  11.  
  12.   lblMessage.Caption := 'Message 3';
  13. end;  
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

SnoopyDog

  • New Member
  • *
  • Posts: 26
Re: Strange Delay() behavior
« Reply #2 on: November 14, 2018, 02:13:26 pm »
The problem comes from the way how windows refreshes the captions in dialog elements, not from the delay routine!
If you set the caption of a label to a new value, then the "refresh" for this action does not happen immediately. Windows creates a refresh message and adds it to a queue. This message will be processed after all other pending messages in this queue were processed.
Windows does NOT work with multitasking here, all these messages will be processed from within the context of the main program. "Delay" stops the execution of the thread where it is called from for the specified time. Since it is called from the main program, it will also stop processing the message queue for the specified time. Background threads continue executing.
If you call
Code: [Select]
Application.ProcessMessages;before each call of "Delay", then you'll see what you've expected ;)

Timers are also triggered with windows messaging btw.
« Last Edit: November 14, 2018, 02:20:31 pm by SnoopyDog »

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Strange Delay() behavior
« Reply #3 on: November 14, 2018, 03:38:09 pm »
In a GUI app you should not use crt so also not use crt.delay. You just ran into the side effects this can cause....
You can use a timer as suggested, or just Sleep()
On Windows Delay calls just Sleep anyway. On Unix it calls the kernel function. The crt unit has a lot of initialization hooks (keyboard, mouse, video) that may fail in a GUI app or make the GUI app fail.., so really DON'T use it.

Aside: this can possibly be fixed/prevented:
Question for a Lazarus expert: is there anywhere in the Lazarus libraries (and fpGUI and MSEGui for that matter) a flag or unique variable that indicates a GUI application?
Then I can prepare a patch for the crt unit on the FPC side that tries to see if that is declared (similar to heapmm detection, with {$IF DECLARED(LAZGUI)}{$WARNING You can't use crt in a GUI application}{$ENDIF}). Maybe I can check if there is a console handle, but that maybe platform
« Last Edit: November 14, 2018, 04:14:56 pm by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Strange Delay() behavior
« Reply #4 on: November 14, 2018, 04:16:32 pm »
The problem comes from the way how windows refreshes the captions in dialog elements, not from the delay routine!
No it does not. It is caused by the use of the crt unit. See my other reply, which is the correct reply.
(Note I am an FPC expert, but not a Lazarus in-depth expert, hence the ask for advice)
« Last Edit: November 14, 2018, 04:18:51 pm by Thaddy »
Specialize a type, not a var.

SnoopyDog

  • New Member
  • *
  • Posts: 26
Re: Strange Delay() behavior
« Reply #5 on: November 14, 2018, 06:26:06 pm »
@Thaddy: It is good that you think you are an expert, but you are wrong!

The thread opener asked for the reason why he has this effect and I gave him the correct answer for a Windows operating system.
All other answers are suggestions what he could change but they don't answer his original question, because he wants to know why this happens.

Under Windows, "Sleep" or "Delay" suspend the current thread for the specified time and switch to any other available thread. If the suspended thread is the main thread (the application), then also the applications's messaging is suspended and the label doesn't get refreshed.
If you call "Application.ProcessMessages" before you call "Delay" or "Sleep", then the label gets refreshed as desired.

The same will happen, if you use Sleep and don't include the Crt unit...

If you don't believe it, just write a small test app.

Of course you can improve or modify the code, but this was not what he asked.

The code will behave the same in Delphi and in FPC / Lazarus...

Edit: lucamar already wrote in the second post that the refresh message gets processed by Windows when you call Application.ProcessMessages and I only explained why it behaves like this in Windows.

This is the FPC implementation of "Delay"

Code: [Select]
procedure Delay(MS: Word);
begin
  Sleep(ms);
end; { proc. Delay }   

But even if you don't include the Crt unit, you have the same behaviour...
« Last Edit: November 14, 2018, 06:49:42 pm by SnoopyDog »

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Strange Delay() behavior
« Reply #6 on: November 14, 2018, 06:49:55 pm »
@Thaddy: It is good that you think you are an expert, but you are wrong!
Nope. He should not use crt. Period. He should use sleep instead.
I can get very annoyed by such replies, so a well deserved real grumpy:  >:D >:D >:D >:D > is your fate. :D
If you talk nonsense you may be excused, just do not do it a second time about the same subject.

< most who know about grumpy on this forum will agree. the rest I ignore.>
« Last Edit: November 14, 2018, 06:53:00 pm by Thaddy »
Specialize a type, not a var.

SnoopyDog

  • New Member
  • *
  • Posts: 26
Re: Strange Delay() behavior
« Reply #7 on: November 14, 2018, 06:52:11 pm »
But the same will happen when you don't use the Crt unit and call "Sleep" Mr. "expert"...

I don't understand your problem btw...

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Strange Delay() behavior
« Reply #8 on: November 14, 2018, 06:55:30 pm »
Then debug your code some more.... using crt has side effects. I covered that. everything else can be nailed down to something else < the art of elimination dr Watson...>
<More grumps  >:D >:D >:D >
And sleep does work if you correct the code............. <sigh>
« Last Edit: November 14, 2018, 09:14:50 pm by Martin_fr »
Specialize a type, not a var.

SnoopyDog

  • New Member
  • *
  • Posts: 26
Re: Strange Delay() behavior
« Reply #9 on: November 14, 2018, 06:58:24 pm »
Please read the first post.

All your answers don't adress what was asked there and they are wrong!

I you write this very simple procedure:

--------------------------------
procedure tform1.btnlukclick(sender : tobject);

begin
  lblMessage.Caption := 'Message 1';
  Sleep(2000);

  lblMessage.Caption := 'Message 2';
  Sleep(2000);

  lblMessage.Caption := 'Message 3';
end;
--------------------     

And don't include the unit CRT, then still

I expected this procedure to show 'Message 1' for 2 seconds, then 'Message 2' for 2 seconds, and finally 'Message 3'.

However, nothing happens for 4 seconds. Then 'Message 3' is shown.


If I comment the last line out, nothing happens for 4 seconds. Then 'Message 2' is shown.

If I comment the two last lines out, nothing happens for 2 seconds. Then 'Message 2' is shown.

If I comment the three last lines out, nothing happens for 2 seconds. Then 'Message 1' is shown.

If I comment all lines but the first line out, 'Message 1' is shown immediately as expected.

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Strange Delay() behavior
« Reply #10 on: November 14, 2018, 07:04:10 pm »
NOT without calling Application.ProcessMessages AFTER the sleep/delay.
The logic is updated, but not the screen .
That's a basic lack of knowledge. If there is no paint message, the screen (which is the least important part of an application, thank you, won't get updated.)
« Last Edit: November 14, 2018, 09:16:17 pm by Martin_fr »
Specialize a type, not a var.

SnoopyDog

  • New Member
  • *
  • Posts: 26
Re: Strange Delay() behavior
« Reply #11 on: November 14, 2018, 07:08:38 pm »
But this is what I wrote in the 3rd post...

You did not write anywhere that "Application.ProcessMessages" will trigger Windows messaging to display the changed label text - this is what lucamar and me wrote and I explained why it happens.
You only wrote that the use of the Crt unit is the problem, but this is not true.

And the explanation why this happens under Windows is also correct. It comes from how Windows Messaging works. I also wrote this in post #3
« Last Edit: November 14, 2018, 07:12:31 pm by SnoopyDog »

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Strange Delay() behavior
« Reply #12 on: November 14, 2018, 08:23:34 pm »
Let's summarize, OK?

The OP's question was about why the label caption was changed only for the last string, and the reason is that Delay() prevents the application from processing its message queue.

The solution is to insert calls to Application.ProcessMessages() or--more circunscript--lblMessage.Repaint, the difference being that ProcessMessages() makes the application process *all* its pending messages while Repaint causes, as the documentation says, just an "immediate redraw of the control, bypassing the message queue."

Beyond this, as Thaddy correctly remarks, using CRT in a GUI application should be considered forbidden because the initialization and termination of this unit can cause lots of (relatively) hard to debug problems. The solution is to use Sleep() instead of Delay(), and delete CRT from the uses clause.

With all this in mind, this may be the (somewhat) correct code:

Code: Pascal  [Select][+][-]
  1. procedure tform1.btnlukclick(sender : tobject);
  2. begin
  3.   lblMessage.Caption := 'Message 1';
  4.   lblMessage.Repaint;
  5.   Sleep(2000);
  6.  
  7.   lblMessage.Caption := 'Message 2';
  8.   lblMessage.Repaint;
  9.   Sleep(2000);
  10.  
  11.   lblMessage.Caption := 'Message 3';
  12. end;
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

balazsszekely

  • Guest
Re: Strange Delay() behavior
« Reply #13 on: November 14, 2018, 08:52:24 pm »
Instead of Sleep I use the following method:
Code: [Select]
procedure Wait(dt: QWord);
var
  tc: QWord;
begin
  tc := GetTickCount64;
  while (GetTickCount64 < tc + dt) and (not Application.Terminated) do
    Application.ProcessMessages;
end;

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: Strange Delay() behavior
« Reply #14 on: November 14, 2018, 09:03:48 pm »
But this is what I wrote in the 3rd post...

You did not write anywhere that "Application.ProcessMessages" will trigger Windows messaging to display the changed label text - this is what lucamar and me wrote and I explained why it happens.
You only wrote that the use of the Crt unit is the problem, but this is not true.

And the explanation why this happens under Windows is also correct. It comes from how Windows Messaging works. I also wrote this in post #3
@snoopydog

As you know, you are correct.  In a straight API program a call to UpdateWindow (which bypasses the message queue) would be the way to ensure the window is refreshed/updated before the call to Sleep/Delay.

@Thaddy

You are right that using Crt in a GUI app is not a good idea but, as Snoopydog correctly pointed out, that is not the source of the problem the OP is having.  He is right and, your hammer is at least 3 feet away from the OP's nail.




(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018