Lazarus

Programming => General => Topic started by: GeneCode on November 08, 2021, 10:38:30 am

Title: Synchronize not working
Post by: GeneCode on November 08, 2021, 10:38:30 am
Hi.

I have a custom thread in my app.

Code: Pascal  [Select][+][-]
  1.  MainThread := TMainPortThread.Create(True); // With the True parameter it doesn't start automatically
  2.             if Assigned(MainThread.FatalException) then
  3.             raise MainThread.FatalException;
  4.             // Here the code initialises anything required before the threads starts executing
  5.             LoopPosM := 1;
  6.             MainThread.Start;  

Here is the constructor:
Code: Pascal  [Select][+][-]
  1. constructor TMainPortThread.Create(CreateSuspended: boolean);
  2. begin
  3.   FreeOnTerminate := True;
  4.   inherited Create(CreateSuspended);
  5. end;  

The thread works fine. But I cannot figure out why the call to Synchronize does not work.
I am calling the Synchronize on one of the TMainPortThread private methods in the MainPortThread
Execute method.

Code: Pascal  [Select][+][-]
  1. Synchronize(@UpdateGUI);  

UpdateGUI:

Code: Pascal  [Select][+][-]
  1. procedure TMainPortThread.UpdateGUI;
  2. // this method is only called by Synchronize(@ShowStatus) and therefore
  3. // executed by the main thread
  4. // The main thread can access GUI elements, for example Form1.Caption.
  5. begin
  6.  
  7.   PulseGenForm.Shape2.Brush.Color:=clLime;
  8.  
  9.  if (GUIMsg='HI') then begin
  10.     WriteLn('test');
  11.  end;
  12.  
  13.  GUIMsg := '';
  14. end;  

I can see in console, test is printed. That means this function is being called, but
the Shape2 color never changed.  %)

If you need other details let me know then i can add here. I am continuing to debug this.
Title: Re: Synchronize not working
Post by: loaded on November 08, 2021, 11:33:18 am
Hi,
I have a working example on the subject;

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { T_f_SecT }
  13.   T_f_SecT = class(TThread)
  14.   private
  15.     procedure Synchronous;
  16.   protected
  17.     procedure Execute; override;
  18.   end;
  19.  
  20.   { TForm1 }
  21.  
  22.   TForm1 = class(TForm)
  23.     Button1: TButton;
  24.     procedure Button1Click(Sender: TObject);
  25.   private
  26.  
  27.   public
  28.  
  29.   end;
  30.  
  31. var
  32.   Form1: TForm1;
  33.   d:Double=0;
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. { For Thread }
  39. procedure T_f_SecT.Synchronous;
  40. begin
  41.  Showmessage('Thread Synchronous Sum d :' + floattostr(d));
  42. end;
  43.  
  44. procedure T_f_SecT.Execute;
  45. var
  46.   i:integer;
  47.  
  48. begin
  49.  for i:=0 to high(Integer) do
  50.  begin
  51.  d:=d+i;
  52.  end;
  53.  Synchronize(@Synchronous);
  54. end;
  55.  
  56. { TForm1 }
  57.  
  58. procedure TForm1.Button1Click(Sender: TObject);
  59. var
  60.   Zcounter : T_f_SecT;
  61. begin
  62.   Zcounter:=T_f_SecT.Create(false);
  63. end;
  64.  
  65. end.
  66.  
Title: Re: Synchronize not working
Post by: devEric69 on November 08, 2021, 11:59:50 am
Hello,

Your method UpdateGUI should be part of your TForm. The " main thread " that can access GUI elements, for example Form1.Caption, ..., is managing the ...TForm's methods to Invalidate, Update, Paint its GUI controls (overall).
Title: Re: Synchronize not working
Post by: PascalDragon on November 08, 2021, 01:31:04 pm
Code: Pascal  [Select][+][-]
  1.  MainThread := TMainPortThread.Create(True); // With the True parameter it doesn't start automatically
  2.             if Assigned(MainThread.FatalException) then
  3.             raise MainThread.FatalException;
  4.             // Here the code initialises anything required before the threads starts executing
  5.             LoopPosM := 1;
  6.             MainThread.Start;  

Side note: you only need to check FatalException after the execution of the thread, but since you're using FreeOnTerminate that's not possible anyway.

UpdateGUI:

Code: Pascal  [Select][+][-]
  1. procedure TMainPortThread.UpdateGUI;
  2. // this method is only called by Synchronize(@ShowStatus) and therefore
  3. // executed by the main thread
  4. // The main thread can access GUI elements, for example Form1.Caption.
  5. begin
  6.  
  7.   PulseGenForm.Shape2.Brush.Color:=clLime;
  8.  
  9.  if (GUIMsg='HI') then begin
  10.     WriteLn('test');
  11.  end;
  12.  
  13.  GUIMsg := '';
  14. end;  

I can see in console, test is printed. That means this function is being called, but
the Shape2 color never changed.  %)

Then the problem is probably with the shape, but definitely not with the Synchronize. Does it work if you call the same code from e.g. a timer?

Your method UpdateGUI should be part of your TForm. The " main thread " that can access GUI elements, for example Form1.Caption, ..., is managing the ...TForm's methods to Invalidate, Update, Paint its GUI controls (overall).

That's of course a nicer separation of concerns, but simply from the point-of-view of the execution it doesn't matter whether the code is part of the thread or the form (as long as the thread only calls it only using Synchronize or Queue).
Title: Re: Synchronize not working
Post by: MarkMLl on November 08, 2021, 02:14:16 pm
That's of course a nicer separation of concerns, but simply from the point-of-view of the execution it doesn't matter whether the code is part of the thread or the form (as long as the thread only calls it only using Synchronize or Queue).

Thanks for that, had me worried.

MarkMLl
Title: Re: Synchronize not working
Post by: devEric69 on November 08, 2021, 02:56:55 pm
@MarkMLl: sorry for the worry.
@GeneCode: try adding an Application.ProcessMessages at the end of your method UpdateGUI.
Title: Re: Synchronize not working
Post by: MarkMLl on November 08, 2021, 03:13:55 pm
@MarkMLl: sorry for the worry.
@GeneCode: try adding an Application.ProcessMessages at the end of your method UpdateGUI.

No problem :-) I usually put it in the thread object since it's the neatest way of isolating any temporaries.

Agree with your point about the APM... in conjunction with Synchronize() but not with anything asynchronous which can cause recursion, and in any event it can take a lot of time if called repeatedly.

MarkMLl
Title: Re: Synchronize not working
Post by: marcov on November 08, 2021, 05:08:11 pm
 PulseGenForm.Shape2.Brush.Color changed the color for the next draw. But you don't draw anywhere?
Title: Re: Synchronize not working
Post by: GeneCode on November 12, 2021, 11:38:43 am

Side note: you only need to check FatalException after the execution of the thread, but since you're using FreeOnTerminate that's not possible anyway.

Thanks for the note. That make sense.

Then the problem is probably with the shape, but definitely not with the Synchronize. Does it work if you call the same code from e.g. a timer?

That's the thing, Shape does change color when I call from timer. It is just not changing with the Synchronize.

PulseGenForm.Shape2.Brush.Color changed the color for the next draw. But you don't draw anywhere?

I don't need to call draw because Synchronize is supposed to run the method in main program thread, no?

Anyway...

I think I saw a break that might have skipped that Synchronize call altogether. In which case a brain fart part of mine.
I'll check it further and get back here if that was the case.
Title: Re: Synchronize not working
Post by: marcov on November 12, 2021, 02:45:55 pm
PulseGenForm.Shape2.Brush.Color changed the color for the next draw. But you don't draw anywhere?

I don't need to call draw because Synchronize is supposed to run the method in main program thread, no?

True, and afaik it will set the brush color for the next draw to the specified color. But I suspect that doesn't trigger a redraw of whatever you draw.

Anyway...

