Recent

Author Topic: Threadsafe Singleton  (Read 1425 times)

Trenatos

  • Hero Member
  • *****
  • Posts: 519
    • MarcusFernstrom.com
Threadsafe Singleton
« on: January 06, 2020, 05:31:04 pm »
I initially looked into semaphores but they're deprecated.

Looking at https://wiki.freepascal.org/Singleton_Pattern there's a line towards the end that says "The implementations have a problem with multi-threading and synchronization"

So on that note, can someone point me to an example of a threadsafe singleton?

Ultimately my goal is a threadsafe database connection pool.

--

I did find http://www.nickhodges.com/MultiThreadingInDelphi/ToC.html, recommended by Thaddy in a different thread, checking that out.

And I also found this thread talking about syncobjs https://forum.lazarus.freepascal.org/index.php?topic=36540.0
« Last Edit: January 06, 2020, 05:51:52 pm by Trenatos »

Thaddy

  • Hero Member
  • *****
  • Posts: 9634
Re: Threadsafe Singleton
« Reply #1 on: January 06, 2020, 06:04:38 pm »
I would advise against simple singletons in database applications, but under windows it is quite easy using a named event.
Otherwise, write a small Daemon/Service and poll that.

*****
Warning: the current example on wikipedia is incorrect: https://en.wikipedia.org/wiki/Singleton_pattern#Delphi_and_Free_Pascal_implementation so do not use that.
The case is the initialization and finalization handling: that should not be there. I will put it on my todo list to write a better/working/real one.
*****
« Last Edit: January 06, 2020, 06:10:47 pm by Thaddy »
I am more like donkey than shrek

Trenatos

  • Hero Member
  • *****
  • Posts: 519
    • MarcusFernstrom.com
Re: Threadsafe Singleton
« Reply #2 on: January 06, 2020, 06:11:23 pm »
This is for a webserver running under Linux with threaded fphttpserver.

I need a way to set up and manage a pool of database connections. A threadsafe singleton seems like the way to go, what would you suggest instead?

Thaddy

  • Hero Member
  • *****
  • Posts: 9634
Re: Threadsafe Singleton
« Reply #3 on: January 06, 2020, 06:21:41 pm »
Usually the webserver itself can handle such issues quite well based on the internal workings of the sockets.  And most databases can handle this too, just not lightweight embedded ones.
« Last Edit: January 06, 2020, 06:25:22 pm by Thaddy »
I am more like donkey than shrek

BeniBela

  • Hero Member
  • *****
  • Posts: 708
    • homepage
Re: Threadsafe Singleton
« Reply #4 on: January 06, 2020, 07:07:26 pm »
For multi-threading you need some synchronization.

Take the last example

Code: Pascal  [Select]
  1.  class function TSingleton.Create: TSingleton;
  2.  begin
  3.    if Singleton = nil then
  4.      Singleton := TSingleton.Init;
  5.    Result := Singleton;
  6.  end;

and change it to:

Code: Pascal  [Select]
  1.  
  2. class function TSingleton.Create: TSingleton;
  3. var temp: TSingleton;
  4. begin
  5.   if Singleton = nil then begin
  6.    temp := TSingleton.Init;  
  7.    WriteBarrier;
  8.    if InterlockedCompareExchange(pointer(Singleton), pointer(temp), nil) <> nil then
  9.      temp.free;
  10.   end;
  11.   Result := Singleton;
  12. end;
  13.  

Now I am not sure about the barriers. InterlockedCompareExchange might already add a write barrier, and you probably do not need the write barrier anyways on x86. Perhaps you also need a read barrier for arm or alpha in case Singleton is not nil

Trenatos

  • Hero Member
  • *****
  • Posts: 519
    • MarcusFernstrom.com
Re: Threadsafe Singleton
« Reply #5 on: January 06, 2020, 07:20:33 pm »
Thaddy, I could have each webserver client connection get its own database connection, but that's a pretty heavy process so I'd like to offload it to a pool manager to lower the overall response time per web-request.

