Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2015-07

Counting semaphore for free pascal

17 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

kapibara

Hi there,

A counting, or general, semaphore limits simultaneous access to a resource according to the number of permits you specify. On the other hand, a binary semaphore like a critical section limits the access to "one at a time".

FPC doesn't seem to have a general semaphore? I recently attempted to implement one, here is the code, plus a working Lazarus demo attached. It blocks on an event instead of a critical section. However, I didnt manage to have it block by just calling Wait, because then the code blocked without leaving the PermitLock and that meant deadlock for all threads. So thats why you see the "if Semaphore.Wait(aEvent) then RTLEventWaitFor(aEvent)" instead of just Semaphore.Wait(aEvent). It works just fine, but if anyone knows how to implement this by just calling Wait(aEvent), please tell. If you want to block on a critical section instead of an event, do that. Perhaps the result could be TFPSemaphore and go with FPC.

unit kbsemaphore;
{$mode objfpc}{$H+}
interface
uses
   Classes, SysUtils, syncobjs, contnrs;
type
   { TKBSemaphore }
   TKBSemaphore = class
   private
     FPermits: Cardinal;
     FPermitLock: TCriticalSection;
     FBlockQueue: TQueue;
     function GetWaitCount: Cardinal;
   public
     function Wait(var AEvent: PRTLEvent): Boolean;
     procedure Post;
     constructor Create(MaxPermits: Cardinal);
     destructor Destroy; override;
     property WaitCount: Cardinal read GetWaitCount;  //Approximate
precision!
     property Permits: Cardinal read FPermits;
   end;
implementation

{ TKBSemaphore }

function TKBSemaphore.GetWaitCount: Cardinal;
begin
   FPermitLock.Enter;
   Result:= FBlockQueue.Count;
   FPermitLock.Leave;
end;
function TKBSemaphore.Wait(var AEvent: PRTLEvent): Boolean;
begin
   FPermitLock.Enter;
   if (FPermits > 0) then
   begin
     Dec(FPermits);
     Result:= False;
   end
   else
   begin
     AEvent:= RTLEventCreate;
     FBlockQueue.Push(AEvent);
     Result:= True;
   end;
   FPermitLock.Leave;
end;
procedure TKBSemaphore.Post;
begin
   FPermitLock.Enter;
   if FBlockQueue.Count > 0 then
   begin
     RTLeventSetEvent(PRTLEvent(FBlockQueue.Peek));
     RTLeventdestroy(PRTLEvent(FBlockQueue.Pop));
   end
   else
     Inc(FPermits);
   FPermitLock.Leave;
end;
constructor TKBSemaphore.Create(MaxPermits: Cardinal);
begin
   FPermits:= MaxPermits;
   FPermitLock:= TCriticalSection.Create;
   FBlockQueue:= TQueue.Create;
end;
destructor TKBSemaphore.Destroy;
begin
   FPermitLock.Free;
   FBlockQueue.Free;
   inherited Destroy;
end;
end.
Serguei TARASSOV

Hello,

A counting, or general, semaphore limits simultaneous access to a resource according to the number of permits you specify. On the other hand, a binary semaphore like a critical section limits the access to "one at a time".

Seems, you confuse a little semaphores and critical sections. In general, semaphores are inter-process but critical sections are not. I mean that semaphores allow synchronize processes whereas critical sections are designed to synchronize the threads within the same process. Some frameworks (i.e. .NET) provide classes like "threading semaphore" but it is very particular case.
It is possible to use real semaphores to synchronize the threads too (with some overhead).

Regards,
Serguei

Michael Schnell
to Serguei TARASSOV
Serguei TARASSOV wrote
In general, semaphores are inter-process but critical sections are not.

Hmmm I suppose this is "fpc speak".

The "really general" term "semaphore" denotes a kind of for synchronizing means for multiple execution streams, be that Threads or tasks (or multiple processors, ....)

-Michael

kapibara
to Serguei TARASSOV: Seems, you confuse a little semaphores and critical sections. In general, semaphores are…

Hi all,

@Serguei Thanks for your reply. What you write is probably right, but let me explain the problem.

In an application you have N number of threads and they all want to access a service. Now, for example, only five threads are allowed to have access at the same time. I believe this is a job for a counting semaphore. Delphi also has a semaphore. The code I wrote, as basic as it is, limits the simultaneous thread accesses to the service. So the semaphore is used here for synchronization of threads within the same application, and that is one of the intended uses of semaphores. A critical section blocks all threads except one, so I'm under the impression that it can't be used for this task.

Reason I wrote is because I have a feeling the code I wrote could be improved, or implemented differently altogether. A few weeks ago I never even heard about "counting semaphores". There seem to be not so much time spent on this subject in education, and many different opinions about what semaphores are and what they are used for can be found.