I think I saw a break that might have skipped that Synchronize call altogether. In which case a brain fart part of mine.
I'll check it further and get back here if that was the case.
[/quote]
Title: Re: Synchronize not working
Post by: SymbolicFrank on November 12, 2021, 04:10:32 pm
@MarkMLl: sorry for the worry.
@GeneCode: try adding an Application.ProcessMessages at the end of your method UpdateGUI.
Sleep(0) or Sleep(1) is often a better alternative of ProcessMessages.
Title: Re: Synchronize not working
Post by: 440bx on November 12, 2021, 04:33:50 pm
Sleep(0) or Sleep(1) is often a better alternative of ProcessMessages.
Just an "academic" observation. Sleep() and ProcessMessages are two very different things.  A call to Sleep() has nothing to do with processing messages and, calling Sleep in a UI thread is, more often than not, a bad idea because it delays the repainting of the window.

Title: Re: Synchronize not working
Post by: MarkMLl on November 12, 2021, 04:36:59 pm
Sleep(0) or Sleep(1) is often a better alternative of ProcessMessages.

Are you absolutely sure of that? Application.ProcessMessages specifically causes the message queue to be checked, while Sleep() simply withholds the CPU for some number of mSec.

MarkMLl
Title: Re: Synchronize not working
Post by: cdbc on November 12, 2021, 05:39:37 pm
Hi
Have a look at Postmessage / Sendmessage...
From thread to main...
Regards Benny
Title: Re: Synchronize not working
Post by: GeneCode on November 17, 2021, 08:15:09 am
Just an "academic" observation. Sleep() and ProcessMessages are two very different things.  A call to Sleep() has nothing to do with processing messages and, calling Sleep in a UI thread is, more often than not, a bad idea because it delays the repainting of the window.

I agree with you. Sleep will block the UI thread for sure.

Hi
Have a look at Postmessage / Sendmessage...
From thread to main...
Regards Benny

Thanks for your suggestion, but I really don't want a too complicated solution.
Synchronize should've worked.


_____________________________________

Anyway, I was able to reproduce the problem in a test application.
It seems the Synchronize failed only in the child form. :o In the main form, it works well.

You can try it in a crude app I made. Pls ignore code quality  :P
Ps. I *THINK* my code is right. But if you find bug do let me know. Otherwise I am baffled as to why the UI is not updating in Synchronized methods in child form only.
Title: Re: Synchronize not working
Post by: GeneCode on November 23, 2021, 06:50:45 am
So I found out the cause of the problem. It was not related to Synchronize at all.
It has something to do with the child form that I created wrongly. (noobie mistake  :P)

Referring to the test project,

Form2 was autocreated at application run. And then I went and declared another instance of Form2 as "settings" and created another one
and show that one.

What i did to fix it was to remove the autocreate Form2 in Project1.lpr.

Code: Pascal  [Select][+][-]
  1.   RequireDerivedFormResource:=True;
  2.   Application.Initialize;
  3.   Application.CreateForm(TForm1, Form1);
  4.  // Application.CreateForm(TForm2, Form2);
  5.   Application.Run;


And then calling the Form2 directly when creating the form.

Code: Pascal  [Select][+][-]
  1.     Form2 := TForm2.Create(Form1);
  2.   Form2.Show;  

That's all. Anyway thank you to all of you that responded to my question. Very much appreciated.
Title: Re: Synchronize not working
Post by: SymbolicFrank on November 24, 2021, 03:19:18 pm
Sleep(0) or Sleep(1) is often a better alternative of ProcessMessages.

Are you absolutely sure of that? Application.ProcessMessages specifically causes the message queue to be checked, while Sleep() simply withholds the CPU for some number of mSec.

MarkMLl

Sleep gives other threads / processes a chance to run. If you run the CPU at max, everything that is waiting for something else, freezes. The same if you use threads: they all need time to process incoming data and respond. And then you don't need ProcessMessages, it happens automatically. Even the messages inside your own collection of threads require the Windows Kernel and the resources they use to have the processing time to do so.

If you think you need Application.ProcessMessages, the chances are it occasionally freezes your application because it is waiting for something else. Sleep fixes that.
Title: Re: Synchronize not working
Post by: Thaddy on November 24, 2021, 03:38:10 pm
no. the only Sleep() that does that under windows is Sleep(0) which is documented as "Relinquish Process" or depending on version "relinquish timeslice"'. All other values may still be blocking. A common error and a source of many a fake news.  see msdn.
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep

Very carefully read the official documentation...

Any other value- in the context of synchronization - is usually programmer error.
Title: Re: Synchronize not working
Post by: SymbolicFrank on November 24, 2021, 06:07:11 pm
no. the only Sleep() that does that under windows is Sleep(0) which is documented as "Relinquish Process" or depending on version "relinquish timeslice"'. All other values may still be blocking. A common error and a source of many a fake news.  see msdn.
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep

Very carefully read the official documentation...

Any other value- in the context of synchronization - is usually programmer error.

I did. (Well, not recently.) It says:

Quote from: Microsoft
This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds.

With a value of 0, it only gives up the remainder of its timeslice. With other values, it does that as well as waiting for the time to pass (and the next timer tick).

The idea is to have the OS manage the message passing, instead of trying to force that to happen yourself. Because you cannot force other processes to start processing their messages.

I use a small value, like 1, if I want to make sure the other processes get enough time to do their thing. In all other cases I use "WaitForObject" or something like a mutex.
Title: Re: Synchronize not working
Post by: PascalDragon on November 25, 2021, 09:27:32 pm
The idea is to have the OS manage the message passing, instead of trying to force that to happen yourself. Because you cannot force other processes to start processing their messages.

I use a small value, like 1, if I want to make sure the other processes get enough time to do their thing. In all other cases I use "WaitForObject" or something like a mutex.

This does however not help if you want your own application to process its own messages while you're in the thread that is responsible for processing the messages. Then you can Sleep all you want, but nothing will happen then.
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 04:01:05 am
MS has been saying that Sleep(0) causes a thread to relinquish the remainder of its time slice, probably for ever.  I've tested that on every version of Windows, probably since Win95 and, it does NOT.  That is simply and provably NOT true.  It didn't work that way even on NT4.

