DBServer close in X#

This forum is meant for questions and discussions about the X# language and tools
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

DBServer close in X#

Post by wriedmann »

Hello,
in my migrated VO applications I have noted that the DBServers are never closed, even when the using window is closed.
In VO, you can try this code:

Code: Select all

FUNCTION Start(p)
	LOCAL sCmdLine := Psz2String(_GetCmdLine()) AS STRING
	LOCAL oCon AS Console
	
	oCon := Console{}
	
	oCon:Clear()
	oCon:Title := "Visual Objects Console Application."
	oCon:WriteLine("CA-Visual Objects Basic Console Application.")
	oCon:TextAttribute := FOREGROUND_WHITE
	
	TestServer( oCon )
	OpenAreas( oCon )
	oCon:Write("Press Enter")
	oCon:Read()
	oCon:WriteLine( "execute CollectForced()" )

	CollectForced()
	OpenAreas( oCon )

	oCon:Write("Press Enter")
	oCon:Read()

	RETURN NIL
	
function TestServer( oCon as Console ) as void pascal
	local oDBServer		as DBServer
	
	oCon:WriteLine( "Open server...." )	
	oDBServer			:= DBServer{ "C:cavo28SamplesSsatutorcustomer.dbf", true, false, "DBFNTX" }
	
	return

function OpenAreas( oCon as Console ) as void
	local nCounter			as dword
	local cPath				as string

	for nCounter := 1023 downto 1
		if VODBAlias( nCounter ) != NULL_STRING
			cPath		:= AllTrim( ( nCounter )->( DBInfo( DBI_FULLPATH ) ) )
			oCon:WriteLine( "Open:" + cPath )
		endif
	next
	
	return
and you will see that after the CollectForced() call there is no open workarea.
The same code in X# leaves the DBServer open.
Please see the following code (and the changed behavior of the DbServerEx class that has a destructor method):

Code: Select all

function Start( ) as void

TestServer()
OpenAreas()
System.Console.Write("Press Enter")
System.Console.Read()
System.Console.WriteLine( "execute CollectForced()" )
System.GC.Collect()
OpenAreas()
System.Console.Write("Press Enter")
System.Console.Read()
System.GC.Collect()
OpenAreas()

return

function TestServer() as void pascal
local oDBServer		as DBServer
local oDBServerEx	as DBServerEx

System.Console.WriteLine( "Open server...." )
oDBServer := DBServer{ "C:cavo28SamplesSsatutorcustomer.dbf", true, false, "DBFNTX" }
oDBServerEx := DBServerEx{ "C:cavo28SamplesSsatutorcustomer.dbf", true, false, "DBFNTX" }

return

function OpenAreas() as void
local nCounter as dword
local cPath as string

for nCounter := XSharp.RDD.WorkAreas.MaxWorkAreas downto 1
  if VODBAlias( nCounter ) != NULL_STRING
    cPath := AllTrim( ( nCounter )->( DBInfo( DBI_FULLPATH ) ) )
    System.Console.WriteLine( "Open:" + cPath )
  endif
next

return

class DBServerEx inherit DBServer

constructor( oFile as usual, lShareMode as usual, lReadOnlyMode as usual, xDriver as usual )
super( oFile, lShareMode, lReadOnlyMode, xDriver )
return

destructor()
System.Console.WriteLine( "Destructor called on class DBServerEx" )
if self:Used
  self:Close()
endif
return
end class
Please find both the AEF and the XIDE export file attached to this message.
CloseServer.zip
(2.74 KiB) Downloaded 90 times
Wolfgang
P.S. I would map the CollectForced() function to a call of System.GC.Collect().
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
robert
Posts: 4520
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

DBServer close in X#

Post by robert »

Wolfgang,
This is strange. There is a Destructor on the DbServer class that closes the database:
https://github.com/X-Sharp/XSharpPublic ... 1.prg#L530

We'll have to see why this is not called.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
robert
Posts: 4520
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

DBServer close in X#