Thanks BeniBela, I'll take a look at that as soon as I'm off work.
Generally this will run on x64 Linux, but the more systems supported the better.
« Last Edit: January 06, 2020, 08:34:16 pm by Trenatos »

avk

  • Full Member
  • ***
  • Posts: 203
    • my self-education project
Re: Threadsafe Singleton
« Reply #6 on: January 17, 2020, 11:20:34 am »
Just curious, is this the correct implementation of Singleton?
Code: Pascal  [Select]
  1. type
  2.  
  3.   TSingleton = class
  4.   strict private
  5.   class var
  6.     FInstance: TSingleton;
  7.     class constructor Init;
  8.     class destructor Done;
  9.   var
  10.     FExistFlag: LongInt;
  11.   protected
  12.     function AlreadyExists: Boolean;
  13.   public
  14.     class function NewInstance: TObject; override;
  15.     constructor Create; virtual;
  16.     procedure FreeInstance; override;
  17.   end;
  18.  
  19. ////
  20.  
  21. class constructor TSingleton.Init;
  22. begin
  23.   FInstance := nil;
  24. end;
  25.  
  26. class destructor TSingleton.Done;
  27. begin
  28.   if FInstance <> nil then
  29.     begin
  30.       FInstance.FExistFlag := 0;
  31.       FreeAndNil(FInstance);
  32.     end;
  33. end;
  34.  
  35. function TSingleton.AlreadyExists: Boolean;
  36. begin
  37.   Result := InterlockedCompareExchange(FExistFlag, 1, 0) <> 0;
  38. end;
  39.  
  40. class function TSingleton.NewInstance: TObject;
  41. var
  42.   Inst: TObject;
  43. begin
  44.   if FInstance = nil then
  45.     begin
  46.       Inst := inherited NewInstance;
  47.       WriteBarrier;
  48.       if InterlockedCompareExchange(Pointer(FInstance), Pointer(Inst), nil) <> nil then
  49.         Inst.Free;
  50.     end;
  51.   Result := FInstance;
  52. end;
  53.  
  54. constructor TSingleton.Create;
  55. begin
  56.   if AlreadyExists then
  57.     exit;
  58.   inherited;
  59. end;
  60.  
  61. procedure TSingleton.FreeInstance;
  62. begin
  63.   if FExistFlag = 0 then
  64.     inherited;
  65. end;
  66.  

Or maybe this one will be better?
Code: Pascal  [Select]
  1. type
  2.   TSingleton = class abstract
  3.   strict private
  4.   class var
  5.     FInstance: TSingleton;
  6.     class constructor Init;
  7.     class destructor Done;
  8.   var
  9.     FLock: LongInt;
  10.     FExists: Boolean;
  11.   protected
  12.     procedure Lock; inline;
  13.     procedure Unlock; inline;
  14.     procedure InternalCreate; virtual; abstract;
  15.     property  AlreadyExists: Boolean read FExists;
  16.   public
  17.     class function NewInstance: TObject; override;
  18.     constructor Create;
  19.     procedure FreeInstance; override;
  20.   end;
  21.  
  22. ////
  23.  
  24. class constructor TSingleton.Init;
  25. begin
  26.   FInstance := nil;
  27. end;
  28.  
  29. class destructor TSingleton.Done;
  30. begin
  31.   if FInstance <> nil then
  32.     begin
  33.       FInstance.FExists := False;
  34.       FreeAndNil(FInstance);
  35.     end;
  36. end;
  37.  
  38. procedure TSingleton.Lock;
  39. begin
  40.   while Boolean(InterlockedExchange(FLock, 1)) do
  41.     ThreadSwitch;
  42. end;
  43.  
  44. procedure TSingleton.Unlock;
  45. begin
  46.   InterlockedExchange(FLock, 0);
  47. end;
  48.  
  49. class function TSingleton.NewInstance: TObject;
  50. var
  51.   Inst: TObject;
  52. begin
  53.   if FInstance = nil then
  54.     begin
  55.       Inst := inherited NewInstance;
  56.       WriteBarrier;
  57.       if InterlockedCompareExchange(Pointer(FInstance), Pointer(Inst), nil) <> nil then
  58.         Inst.Free;
  59.     end;
  60.   Result := FInstance;
  61. end;
  62.  
  63. constructor TSingleton.Create;
  64. begin
  65.   Lock;
  66.   try
  67.     if AlreadyExists then
  68.       exit;
  69.     FExists := True;
  70.     InternalCreate;
  71.   finally
  72.     Unlock;
  73.   end;
  74. end;
  75.  
  76. procedure TSingleton.FreeInstance;
  77. begin
  78.   if AlreadyExists then
  79.     exit;
  80.   inherited;
  81. end;
  82.  