The easiest way to write a supposedly "well behaved" application is to put a call to Sleep(0) in a loop that breaks on some way in the future condition and watch one core go to 100% utilization _every_ time.  Sleep(0) is bad news (unless you want the program to consume an entire core, then it's made to order.)

@whoever wants to see what Sleep() really does:

Some time ago I posted a little test program that shows how Sleep() behaves (including source).  Anyone interested can find that test program (and a few others in the archive) at https://forum.lazarus.freepascal.org/index.php/topic,52973.msg391376.html#msg391376

Also, as @PascalDragon indicated, Sleep() is most definitely not a substitute for processing messages.  Sleep() tells the O/S not to schedule the thread for X amount of time, that's very different thing than what takes place when an application calls GetMessage.

One thing a programmer should keep in mind when reading documentation: documentation is the way it's supposed to work (IOW, what the programmer who wrote it dreamed) it isn't necessarily the way it _actually_ works.




Title: Re: Synchronize not working
Post by: Mr.Madguy on November 26, 2021, 07:11:13 am
One thing a programmer should keep in mind when reading documentation: documentation is the way it's supposed to work (IOW, what the programmer who wrote it dreamed) it isn't necessarily the way it _actually_ works.
Yeah, sometimes docs focus too much on unimportant details, while completely missing really important things. For example for me the biggest mystery - is IDXGIOutput.FindClosestMatchingMode behavior, that seems to be wrong due to wrong treatment of DXGI_MODE_SCALING. But it isn't explained anywhere.
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 09:33:33 am
MS has been saying that Sleep(0) causes a thread to relinquish the remainder of its time slice, probably for ever.  I've tested that on every version of Windows, probably since Win95 and, it does NOT.  That is simply and provably NOT true.  It didn't work that way even on NT4.

Does it? The way I've always read the docs (and this applies to most multitasking OSes) is that it says that it re-runs the scheduling algorithm: it makes no statement as to which thread will be resumed which could very easily be the same one that called Sleep(0) if it's the best candidate.

Quote
Also, as @PascalDragon indicated, Sleep() is most definitely not a substitute for processing messages.  Sleep() tells the O/S not to schedule the thread for X amount of time, that's very different thing than what takes place when an application calls GetMessage.

Agreed, a point I tried to make earlier.

Code: Pascal  [Select][+][-]
  1.   PostMessage(...);
  2.   Sleep(0)
  3.  

and

Code: Pascal  [Select][+][-]
  1.   Synchronize(...)
  2.  

are not equivalent.

MarkMLl
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 11:43:25 am
Does it? The way I've always read the docs (and this applies to most multitasking OSes) is that it says that it re-runs the scheduling algorithm: it makes no statement as to which thread will be resumed which could very easily be the same one that called Sleep(0) if it's the best candidate.
That is true but, it quickly becomes very suspicious when it is _always_ the thread that executed the Sleep(0) that gets immediately rescheduled. The results of testing Sleep(0)'s behavior indicate it _renews_ the time slice (the opposite of what the documentation states.)  If the thread was truly giving up the remainder of its time slice, it could not be using 100% of a core because there would be some portion of the time slice given up in each call.
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 11:55:53 am
If the thread was truly giving up the remainder of its time slice, it could not be using 100% of a core because there would be some portion of the time slice given up in each call.

Unless there were nothing else scheduled ready to run, i.e. everything else on the system were waiting on a (kernel) timer or event.

In any event, Sleep()- with any parameter- is not equivalent to APM.

MarkMLl
Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 12:05:26 pm
That is true but, it quickly becomes very suspicious when it is _always_ the thread that executed the Sleep(0) that gets immediately rescheduled. The results of testing Sleep(0)'s behavior indicate it _renews_ the time slice (the opposite of what the documentation states.)  If the thread was truly giving up the remainder of its time slice, it could not be using 100% of a core because there would be some portion of the time slice given up in each call.

Could it be that sleep, as an optimization, rather than always rescheduling, checks if there is a process in the queue for the current CPU and if so does reschedule and in all other cases just renews the time slice to not have to go through the scheduler?

It would be interesting to see how two processes on the same CPU (e.g. via CPU pinning in task manager) behave if one does the following:
Code: Pascal  [Select][+][-]
  1. While True do;
and the other one does:
Code: Pascal  [Select][+][-]
  1. While True do Sleep(0);
I would guess that the second one should get a much smaller slice of the CPU time. But sadly I don't have the ability to test it right now
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 12:08:20 pm
@Warfley: ISWYM, thread affinity in Linux terms.

MarkMLl
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 12:36:06 pm
If the thread was truly giving up the remainder of its time slice, it could not be using 100% of a core because there would be some portion of the time slice given up in each call.

Unless there were nothing else scheduled ready to run, i.e. everything else on the system were waiting on a (kernel) timer or event.
The problem is a call to Sleep() is, theoretically, a way to tell the CPU "don't schedule me for x amount of time" and obviously the scheduler isn't honoring that request. If it did, it would be impossible for the thread to consume 100% of a core because there would be some time given up.  The fact that the CPU utilization is at 100% indicates no time is given up.    Also, the O/S, most of the time, has internal housekeeping chores to take care of (such as zeroing out free pages, getting them ready to be handed to a process that will later, at some point in time, make a memory request), IOW, most of the time, the O/S has something to do other than rescheduling a thread that explicitly asked not be rescheduled until the next cycle.


In any event, Sleep()- with any parameter- is not equivalent to APM.
Absolutely.
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 12:57:59 pm
The problem is a call to Sleep() is, theoretically, a way to tell the CPU "don't schedule me for x amount of time" and obviously the scheduler isn't honoring that request. If it did, it would be impossible for the thread to consume 100% of a core because there would be some time given up.  The fact that the CPU utilization is at 100% indicates no time is given up.    Also, the O/S, most of the time, has internal housekeeping chores to take care of (such as zeroing out free pages, getting them ready to be handed to a process that will later, at some point in time, make a memory request), IOW, most of the time, the O/S has something to do other than rescheduling a thread that explicitly asked not be rescheduled until the next cycle.

But it /is/ honouring it. Sleep() is defined as taking a parameter in (usually, depending on OS) mSec representing a minimum wallclock time, not a number of cycles of system or application execution. Since a finite (albeit small) amount of wallclock time is taken by the syscall itself, Sleep(0) is behaving as expected.

More significant is whether Sleep(1) reliably pauses for at least one complete mSec on a system where the tick rate is substantially slower than that e.g. every 10 mSec. Or for that matter whether an hypothetical syscall to delay by at least number of ticks- let's call it yield(t)- rounded up or down, i.e. whether yield(1) were guaranteed to delay for at least 10 mSec on a system with 100 ticks per sec or if it might delay for significantly less than that if called just before a tick interrupt. A subsidiary consideration is whether any OSes drift their tick timing to accommodate sleep/yield syscalls, or if it's always guaranteed to stay in sync with the wallclock.

MarkMLl
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 01:27:15 pm
But it /is/ honouring it.
It isn't and the fact that it consumes 100% of a core proves it.  If the thread was sleeping for _any_ amount of time, it wouldn't be consuming 100% of a core.

More significant is whether Sleep(1) reliably pauses for at least one complete mSec on a system where the tick rate is substantially slower than that e.g. every 10 mSec.
The precision of the time slice given and the amount of time elapsed between time slices are a completely different matter.

The bottom line is: if a thread sleeps then it cannot consume 100% of a CPU.

Sleep(0) is the simplest and fastest way to write a program that is a CPU hog.
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 01:36:58 pm
It isn't and the fact that it consumes 100% of a core proves it.  If the thread was sleeping for _any_ amount of time, it wouldn't be consuming 100% of a core.

Are you trying to tell me that your monitoring utility calculates and displays this to full floating point precision? Because these days the amount of time required for the scheduler to spin amounts to a mere rounding error.

MarkMLl
Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 02:11:33 pm
It would be interesting to see how two processes on the same CPU (e.g. via CPU pinning in task manager) behave if one does the following:
Code: Pascal  [Select][+][-]
  1. While True do;
and the other one does:
Code: Pascal  [Select][+][-]
  1. While True do Sleep(0);
I would guess that the second one should get a much smaller slice of the CPU time. But sadly I don't have the ability to test it right now

Got to do the test. Started two processes, spin.exe and sleep.exe. Spin spins with:
Code: Pascal  [Select][+][-]
  1. While True do;
While sleep does:
Code: Pascal  [Select][+][-]
  1. While True do Sleep(0);
When I pin then to different cores (Spin to CPU 10 and Sleep to CPU11) both take up 100% of that CPU (and 4% of the total usage, which exactly what you would expect when using 24 virtual CPUs). If I pin both on them to the same core (CPU 10), the spin takes up 100% of CPU time while sleep takes up 0% (of course rounding errors are included).

So Sleep(0) is behaving exactly as expected. It will call the scheduler and if there is no other process to schedule (because there are simply not so many tasks on a multicore system), it will immediately be rescheduled resulting in 100% CPU usage anyway.
If there is another process that takes CPU time, this process will be scheduled instead, resulting in a much smaller CPU share.
For good measure I also checked what happens when I assign two spin processes to the same CPU, which results in both of them getting 50% of the CPU time. And the same happens when starting two sleep processes on the same CPU.

Therefore Sleep behaves exactly as expected. It's just that the scheduler is so fast that it does not have a notable impact on the CPU usage
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 02:20:45 pm
Are you trying to tell me that your monitoring utility calculates and displays this to full floating point precision?
No, that's not what I've tried to say.  What I say is very simple: if the thread was giving up part of its time slice then it would NOT show a _consistent_ usage of 100% of a CPU  because the thread would actually spend some time _sleeping_.

As far as the utilities to watch the process, those include Process Explorer, Process Hacker and Task Manager among others.  They all agree, Sleep(0) causes a thread that has a loop with such a call to consume 100% of a core.  Also, the CPU says it too but, a different way, the fan starts revving faster.  Not only you can see the 100%, on some machines you can hear it too.


So Sleep(0) is behaving exactly as expected. It will call the scheduler and if there is no other process to schedule (because there are simply not so many tasks on a multicore system), it will immediately be rescheduled resulting in 100% CPU usage anyway.
Absolutely not.   Sleep tells the scheduler "don't schedule me", "don't give me clock cycles", it does not mean "put me to work", it means: for whatever the remaining amount of time I had left in the time slice, I want to do _nothing_.   It tells the scheduler, NOT to schedule the thread and, if the scheduler was doing that, that thread could never use 100% of a core.

Sleep does NOT behave as documented.  It does NOT.  If it did, the thread would not consume 100% of the CPU.

What you guys are saying is that, if some thread calls Sleep(3000) but, the system has nothing to do, the scheduler would reschedule the thread immediately completely disregarding the request to not be given any clock cycles the specified amount of time and, we know that is not what happens _except_ when the call is Sleep(0).

You want to write lousy code that hogs a cpu, put calls to Sleep(0) in there.  Be my guest.



Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 02:29:50 pm
@440bx: I'm disappointed that at your age you've never learnt what "zero" means :-)