Post by robert »

Wolfgang,

I think I know what is going on.
In our implementation each thread has its own workareas.
The destructor is called in another thread than where the DbServer was created. As a result it tried to close the workarea in the GC thread, which is already closed, since no workareas were opened in the GC thread.
I'll have to find a way to remember in which thread the workarea was opened.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

DBServer close in X#

Post by wriedmann »

Hi Robert,
if I understand you correctly, X# is doing this on the workarea level.
In the meantime it would be a good idea to add this on the DBServer level as in my sample.
The non closing DBServer is a bigger problem when combined with the RDD problem we have found because it makes it occurs much more often (in fact, the non-closed DBServers made this problem visibile in our application).
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
robert
Posts: 4520
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

DBServer close in X#

Post by robert »

Wolfgang,
No you did not understand me completely.
There is a destructor on the DbServer class. It tries to close a certain workarea NUMBER:
SELF:wWorkArea)->(VODBCloseArea( ))

But the GC thread has no RDD open in that workarea number.

Your code will fail too by the way when it is called in the GC thread (where Destructors are normally running) because the close method also selects the workarea with VoDbSelect() and then calls VODBCloseArea( ) for that workarea.
This was never an issue in Vulcan since there was one set of workareas shared by all threads.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

DBServer close in X#

Post by wriedmann »

Hi Robert,
ok, thank you, got it.
Effectively I had added a destructor to my own server class and it has not solved anything....
I have a lot to learn when it comes to multithreading....
So the following code will not more show all open DBFs:

Code: Select all

for nCounter := XSharp.RDD.WorkAreas.MaxWorkAreas downto 1
  if VODBAlias( nCounter ) != NULL_STRING
    System.Console.WriteLine( "Open:" + AllTrim( ( nCounter )->( DBInfo( DBI_FULLPATH ) ) ) )
  endif
next
So maybe we also need some central instance where to see all open workareas, maybe combined with the thread info - and a workarea is only fully qualified with its number and a thread identification.

Or would it help to loop through all threads of the application and look for open workareas?

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
robert
Posts: 4520
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

DBServer close in X#

Post by robert »

Wolfgang,
That code will show the open DBFs in the current thread.
Please give me some time to think of a mechanism to:
- close the DBF properly from the destructor
- present you a list of all open DBFs.
I did add a method on the runtime state that should close all workareas for all threads

RuntimeState.CloseWorkareasForAllThreads()

And this is called at application exit.
Apparently this is not enough or this is called at the wrong moment.
I'll have to do some research.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

DBServer close in X#

Post by wriedmann »

Hi Robert,
thank you very much!
There is a big change: in VO and Vulcan every workarea was completely identified by its number. With X# this is not more true, as the same workarea number can exist in different threads, and therefore the thread Id has to be added to the work area identification.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
g.bunzel@domonet.de
Posts: 97
Joined: Tue Mar 01, 2016 11:50 am
Location: Germany

DBServer close in X#

Post by g.bunzel@domonet.de »

...just an idea from a X#-beginner...
Why not doing it as in Vulcan to have one set of workarea numbers shared by all threads?

- There would no need to change the VO-Code
- The same workarea number can't exist in different threads
- With a new Access 'ThreadId' for the Server class - stored when the Server is opening - every thread can close his own servers if the thread is ending
- With a maximum of MaxWorkAreas := 4096 - that should be enough for all working threads
- If the DbServer class is restoring the workarea after an operation (DbSetRestoreWorkarea(TRUE)) - with a unique workarea number that should also work
- The workarea must not be fully qualified with its number and a thread identification
Gerhard
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

DBServer close in X#

Post by wriedmann »

Hi Gerhard,
Vulcan (and VO...) had a big problem: the RDDs are not thread safe and can be used only in the main thread.
The X# RDD should make this better.
But Robert is the multithreading specialist, so I'm confident that he will come up with a solution.
But maybe you are right and there should be a global workarea list, spanning all threads.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Post Reply