xsharp.eu • DBServer close in X#
Page 1 of 2

DBServer close in X#

Posted: Sat Oct 19, 2019 3:39 pm
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 92 times
Wolfgang
P.S. I would map the CollectForced() function to a call of System.GC.Collect().

DBServer close in X#

Posted: Sat Oct 19, 2019 4:56 pm
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

DBServer close in X#

Posted: Sat Oct 19, 2019 6:26 pm
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

DBServer close in X#

Posted: Sun Oct 20, 2019 5:23 am
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

DBServer close in X#

Posted: Sun Oct 20, 2019 8:47 am
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

DBServer close in X#

Posted: Sun Oct 20, 2019 11:21 am
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

DBServer close in X#

Posted: Sun Oct 20, 2019 12:20 pm
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

DBServer close in X#

Posted: Sun Oct 20, 2019 12:27 pm
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

DBServer close in X#

Posted: Mon Oct 21, 2019 12:32 am
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

DBServer close in X#

Posted: Mon Oct 21, 2019 5:30 am
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