MarkMLl
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 02:41:20 pm
@440bx: I'm disappointed that at your age you've never learnt what "zero" means :-)

MarkMLl
At whatever your age is, I'm disappointed you haven't learned the meaning of "whatever remains of the time slice", which will very rarely be zero.
Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 03:07:47 pm
Absolutely not.   Sleep tells the scheduler "don't schedule me", "don't give me clock cycles", it does not mean "put me to work", it means: for whatever the remaining amount of time I had left in the time slice, I want to do _nothing_.   It tells the scheduler, NOT to schedule the thread and, if the scheduler was doing that, that thread could never use 100% of a core.

Sleep does NOT behave as documented.  It does NOT.  If it did, the thread would not consume 100% of the CPU.
Let's see the documentation: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
Quote
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.
"If there are no other threads ready to run [...] the thread continues execution" sounds exactly as if it would result in 100% CPU usage. This behavior is fully compliant with the documentation. Also note that the doc specifically mentions that the function returns immediately, so it doesn't even go through the scheduler. It simply checks if there is another thread in queue, if yes it calls the scheduler if no it simply returns and has no effect on the scheduling behavior
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 03:27:30 pm
Let's see the documentation: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep
Quote
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.
"If there are no other threads ready to run [...] the thread continues execution" sounds exactly as if it would result in 100% CPU usage. This behavior is fully compliant with the documentation. Also note that the doc specifically mentions that the function returns immediately, so it doesn't even go through the scheduler. It simply checks if there is another thread in queue, if yes it calls the scheduler if no it simply returns and has no effect on the scheduling behavior
I'm pleased you read the documentation.

The documentation also says this:(look in the Remarks)
Quote
If you specify 0 milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately.
Supposedly, the thread relinquishes the remainder of its time slice (that's what the documentation says), goes into the ready state and, there is supposedly no guarantee that it will run immediately (yet, "amazingly" it does.)

The part that in bold is very clear.  If the thread relinquished the remainder of its time slice, it could not consume 100% of CPU.  The only way that can happen is if the scheduler does not put the thread to sleep for the remainder of the time slice and, worse, to consume 100% of the CPU, the scheduler has to reschedule it immediately, every time.

Too bad I can't relinquish money that way. 

Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 03:49:25 pm
Supposedly, the thread relinquishes the remainder of its time slice (that's what the documentation says), goes into the ready state and, there is supposedly no guarantee that it will run immediately (yet, "amazingly" it does.)

The part that in bold is very clear.  If the thread relinquished the remainder of its time slice, it could not consume 100% of CPU.  The only way that can happen is if the scheduler does not put the thread to sleep for the remainder of the time slice and, worse, to consume 100% of the CPU, the scheduler has to reschedule it immediately, every time.

Too bad I can't relinquish money that way.
No the scheduler doesn't, as seen in my example where I pinned the spin and the sleep process to the same CPU. It only does so if no other thread is intendet to be scheduled on the CPU. If there is another thread requiring time on that CPU the process will yield
It looks to me as if you have a pretty outdated view on how modern schedulers work. The goal of the scheduler (besides not starving processes) is first and foremost to maximize cache locality. So if you have a process that yields it's time on one of the CPUs, the scheduler will prioritize rerunning this process on the same CPU immediately if possible.
If you have another process waiting to be scheduled, but hasn't been on a CPU for a while, it does not have any cache on the CPUs anymore, meaning that it doesn't matter for the OS on which CPU to schedule it. All are equally bad. While the process that just had a sleep 0 has it's cache still being hot, so the OS will try to reschedule it. So accasionally scheduled processes (long sleeps or event driven sleeps) will be scheduled on the CPUs such that the regular processes (those that only yield to the CPU to not starve the CPU) can continue on the same CPU.

Modern CPUs have >8 cores with hypethreading >16 virtual cpus. Unless I am doing something requireing a lot of parallel computing power, there is always a way for the OS to implicetly pin regular tasks to certain CPUs such that it will be optimized for cache locality.

Note that cache locality does not necessarily only referrs to the L1-L3 caches of the CPUs in this example, but also to the CPU and memory state. Loading a process onto the CPU requires loading all the registers and memory pages. So basically the OS always wants to keep long running processes pinned to a single CPU and do all the short running tasks on other CPUs, as this minimizes loading times and therefore time the CPU is not advancing any programs. "Modern" schedulers also referr to like since the inception of UNIX, so they aren't that modern anymore.

Your assumptions on how sleep "should" work are only valid on a system under very high load or on a system with only very old CPUs with only a few cores.
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 04:38:54 pm
After it's all said and done, if you use Sleep(0) in a program, one core will be pegged at !00% and your users are not going to appreciate that.

TD32.exe (Turbo Debugger) has one of those.  It was a problem.  I patched the executable to do a Sleep(1), after that, everything nice and smooth the way it should be. 
Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 05:16:47 pm
Yes sleep(0) will not reduce the CPU load, thats not the goal, it just yields to other processes so you don't starve them. If you want to actually reduce CPU usage you need to actually slow down your program (e.g. with sleep > 0)
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 05:35:57 pm
Yes sleep(0) will not reduce the CPU load, thats not the goal, it just yields to other processes so you don't starve them.
Doesn't it strike you as a bit "unusual" that a function that is supposed to yield to other processes consumes 100% of a CPU in order to get that done ?

Just what every laptop user dreams of ...  ;)
Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 05:45:47 pm
Yeah but it makes sense, when calling sleep you say: "Let other CPUs run but schedule me again as soon as possible after the the ms have passed". If that time is 0 you say: "Let others have the CPU but reschedule me as soon as possible". So this is exactly what you would expect
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 05:52:09 pm
Yes sleep(0) will not reduce the CPU load, thats not the goal, it just yields to other processes so you don't starve them.
Doesn't it strike you as a bit "unusual" that a function that is supposed to yield to other processes consumes 100% of a CPU in order to get that done ?

Just what every laptop user dreams of ...  ;)

If power consumption is a factor I'm not convinced that running for 20 minutes at 50% is more efficient than running for 10 minutes at 100%.

Note that I'm saying "not convinced" here: on the one hand you've got overheads due to disc, backlight, PSU inefficiency etc. which will broadly vary proportional to time, on the other you've got battery life which could conceivably vary inverse to time.

But can I please point out that the only thing that is remotely relevant to OP's question is use of Application.ProcessMessages. All of this business about Sleep() etc. is spurious.