Serguei TARASSOV wrote 15 lines
Hello, On 29/07/2015 12:00, wrote: > A counting, or general, semaphore limits simultaneous access to a > resource according to the number of permits you specify. On the other > hand, a binary semaphore like a critical section limits the access to > "one at a time". Seems, you confuse a little semaphores and critical sections. In general, semaphores are inter-process but critical sections are not. I mean that semaphores allow synchronize processes whereas critical sections are designed to synchronize the threads within the same process. Some frameworks (i.e. .NET) provide classes like "threading semaphore" but it is very particular case. It is possible to use real semaphores to synchronize the threads too (with some overhead).
Michael Schnell
to kapibara
kapibara wrote
In an application you have N number of threads and they all want to access a service. Now, for example, only five threads are allowed to have access at the same time. ..

I suppose that easily can be made by two critical sections.

One is used to protect the mutual access to the counter variable (here you could use an atomic operation - provided in the FPC library - as well, this might provide better performance, but does nit seem necessary for that purpose here.

The second is used to provide the actual waiting and is only entered if the count is over the top.

-Michael

Martin Schreiber
to kapibara: A counting, or general, semaphore limits simultaneous access to a resource according to…
kapibara wrote
FPC doesn't seem to have a general semaphore? I recently attempted to implement one, here is the code, plus a working Lazarus demo attached.

MSEgui also has a semaphore implementation, please see sys_sem*() functions in lib/kernel/windows/msesysintf1.pas and lib/kernel/linux/msesysintf1.pas. https://gitlab.com/mseide-msegui/mseide-msegui

Martin

Sven Barth
to Serguei TARASSOV: Seems, you confuse a little semaphores and critical sections. In general, semaphores are…
"Serguei TARASSOV" wrote 13 lines
Hello, On 29/07/2015 12:00, wrote: > > A counting, or general, semaphore limits simultaneous access to a > resource according to the number of permits you specify. On the other > hand, a binary semaphore like a critical section limits the access to > "one at a time". Seems, you confuse a little semaphores and critical sections. In general, semaphores are inter-process but critical sections are not. I mean that semaphores allow synchronize processes whereas critical

sections are designed to synchronize the threads within the same process.

Some frameworks (i.e. .NET) provide classes like "threading semaphore"

but it is very particular case.

It is possible to use real semaphores to synchronize the threads too

(with some overhead).

Semaphores don't necessarily need to be cross process.

Regards,
Sven

Sven Barth
to kapibara: A counting, or general, semaphore limits simultaneous access to a resource according to…
"kapibara" wrote
Hi there, A counting, or general, semaphore limits simultaneous access to a

resource according to the number of permits you specify. On the other hand, a binary semaphore like a critical section limits the access to "one at a time".

FPC doesn't seem to have a general semaphore? I recently attempted to

implement one, here is the code, plus a working Lazarus demo attached. It blocks on an event instead of a critical section. However, I didnt manage to have it block by just calling Wait, because then the code blocked without leaving the PermitLock and that meant deadlock for all threads. So thats why you see the "if Semaphore.Wait(aEvent) then RTLEventWaitFor(aEvent)" instead of just Semaphore.Wait(aEvent). It works just fine, but if anyone knows how to implement this by just calling Wait(aEvent), please tell. If you want to block on a critical section instead of an event, do that. Perhaps the result could be TFPSemaphore and go with FPC.

Any TSemaphore implementation should provide the same methods that Delphi's TSemaphore provides:
http://docwiki.embarcadero.com/Libraries/XE6/en/System.SyncObjs.TSemaphore

Regards,
Sven

Serguei TARASSOV
Show quoted text 10 lines
Date: Wed, 29 Jul 2015 15:16:13 +0200 From: Sven Barth To: FPC-Pascal users discussions Subject: Re: [fpc-pascal] Counting semaphore for free pascal Message-ID: Content-Type: text/plain; charset="utf-8" Semaphores don't necessarily need to be cross process.

Yes, it is not necessary.
As I wrote, some frameworks (i.e. .NET) provide classes like "threading semaphore".
"By default" since 1960s semaphores are designed and used to synchronize the processes.
When the semaphore's scope is limited by threads it is documented as "threading semaphore" in most cases.

Regards,
Serguei

Serguei TARASSOV
Show quoted text 11 lines
Date: Wed, 29 Jul 2015 13:10:23 +0200 From: kapibara Subject: Re: [fpc-pascal] Counting semaphore for free pascal Message-ID: Content-Type: text/plain; charset=windows-1252; format=flowed Hi all, @Serguei Thanks for your reply. What you write is probably right, but let me explain the problem.

Well, the terminology doesn't matter, I'm afraid to start some flood, so forget it.

Reason I wrote is because I have a feeling the code I wrote could be improved, or implemented differently altogether.

I see you're using two concepts of synchronization (semaphore and events) at the same time.
Maybe better to separate them?
It is not clear why use events to limit thread access to service.

Regards,
Serguei

Michael Schnell
to Serguei TARASSOV
Serguei TARASSOV wrote 5 lines
I see you're using two concepts of synchronization (semaphore and events) at the same time. Maybe better to separate them? It is not clear why use events to limit thread access to service.

There are several implementation details a semaphore can have
- counting / not counting
- if taken by a precess/thread will the same process/thread block/count when it tries a second take ?
- if taken by a precess/thread is a different process/thread allowed to free it ?
- ...

-Michael

Michael Schnell
to Michael Schnell: Hmmm I suppose this is "fpc speak". The "really general" term "semaphore" denotes a kind…
Michael Schnell wrote
On 07/29/2015 12:35 PM, Serguei TARASSOV wrote: > > critical sections are not. Hmmm I suppose this is "fpc speak".

Thinking about this I feel that "CriticalSection" (the official name of this functionality is Mutex) and "Event" is "fpc speak". "Event even has several different meanings in "fpc speak, e.g. TEvent (which is some kind of Thread-Semaphore ) and and "Event" function to be settable in a class (the general name of such construct is "Callback").

In the Linux OS/Libraries, you have Mutex in both variants. For (Posix) threads in pthreadlib and for processes as a system call.

When used with threads, a Mutex in pthreadlib for supported archs automatically is done as a FUTEX that is much faster as it does not do a system call when the Mutex is not already taken by another thread.

-Michael

Michael Van Canneyt
to Michael Schnell
Michael Schnell wrote 10 lines
On 07/29/2015 01:06 PM, Michael Schnell wrote: > On 07/29/2015 12:35 PM, Serguei TARASSOV wrote: >> >> critical sections are not. > Hmmm I suppose this is "fpc speak". Thinking about this I feel that "CriticalSection" (the official name of this functionality is Mutex) and "Event" is "fpc speak". "Event even has several different meanings in "fpc speak, e.g. TEvent (which is some kind of Thread-Semaphore ) and and "Event" function to be settable in a class (the general name of such construct is "Callback").

CriticalSection and TEvent derive their names from Windows OS calls, adopted by Delphi and hence by FPC. There is nothing FPC specific about it.

Michael.

Michael Schnell
to Michael Van Canneyt
Michael Van Canneyt wrote
CriticalSection and TEvent derive their names from Windows OS calls, adopted by Delphi and hence by FPC. There is nothing FPC specific about it.

OK. Thanks for the clarification.

I understand that the computer science names ate Mutex and Semaphore and both are adopted by UNIX / POSIX and then by Linux. No idea why the Windows makers chose to invent a new notation for that old stuff.

-Michael

Sven Barth
to Michael Schnell
"Michael Schnell" wrote
On 07/30/2015 10:50 AM, Michael Van Canneyt wrote: > > CriticalSection and TEvent derive their names from Windows OS calls,

adopted by Delphi and hence by FPC.

Show quoted text 5 lines
> There is nothing FPC specific about it. > OK. Thanks for the clarification. I understand that the computer science names ate Mutex and Semaphore and

both are adopted by UNIX / POSIX and then by Linux. No idea why the Windows makers chose to invent a new notation for that old stuff.

Because they need to differentiate more than those two. Windows knows both Semaphore und Mutex. In addition there are also Event (binary Semaphore) and CriticalSection (process local Mutex).

Regards,
Sven

Serguei TARASSOV
Show quoted text 15 lines
Date: Wed, 29 Jul 2015 17:05:03 +0200 From: Michael Schnell Subject: Re: [fpc-pascal] Counting semaphore for free pascal Message-ID: Content-Type: text/plain; charset=ISO-8859-1; format=flowed On 07/29/2015 04:47 PM, Serguei TARASSOV wrote: > > > >I see you're using two concepts of synchronization (semaphore and > >events) at the same time. > >Maybe better to separate them? > >It is not clear why use events to limit thread access to service. There are several implementation details a semaphore can have - counting / not counting

What is "not counting semaphore"? Even a binary semaphore counts up to 1.

- if taken by a precess/thread will the same process/thread block/count when it tries a second take ?

Yes, re-entrant / non re-entrant

- if taken by a precess/thread is a different process/thread allowed to free it ?

AFAIK, it is a mutex that have notion of ownership but a semaphore is not safe for such case.

- ...

Yes, there are multiple details depending on used scenarios that makes a generic implementation complex and hard to use.
So my question was to clarify the goals of the posted code. I.e. a sleeping lock semaphore is expensive and is not appropriate in case of many short requests that is typically for multithreading applications.
However, I could share some simple semaphore implementation for non-sleeping and non-reentrant scenario.

Regards,
Serguei