« Last Edit: January 17, 2020, 01:25:19 pm by avk »

BeniBela

  • Hero Member
  • *****
  • Posts: 708
    • homepage
Re: Threadsafe Singleton
« Reply #7 on: January 17, 2020, 11:34:04 pm »
The second is better. I doubt the first one will work.

However, I was trying  to avoid the lock, hence the use of Interlocked....  if you use a lock, you should use a critical section, and forget about Interlocked...

A lock/critical section is the most reliable way to implement a threadsafe singleton. But it is also the slowest. It can be made faster with double-checked locking, which only uses the lock, if the singleton has not been created.

But i want to do the entire initialization with only one global variable, FInstance. (then the code can also be used for other objects than singletons. One lock for one singleton is not a big issue, but imagine you want to lazily initialize thousands of objects, then you might need to have thousands of locks)

Hence, InterlockedCompareExchange. It only has the disadvantage that multiple threads might create a TSingleton instance, and then all threads except one thread free their own instance again


argb32

  • Jr. Member
  • **
  • Posts: 82
    • Pascal IDE based on IntelliJ platform
Re: Threadsafe Singleton
« Reply #8 on: January 17, 2020, 11:53:22 pm »
Every unit is a ready to use singleton.
Other singletons are antipattern.

avk

  • Full Member
  • ***
  • Posts: 203
    • my self-education project
Re: Threadsafe Singleton
« Reply #9 on: January 18, 2020, 06:28:49 am »
@BeniBela, thank you.
@argb32, could you please give a little more detail?

Thaddy

  • Hero Member
  • *****
  • Posts: 9634
Re: Threadsafe Singleton
« Reply #10 on: January 18, 2020, 10:12:34 am »
@BeniBela
Maybe drop the locking and use a TEventObject instead?
I am more like donkey than shrek

argb32

  • Jr. Member
  • **
  • Posts: 82
    • Pascal IDE based on IntelliJ platform
Re: Threadsafe Singleton
« Reply #11 on: January 18, 2020, 01:47:06 pm »
@argb32, could you please give a little more detail?

A Pascal unit can contain stuff, does initialize once and is accessible anywhere. Just as singleton. And that all is threadsafe.
Why need to reimplement all these properties with classes?

avk

  • Full Member
  • ***
  • Posts: 203
    • my self-education project
Re: Threadsafe Singleton
« Reply #12 on: January 18, 2020, 02:57:54 pm »
Just out of curiosity. I've never seen any good Singleton implementation in Pascal.

BeniBela

  • Hero Member
  • *****
  • Posts: 708
    • homepage
Re: Threadsafe Singleton
« Reply #13 on: January 19, 2020, 12:19:08 am »
The unit always would create the singleton. The classes could only create the singleton, when it is actually used, so the program starts faster.

And I plan to use this function for non-singletons.

jamie

  • Hero Member
  • *****
  • Posts: 2312
Re: Threadsafe Singleton
« Reply #14 on: January 19, 2020, 03:04:38 am »
Nice fancy new age terminology , "Singleton" , Is that anything related to a Simpleton ? 8-)
Number 1 at blue screen app creations!