MarkMLl
Title: Re: Synchronize not working
Post by: loaded on November 26, 2021, 05:54:47 pm
Yes sleep(0) will not reduce the CPU load, thats not the goal, it just yields to other processes so you don't starve them. If you want to actually reduce CPU usage you need to actually slow down your program (e.g. with sleep > 0)
Why use sleep when you can suspend and use resume.
They have no load on the processor.
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 06:00:31 pm
Why use sleep when you can suspend and use resume.
They have no load on the processor.

Because sleep(t) means "pause this thread for t mSec", i.e. it doesn't need anything external to kick it back into life. It's the sort of thing you'd use if e.g. enforcing a protocol delay in background communications.

MarkMLl
Title: Re: Synchronize not working
Post by: loaded on November 26, 2021, 06:07:22 pm
Because sleep(t) means "pause this thread for t mSec", i.e. it doesn't need anything external to kick it back into life. It's the sort of thing you'd use if e.g. enforcing a protocol delay in background communications.
I thought the use of sleep or application.processmessage was considered negative from a programming perspective.
Is sleep used in professional programs?
Title: Re: Synchronize not working
Post by: Warfley on November 26, 2021, 06:39:11 pm
Yes of course, sleep is very usefull if you have to wait for a certain amount of time (of course not in interactive programs like GUIs, but for threads or non interactive programs it is really usefull)
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 06:41:21 pm
I thought the use of sleep or application.processmessage was considered negative from a programming perspective.
Is sleep used in professional programs?

This breaks down into two issues.

Apropos APM: you can't use it in a background thread, if you've got a tight loop in the main thread you /have/ to use it otherwise the GUI will be unresponsive.

Apropos sleep(): use it where necessary in a background thread, you can't use it in the main thread since it will make the GUI unresponsive (i.e. the main loop not running so there is nothing to read and despatch mouse/keyboard/etc. events.

If you find yourself using either of those to avoid a race condition, /then/ you're doing it wrong.

MarkMLl
Title: Re: Synchronize not working
Post by: 440bx on November 26, 2021, 06:43:55 pm
But can I please point out that the only thing that is remotely relevant to OP's question is use of Application.ProcessMessages. All of this business about Sleep() etc. is spurious.
Absolutely.  At this stage I'd like to point out that the whole thing about Sleep() took a life of its own because SymbolicFrank suggested that Sleep() could be used as a better alternative to replacement than Application.ProcessMessages. 

As far as CPU usage goes, a well written application avoids gratuitously consuming CPU which is what Sleep(0) ends up doing and, that is particularly bad on laptops because it drains the battery for nothing.



I thought the use of sleep or application.processmessage was considered negative from a programming perspective.
Is sleep used in professional programs?
Application.ProcessMessages is usually negative now because today's OSs support multi-threading and implement preemptive multitasking.  Application.ProcessMessages is something that was often necessary in the versions of Windows that required cooperative multitasking, their scheduler depended on the program voluntarily relinquishing the CPU to the scheduler and processing messages which was the gateway to those O/Ss schedulers regaining control.

Sleep (except Sleep(0)) is usually an indicator that the programmer doesn't want to gratuitously waste clock cycles which, obviously, is detrimental to the smooth operation of the system as a whole.  That said, even Sleep (even if not zero) can be misused.  For instance, calling Sleep(somenonzerovalue) in a GUI thread is likely always a bad idea (as @MarkMLI pointed out while I was typing this post.)

In addition to what @Warfley mentioned, there are times when the need to poll for a non-critical event is needed, that's a shoe-in for Sleep.
Title: Re: Synchronize not working
Post by: loaded on November 26, 2021, 07:05:55 pm
MarkMLl and Warfley and 440bx  thank you very much for the enlightening information.
A little off topic:
I have a lot of questions in my mind, one of them is;
Does the running speed of threads change according to the language they are written in?
So is it possible that a thread written in a different language needs sleep, while a thread written in another language does not need sleep?
Title: Re: Synchronize not working
Post by: MarkMLl on November 26, 2021, 07:57:15 pm
MarkMLl and Warfley and 440bx  thank you very much for the enlightening information.
A little off topic:
I have a lot of questions in my mind, one of them is;
Does the running speed of threads change according to the language they are written in?
So is it possible that a thread written in a different language needs sleep, while a thread written in another language does not need sleep?

Hypothetically: if a program were written in a reincarnation of BASIC which guaranteed that it behaved the same as some particular model of 8-bit home computer, then it would be reasonable for this to be implemented by intercalating a sleep() after every few emulated opcodes.

Apart from that... I'd be surprised if the language per se made any difference. I'd be surprised if the OS or the CPU said "Aha, I can see this is a non-standard calling convention so I'm going to insert execution delays"... and then I'd go looking for an alternative platform, since that almost certainly indicated something dodgy going on. And in that extreme case, while the OS might insert sleep(t) I'd not expect the CPU to, since in the general case the CPU is thread-agnostic (special cases: Sun "Niagara", Intel "Hyperthreads", and similar now implemented by others... but I'm not sure whether the CPU can act as a scheduler there other than as dictated by the cache being filled).

Again hypothetically: there have been attempts at various dataflow languages and there was also Mystic Pascal, many if not all of which had some measure of multithreading implicit to their implementation and which /might/ have needed explicit delays to allow background activities to complete. However since that would not have been implemented by the OS (they generally predate the concept of OS-supported threads significantly) it would be strictly incorrect to say that sleep() was being used. Ditto for coroutines in Modula-2 etc.

Finally, as a counterexample, there have been cases where development environments recognised that they were being asked to compile a benchmark such as- notoriously- the Sieve of Eratosthenes, and messed about with the code generation to make the result look good. Also there are languages such as APL which can do some quite surprising things to improve apparent efficacy... note that I'm not saying efficiency here since it is a regrettable fact that people who promoted them were oblivious to real-World constraints like finite memory.

Hope that isn't too vague. I suggest that further discussion might be better continued in the "Other" topic.

Later, rereading:

"So is it possible that a thread written in a different language needs sleep, while a thread written in another language does not need sleep?"

Strictly speaking, there is no reason why code written in one language should be faster or slower than equivalent code written in a different language. However different /implementations/ may behave very differently, e.g. if one is highly-optimised native code while another is purely interpretive.

And if the question really is "is an *explicit* sleep() ever needed to get threads to synchronise", the unavoidable answer is "if that is the case you're doing something badly wrong".

However, "doing it right" might involve various synchronisation or IPC (Inter-Process Communication) facilities provided by the runtime library or the OS, and they will almost certainly have *implicit* invocations of sleep() internally.

Alternatively, if the question is about e.g. implementing a communications protocol where a fragment in Pascal might look like this:

Code: Pascal  [Select][+][-]
  1. const
  2.   turnaround= 50;
  3.  
  4. begin
  5.   SerSetRTS(serHandle, true);
  6.   Sleep(turnaround);                    // Artificial pacing
  7.   try
  8. ...
  9.     while not (ignoreCts or SerGetCts(serHandle)) do
  10.       Sleep(10);
  11. ...
  12.   finally
  13.     SerDrain(serHandle);
  14.     Sleep(turnaround);                  // Artificial pacing
  15.     SerSetRTS(serHandle, false);
  16.     Sleep(turnaround)                   // Artificial pacing
  17.   end
  18. end { TSerialThread.sendMessage } ;
  19.  

I think it would be incorrect to re-implement it in e.g. interpreted BASIC without doing /something/ to ensure that the turnaround time mandated by the protocol was observed... even if the current BASIC implementation and platform weren't fast enough to make it an issue (remember the number of programs with timing that went wildly wrong when the IBM AT came out?).

MarkMLl
Title: Re: Synchronize not working
Post by: loaded on November 26, 2021, 08:20:51 pm
MarkMLl, thank you very much for taking your precious time to reply. I will try to understand what you wrote by giving up a little sleep.
For now at least I learned about the Sieve of Eratosthenes.
Title: Re: Synchronize not working
Post by: Warfley on November 27, 2021, 02:07:31 pm
A few more practical examples for sleep:
If you write a Game that, due to very low graphic requirements runs crazy fast on modern hardware (like 500fps or so) you can use sleep to slow down the process to limit the framerate to 60fps.

Or, if you want to write a TCP connect with a timeout, you need to do it non blocking. Normal, blocking sockets will just take your thread off the CPU until the operation was successful. So you would require a second thread to kill the connect on a timeout, which is a waste of resource. Rather you can use non-blocking sockets, which will return immediatly each call with a message that the call didn't finish. You can then just wait until that call finished and continue:
Code: Pascal  [Select][+][-]
  1. function TimeoutConnect(Socket: TSocket; Addr: String; Port: Integer; Timeout: Int64): Boolean;
  2. var
  3.   StartTime: Int64;
  4. begin
  5.   StartTime := GetTickCount64;
  6.   Result := Connect(Socket, addr, port);
  7.   if not Result and SocketWasBlocking then
  8.   begin
  9.     while (GetTickCount - Start) < TimeOut do
  10.       if ConnectionEstablished(Socket) then
  11.          Exit(True)
  12.       else
  13.         Sleep(10);
  14.     Result := False;
  15.   end;
  16. end.
This way you don't waste CPU time (which just costs energy)

So there are a few use-cases for sleep
Title: Re: Synchronize not working
Post by: Mr.Madguy on November 27, 2021, 02:38:30 pm
My opinion: yeah, it can be harder to implement, but in such cases, when one needs async processes and API doesn't provide some built-in event mechanism - it's better to use threads. It's very bad idea to use any blocking API like Sleep in modern "passive" applications. It will most likely cause waste of CPU resources. Some cycles would be "lost" due to "sleeping" overhead and some cycles would be wasted due to constantly "waking up" to check external state.  It's always better to use something like WaitForSingleObject or WaitForMultipleObjects inside thread. In this case there would be three methods to unblock thread: 1) You can unblock it by your own event - for example to terminate thread. 2) You can unblock it on timeout, if you need to do some tasks from time to time. 3) End you can unblock it by actual event, you need to wait for. This way thread will only consume CPU time, when it's really needed. And this way it could be woken at any moment without any overhead.

At the end it's not that hard:
Code: Pascal  [Select][+][-]
  1.   TPausableThread = class(TThread)
  2.     protected
  3.       PauseEvent:TEvent;
  4.       procedure Pause(ATimeout:Cardinal);
  5.     public
  6.       constructor Create;
  7.       destructor Destroy;override;
  8.       procedure StopAndWaitFor;
  9.   end;
  10.  
  11.   TPingThread = class(TPausableThread)
  12.     protected
  13.       Gateway:String;
  14.       PingSend:TPingSend;
  15.       FOnPingFailed:TNotifyEvent;
  16.       procedure Execute;override;
  17.       procedure DoPingFailed;
  18.     public
  19.       constructor Create(AGateway:String);
  20.       destructor Destroy;override;
  21.       property OnPingFailed:TNotifyEvent read FOnPingFailed write FOnPingFailed;
  22.   end;
  23.  
  24. {TPausableThread}
  25.  
  26. constructor TPausableThread.Create;
  27. begin
  28.   inherited Create(True);
  29.   PauseEvent := TSimpleEvent.Create;
  30. end;
  31.  
  32. destructor TPausableThread.Destroy;
  33. begin
  34.   PauseEvent.Free;
  35.   inherited Destroy;
  36. end;
  37.  
  38. procedure TPausableThread.StopAndWaitFor;
  39. begin
  40.   Terminate;
  41.   PauseEvent.SetEvent;
  42.   WaitFor;
  43. end;
  44.  
  45. procedure TPausableThread.Pause(ATimeout:Cardinal);
  46. begin
  47.   PauseEvent.WaitFor(ATimeout);
  48. end;
  49.  
  50. {TPingThread}
  51.  
  52. constructor TPingThread.Create(AGateway:String);
  53. begin
  54.   inherited Create;
  55.   Gateway := AGateway;
  56.   PingSend := TPingSend.Create;
  57. end;
  58.  
  59. destructor TPingThread.Destroy;
  60. begin
  61.   PingSend.Free;
  62.   inherited Destroy;
  63. end;
  64.  
  65. procedure TPingThread.Execute;
  66. begin
  67.   while not Terminated do begin
  68.     if not PingSend.Ping(Gateway) then begin
  69.       Queue(@DoPingFailed);
  70.     end;
  71.     Pause(60000);
  72.   end;
  73. end;
  74.  
  75. procedure TPingThread.DoPingFailed;
  76. begin
  77.   if Assigned(OnPingFailed) then OnPingFailed(Self);
  78. end;
  79.  
Title: Re: Synchronize not working
Post by: MarkMLl on November 27, 2021, 04:03:48 pm
But all you've done there is replace Sleep() in a thread with Pause(), and wrapped it with event handling.

Even if you were waiting for a library routine to complete something, there would still be something comparable with Sleep() involved. It might not be Sleep() as such since it might have separate code in the OS to detect the termination condition, but there would still be something which had the effect "suspend this thread but check occasionally to see if some condition is satisfied".

Interestingly, back in the earliest days of multitasking OSes, one ALGOL-60 implementation of which I'm aware had a procedure which allowed an (unprivileged) user's program to suspend itself until an absolute memory address was changed. I can't remember what this was supposed to be used for, but I'm sure that it really was a nightmare since a program could be moved to disc and then back to core while running at which point there was no guarantee that the absolute address being watched was still in its own storage area.

MarkMLl
Title: Re: Synchronize not working
Post by: Mr.Madguy on November 27, 2021, 04:37:23 pm
But all you've done there is replace Sleep() in a thread with Pause(), and wrapped it with event handling.

Even if you were waiting for a library routine to complete something, there would still be something comparable with Sleep() involved. It might not be Sleep() as such since it might have separate code in the OS to detect the termination condition, but there would still be something which had the effect "suspend this thread but check occasionally to see if some condition is satisfied".

Interestingly, back in the earliest days of multitasking OSes, one ALGOL-60 implementation of which I'm aware had a procedure which allowed an (unprivileged) user's program to suspend itself until an absolute memory address was changed. I can't remember what this was supposed to be used for, but I'm sure that it really was a nightmare since a program could be moved to disc and then back to core while running at which point there was no guarantee that the absolute address being watched was still in its own storage area.

MarkMLl
This example shows all three conditions, i.e. timeout condition is one of them. But it's not necessary. The biggest difference here - you can interrupt pause manually, that is done in StopAndWaitFor not to wait for whole minute for thread to terminate. So block can actually be infinite. No reason to unblock from time to time to check thread termination condition.
Title: Re: Synchronize not working
Post by: Warfley on November 27, 2021, 07:35:01 pm
My opinion: yeah, it can be harder to implement, but in such cases, when one needs async processes and API doesn't provide some built-in event mechanism - it's better to use threads. It's very bad idea to use any blocking API like Sleep in modern "passive" applications. It will most likely cause waste of CPU resources. Some cycles would be "lost" due to "sleeping" overhead and some cycles would be wasted due to constantly "waking up" to check external state.  It's always better to use something like WaitForSingleObject or WaitForMultipleObjects inside thread. In this case there would be three methods to unblock thread: 1) You can unblock it by your own event - for example to terminate thread. 2) You can unblock it on timeout, if you need to do some tasks from time to time. 3) End you can unblock it by actual event, you need to wait for. This way thread will only consume CPU time, when it's really needed. And this way it could be woken at any moment without any overhead.

No, using threads only makes sense for long living processes. If you only need a timeout for a single operation (like the connect in my example), the creation of the thread structure in the operating system, the allocation of the stack memory and the management in the scheduler will add far more overhead to the system then having a polling mechanism.

Also, your code does polling like mine, it is just hidden. TThread.Queue just adds an object to a thread safe queue, which requires the main thread to poll in regular intervals. So you basically do the exact same thing as I do, just with much more overhead and 4 times the amount of code.

But let's stick to my example with the timeout on connect. Because this is a very simple example you might come accross quite easiely and it has no obvious solution.
I see exactly two possibilities, either do it like I did, or create a new watchdog thread, which will after the timout occured invalidate the socket which results in the abortion of the blocking connect call.
Adding a watchdog thread, introduces much more code, runtime overhead (unless you do thread caching when doing this multiple times, which in turn requires even more code) and is pretty hard to understand, not to mention that invalidating the socket to stop the blocking call is really ugly. I don't see any advantages with this
Title: Re: Synchronize not working
Post by: MarkMLl on November 27, 2021, 08:04:31 pm
No, using threads only makes sense for long living processes. If you only need a timeout for a single operation (like the connect in my example), the creation of the thread structure in the operating system, the allocation of the stack memory and the management in the scheduler will add far more overhead to the system then having a polling mechanism.

It does have to be said though that unix doctrine seems to fork processes with gay abandon... and that would be expected to be substantially more complex that thread creation since it involves creating a new addressspace and either copying the existing process or setting up enough MMU state that it can be COWed on demand.

Noting of course that the OS/2 and Win32 APIs were heavily oriented towards asynchronous syscalls with callbacks (hence, reputedly, Horace Cutler's rant about byte-by-byte I/O in UNIX), I suppose that one possibility would be to emulate that by having a single background thread to which every blocking activity could be enqueued. That would obviously need to be used carefully, and there would obviously still be a sleep() or equivalent call somewhere.

MarkMLl



Title: Re: Synchronize not working
Post by: Warfley on November 27, 2021, 08:21:29 pm
It does have to be said though that unix doctrine seems to fork processes with gay abandon... and that would be expected to be substantially more complex that thread creation since it involves creating a new addressspace and either copying the existing process or setting up enough MMU state that it can be COWed on demand.
Yes forking is really slow, around 1MS. Creating a fork is usually an order of magnitude faster at like 0.1 to 0.5 ms but this is still a massive overhead.
Also there are alternatives to forking by using clone, which can be configured to behave differently, e.g. work in the same address space or not copy the file descriptors and so on. But it is usally adviced not to use it (because it is very complex) but rather use functions like posix_spawnp which use clone internally. https://www.man7.org/linux/man-pages/man2/clone.2.html
On Linux a thread is btw nothing more then a clone call with a certain configuration.

Especially fork gets slower the more often you call it from the same process. I once ran into the problem that a single fork took more than 1 minute. Solved it by using vfork instead.

Noting of course that the OS/2 and Win32 APIs were heavily oriented towards asynchronous syscalls with callbacks (hence, reputedly, Horace Cutler's rant about byte-by-byte I/O in UNIX), I suppose that one possibility would be to emulate that by having a single background thread to which every blocking activity could be enqueued. That would obviously need to be used carefully, and there would obviously still be a sleep() or equivalent call somewhere.
Yes the WinAPI also provides an asynchronous connect for sockets, but if you want to be cross compatible and stick to the Berkley Sockets API you won't get around either a watchdog thread or a polling sleep loop.

When handling large parallel systems managing millions of connections it is even advised to not use threading and events managed by the system (and scheduled by the scheduler), because it is to slow. There it is best to have a few threads, each managing a few thousand connections, polling them at a high frequency and when having nothing to do do busy waiting. But then you also don't use sleep
Title: Re: Synchronize not working
Post by: MarkMLl on November 27, 2021, 11:11:51 pm
On Linux a thread is btw nothing more then a clone call with a certain configuration.

I always find it useful to consider things like this, in particular what (things that look like) syscalls actually transfer to system mode or have the overhead of switching from the application thread to the scheduler. But I've a microkernel to my name :-)

Quote
Especially fork gets slower the more often you call it from the same process. I once ran into the problem that a single fork took more than 1 minute. Solved it by using vfork instead.

Ouch :-(

Quote
When handling large parallel systems managing millions of connections it is even advised to not use threading and events managed by the system (and scheduled by the scheduler), because it is to slow. There it is best to have a few threads, each managing a few thousand connections, polling them at a high frequency and when having nothing to do do busy waiting. But then you also don't use sleep

The ISP we've used for 20 years designs and sells their own routers. Even their frontline techies are good for a technology discussion :-) https://hackaday.com/2017/12/14/adsl-robustness-verified-by-running-over-wet-string/ https://www.firebrick.co.uk/

MarkMLl
Title: Re: Synchronize not working
Post by: Mr.Madguy on November 28, 2021, 06:06:46 am
Yeah, both solutions have their pros and cons. Blocking main thread is also bad idea and can cause many problems. Especially, when application has GUI. Solutions without threads is possible. Using timer events for example. They're async, i.e. don't block main thread and they allow us to implement something like "do something after x milliseconds".
Title: Re: Synchronize not working
Post by: Warfley on November 28, 2021, 03:52:11 pm
Yeah I think what cannot be stressed enough is, don't use sleep in the main thread of a GUI application. GUI applications (or to be more general interactive applications) are event based. It is generally a sign of bad design if one tries to perform serialzed operations in an event based system.
Title: Re: Synchronize not working
Post by: MarkMLl on November 28, 2021, 05:09:12 pm
Yeah I think what cannot be stressed enough is, don't use sleep in the main thread of a GUI application. GUI applications (or to be more general interactive applications) are event based. It is generally a sign of bad design if one tries to perform serialzed operations in an event based system.

One observation on that. In Delphi, certainly the older versions I used before switching to Lazarus, a procedure called by e.g. OnActivate could loop reading the keyboard provided that it did an APM. In Lazarus I've never been able to get anything comparable working, which has complicated software into which one can type commands enormously.

Please don't ask for a detailed example etc., it would take me days of work to work out the exact conditions.

MarkMLl
Title: Re: Synchronize not working
Post by: Warfley on November 28, 2021, 07:43:38 pm
I encountered the exact same problem when I tried to get my STAX working on forms, I was searching for the best event to start the executor. OnActivate has a pretty weird behavior, it looks to me as if you loop in it, focusing doesn't work anymore
Found the solution to be is to start a timer with a small interval in whoms tick event to do the serialized work.
Title: Re: Synchronize not working
Post by: MarkMLl on November 28, 2021, 08:18:14 pm
Glad it's not just me :-) I wasn't quite using OnActivate... in the Delphi days I used form creation to put a custom message on the queue which caused a procedure to be called (and in this case loop with an APM in it) once the message loop was running. These days I use a QueueAsyncCall() to do approximately the same thing.

It /almost/ worked, except- as in your case- when e.g. a dialog(ue) had to be handled. I ended up with a thoroughly convoluted alternative where the keyboard reader and a great deal of the program itself runs in background threads... it works but I'm not exactly happy with it particularly considering the amount of work it took.

I can't remember the exact details by now, but I quite simply couldn't find a standard component which handled a typed-in command from the user sensibly.

MarkMLl
Title: Re: Synchronize not working
Post by: Mr.Madguy on November 29, 2021, 09:43:31 am
It's always good thing to understand, that VCL - is just OOP wrapper around windows User32 library. So OnActivate - is wrapper around WM_ACTIVATE. In most cases it's important to know, whether message is sync or async, i.e. if it's sent or posted. If sender needs message result, then message should be sent. And in this case blocking it's handler means blocking sender execution. Overall there are 3 types of messages: 1) Posted, that is async and therefore sent via message queue 2) Sent, that is sync (i.e. window proc is called directly) for the same thread, but can be async for other thread 3) Message, that is always sync, because it's sent via calling window proc directly. It's true for something like WM_CREATE. At least in older Windows versions.
Title: Re: Synchronize not working
Post by: MarkMLl on November 29, 2021, 09:59:12 am
It's always good thing to understand, that VCL - is just OOP wrapper around windows User32 library. So OnActivate - is wrapper around WM_ACTIVATE. In most cases it's important to know, whether message is sync or async, i.e. if it's sent or posted. If sender needs message result, then message should be sent. And in this case blocking it's handler means blocking sender execution. Overall there are 3 types of messages: 1) Posted, that is async and therefore sent via message queue 2) Sent, that is sync (i.e. window proc is called directly) for the same thread, but can be async for other thread 3) Message, that is always sync, because it's sent via calling window proc directly. It's true for something like WM_CREATE. At least in older Windows versions.

Yes, and that also applies to the one-shot message that I was using to kick stuff off when I was using Delphi.

I'd be surprised if anybody thought this merited serious investigation, and am not prepared to do it myself only to be told that the LCL was already working properly. But I suppose that the interesting thing is the way that the LCL (on Linux) /almost/ works in the way that Warfley and I hoped, with the exception of the focus issue.

MarkMLl

Title: Re: Synchronize not working
Post by: PascalDragon on November 29, 2021, 02:01:11 pm
Yes sleep(0) will not reduce the CPU load, thats not the goal, it just yields to other processes so you don't starve them. If you want to actually reduce CPU usage you need to actually slow down your program (e.g. with sleep > 0)
Why use sleep when you can suspend and use resume.
They have no load on the processor.

Suspend and Resume should not be used: you might suspend the thread while inside a critical section that is then never released until the thread is resumed and if the thread that controls the suspension and resuming then uses this critical section as well you'll have a deadlock. If you want to pause a thread until something from outside happens then wait on an event (which won't result in CPU load as well).

I thought the use of sleep or application.processmessage was considered negative from a programming perspective.
Is sleep used in professional programs?
Application.ProcessMessages is usually negative now because today's OSs support multi-threading and implement preemptive multitasking.  Application.ProcessMessages is something that was often necessary in the versions of Windows that required cooperative multitasking, their scheduler depended on the program voluntarily relinquishing the CPU to the scheduler and processing messages which was the gateway to those O/Ss schedulers regaining control.

Application.ProcessMessages still has its place if you're doing some long running operation without using threads. E.g. updating a progress bar and listening for the click of a cancel button more often than not can be done without a thread (especially if the remainder of the UI is disabled).
Title: Re: Synchronize not working
Post by: Mr.Madguy on November 29, 2021, 02:08:04 pm
Yes, and that also applies to the one-shot message that I was using to kick stuff off when I was using Delphi.

I'd be surprised if anybody thought this merited serious investigation, and am not prepared to do it myself only to be told that the LCL was already working properly. But I suppose that the interesting thing is the way that the LCL (on Linux) /almost/ works in the way that Warfley and I hoped, with the exception of the focus issue.

MarkMLl
Back in old times I had very good Visual Basic book, that wasn't about Visual Basic only - it was also about many Win32 tips and tricks.
Title: Re: Synchronize not working
Post by: MarkMLl on November 29, 2021, 02:42:15 pm
Suspend and Resume should not be used: you might suspend the thread while inside a critical section that is then never released until the thread is resumed and if the thread that controls the suspension and resuming then uses this critical section as well you'll have a deadlock. If you want to pause a thread until something from outside happens then wait on an event (which won't result in CPU load as well).

Thanks for the reminder. I still use them for one very specific purpose- temporarily stopping a thread's execution loop until work is available- but I think that could be changed to use an event without hassle.

Quote
Application.ProcessMessages still has its place if you're doing some long running operation without using threads. E.g. updating a progress bar and listening for the click of a cancel button more often than not can be done without a thread (especially if the remainder of the UI is disabled).

Pretty much indispensable TBH, since it's unlikely that the GUI execution model will be changed at this late stage. But one does have to be careful about re-entry: e.g. where something being invoked by a thread using Application.QueueAsyncCall() incautiously does an APM to make sure that debug output shows up promptly.

MarkMLl
Title: Re: Synchronize not working
Post by: SymbolicFrank on November 29, 2021, 06:59:54 pm
Application.ProcessMessages is good for just one thing: keeping the GUI alive when doing lots of processing in the main thread. My initial reaction was about, that it doesn't fix your application freezing.

The main issue is resources. You can try to maximize performance by allocating everything you're going to need at startup, or you can allocate and free things on demand. If it is just small pieces of memory, that's easy and fast. But quite a lot of things have to ask for access, from the OS, a library or background process. The obvious ones are files and sockets. Like, the connection to a database.

So, if you're not running just a single loop with pre-allocated objects, but do a lot of different tasks, that require a lot of Create and Free, it is very likely that your application starts freezing randomly. Application.ProcessMessages won't help here. Some well-placed Sleep(0) calls probably will. And yes, you should probably split them up in different processes and/or threads.

That's why you make a single connection to a database and reuse that. And use transactions.

The MS Thread architecture is basically flawed in that it relies on shared memory and that named pipes or other easy inter-process communication methods are barely supported. It's fine to use sockets for inter-process communication, but not between threads, as you need another thread to handle the socket.

It seems like a good thing to put critical sections around every dubious block of code, use lock .. unlock wherever it looks useful and use synchronize to update that single copy of the variable. Which essentially serializes your application and creates a lot of potential race conditions.

Blocking threads are another matter. If I look in Task Manager, I see that there are 217 processes and 3589 treads running, and 116541 handles in use. Most of those threads are blocked and waiting for input. That's totally fine. And most of those handles are locks on dynamically allocated resources. So, there are a lot of resources allocated (like those files and sockets) through processes that cannot immediately respond to changes. They poll them in a loop, or only use them synchronized (wait for completion). Those need enough CPU time to complete the request and/or run through a loop polling them.

So, it doesn't matter if you serialize and synchronize everything, but then threads make no sense. In all other cases, asynchronous requests make a lot of sense, but you would want a tread waiting for completion for all requests. On Linux this is a totally different matter.
Title: Re: Synchronize not working
Post by: MarkMLl on November 29, 2021, 07:20:32 pm
Or put another way, "It's complicated" :-)

On Linux you can, of course, use unix domain sockets with the standard API. But I'm dismayed by the number of people who quite simply grab a multilayer protocol on top of an inet domain socket and start moving enormous blocks of XML or JSON around.

MarkMLl
Title: Re: Synchronize not working
Post by: 440bx on November 29, 2021, 07:42:00 pm
Application.ProcessMessages still has its place if you're doing some long running operation without using threads. E.g. updating a progress bar and listening for the click of a cancel button more often than not can be done without a thread (especially if the remainder of the UI is disabled).
IMO, if some activity takes long enough that it requires a progress bar then, that activity should be done by a thread other than the GUI thread and, that other thread can easily post a message to the GUI thread informing it of the need to update the progress bar and what its new value should be.

IOW, IMO, a long running operation should be handed to a thread that is not the GUI thread.  The GUI thread should not be "indulging" in those things (except for proof-of-concept and throw away programs - and even then.)  Allowing a GUI thread to perform time consuming operations is just asking for trouble and, Application.Process messages is just a narcotic to alleviate pain instead of a cure.
Title: Re: Synchronize not working
Post by: Warfley on November 30, 2021, 10:24:21 am
There are some reasons to avoid Threading, one being that threads can make the code much more complex (especially if you have to start implementing thread safe datastructrues and/or events).
That said, Application.ProcessMessages is more a hotfix rather than a real solution and has many problems. E.g. A short running function calls Application.ProcessMessages, an event causes a long running function to run. Even if that long runing function calls Application.ProcessMessages, the short running function will not terminate until the long running function terminates, resulting in a massive waste of time.

This is exactly why I build STAX (https://forum.lazarus.freepascal.org/index.php/topic,56072.0.html) earlier this year. But this would require mapping the APIs to STAX (which could internally be done using threads) but this of course having to implement those yourself defeats the whole "single threaded is simpler" argument.
But when implemented thourughly (like in other languages s.a. Python, C#, JS, C++) with lots of API support, co-routines can make life much easier and there is a reason they are so popular in those languages.
TinyPortal © 2005-2018