Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Here's the declaration:

Code Block
 	include('DCL_IncludeInAllClassHeaderFiles.inc'),once
	!include('DCL_System_ErrorManager.inc'),once
	include('DCL_System_Threading_CriticalSection.inc'),once

DCL_System_PoolItemQueue                queue,type
ItemNumber                                  long
InUse                                       bool
										end

DCL_System_Pool                         Class,Type,Module('DCL_System_Pool.CLW'),Link('DCL_System_Pool.CLW',_DCL_Classes_LinkMode_),Dll(_DCL_Classes_DllMode_)
!Errors                                          &DCL_System_ErrorManager
AccessLock                                  &DCL_System_Threading_CriticalSection 
StopOnError                                 bool,protected
StopOnErrorMessage                          cstring(500)
ItemQ                                       &DCL_System_PoolItemQueue
Construct                                   Procedure()
Destruct                                    Procedure()
GetFreeItemCount                            procedure,long
GetItemNumber                               procedure,long
Init                                        procedure(long maxItems)
ReleaseItemNumber                       procedure(long itemNumber)
SetStopOnError                              procedure(bool StopOnError,<string errorMessage>)
										End

and here's the method code:

Code Block
 
                                            Member
                                            Map
                                            End

    Include('DCL_System_Pool.inc'),Once


DCL_System_Pool.Construct                     Procedure()
    code
	self.ItemQ &= new DCL_System_PoolItemQueue
	self.AccessLock &= new DCL_System_Threading_CriticalSection
	self.StopOnErrorMessage = 'All pool items are in use'

DCL_System_Pool.Destruct                      Procedure()
    code
	!dispose(self.Errors)
	free(self.ItemQ)
	dispose(self.ItemQ)
	dispose(self.AccessLock)

DCL_System_Pool.GetFreeItemCount        procedure!,long
FreeItemCount                               long
x                                           long
	code
	self.AccessLock.Wait()
	loop x = 1 to records(self.ItemQ)
		get(self.ItemQ,x)
		if not self.ItemQ.InUse then FreeItemCount += 1.
	end
	self.AccessLock.Release()
	return FreeItemCount
	
DCL_System_Pool.GetItemNumber           procedure!,long
x                                           long
retval                                      long
	code
	self.AccessLock.Wait()
	retval = -1
	loop x = 1 to records(self.ItemQ)
		get(self.ItemQ,x)
		if not self.itemq.InUse
			self.ItemQ.InUse = true
			put(self.ItemQ)
			retval = self.ItemQ.ItemNumber
			break
		end
	end
	self.AccessLock.Release()
	if retval = -1 and self.StopOnError then Stop(self.StopOnErrorMessage).
	return retval
	
DCL_System_Pool.Init                    procedure(long maxItems)
x                                           long
	code
	self.AccessLock.Wait()
	free(self.ItemQ)
	loop x = 1 to maxItems
		self.ItemQ.ItemNumber = x
		self.ItemQ.InUse = false
		add(self.ItemQ)
	end
	self.AccessLock.Release()
	
	
DCL_System_Pool.ReleaseItemNumber   procedure(long itemNumber)
	code
	self.AccessLock.Wait()
	self.ItemQ.ItemNumber = itemNumber
	get(self.ItemQ,self.ItemQ.ItemNumber)
	if not errorcode()
		self.itemq.InUse = false
		put(self.ItemQ)
	end
	self.AccessLock.Release()
	
DCL_System_Pool.SetStopOnError          procedure(bool StopOnError,<string errorMessage>)
	code
	self.StopOnError = StopOnError
	if not omitted(errorMessage) then self.StopOnErrorMessage = errorMessage.

The logic is pretty easy to follow; when you get a pool element it's marked as in use; when you're done with it and release it the flag is cleared. There's also some support for customizing error handling. And to make the class thread safe I've added a critical section.

Clearly even if this is the only place I ever use the class, having it in its own INC and CLW makes the class easier to work with, and more visible within the DCL library. 

Inside DCL_System_IO_AsciiFileManager I added five file layouts:

Code Block
 AsciiFileName1 STRING(MaxPathLength),static
AsciiFile1                              FILE,DRIVER('ASCII','/CLIP = on'),CREATE,NAME(AsciiFileName1),PRE(AsciiFile1)
                                            RECORD
Txt                                             STRING(ASCII_IO_RECORD_SIZE)
                                            END
                                        END

AsciiFileName2                          STRING(MaxPathLength),static
AsciiFile2                              FILE,DRIVER('ASCII','/CLIP = on'),CREATE,NAME(AsciiFileName2),PRE(AsciiFile2)
											RECORD
Txt                                             STRING(ASCII_IO_RECORD_SIZE)
											END
										END


AsciiFileName3                          STRING(MaxPathLength),static
AsciiFile3                              FILE,DRIVER('ASCII','/CLIP = on'),CREATE,NAME(AsciiFileName3),PRE(AsciiFile3)
											RECORD
Txt                                             STRING(ASCII_IO_RECORD_SIZE)
											END
										END


AsciiFileName4                          STRING(MaxPathLength),static
AsciiFile4                              FILE,DRIVER('ASCII','/CLIP = on'),CREATE,NAME(AsciiFileName4),PRE(AsciiFile4)
											RECORD
Txt                                             STRING(ASCII_IO_RECORD_SIZE)
											END
										END


AsciiFileName5                          STRING(MaxPathLength),static
AsciiFile5                              FILE,DRIVER('ASCII','/CLIP = on'),CREATE,NAME(AsciiFileName5),PRE(AsciiFile5)
											RECORD
Txt                                             STRING(ASCII_IO_RECORD_SIZE)
											END
										END

I also added a class derived from DCL_System_Pool. This class has a constructor that initializes the pool object with five elements, one for each of five layouts:

Code Block
AsciiFilePool                           class(DCL_System_Pool)
Construct                                   procedure
										end


AsciiFilePool.Construct                 procedure
	code
	self.Init(5)
	self.SetStopOnError(true,'DCL_System_IO_AsciiFile: All available ASCII files are in use!')

Finally I added some code to the DCL_System_IO_AsciiFile constructor to get the first available ASCII file (via the pool object) and then one line to the destructor to release the ASCII file. 

Code Block
DCL_System_IO_AsciiFile.Construct        PROCEDURE()
    CODE
	self.Errors &= new DCL_System_ErrorManager
	self.PoolItemNumber = AsciiFilePool.GetItemNumber()
	execute self.PoolItemNumber
		self.Init(AsciiFile1,AsciiFile1:Record,AsciiFileName1)
		self.Init(AsciiFile2,AsciiFile2:Record,AsciiFileName2)
		self.Init(AsciiFile3,AsciiFile3:Record,AsciiFileName3)
		self.Init(AsciiFile4,AsciiFile4:Record,AsciiFileName4)
		self.Init(AsciiFile5,AsciiFile5:Record,AsciiFileName5)
	else
		stop('Unable to initialize the ASCIIFile instance - if you continue the program will probably crash')
	end
	
DCL_System_IO_AsciiFile.Destruct         PROCEDURE()
	CODE
    SELF.CloseFile()
	AsciiFilePool.ReleaseItemNumber(self.PoolItemNumber)
	dispose(self.Errors)


I've updated the GitHub repository with these changes. If you previously used the ASCIIFileManager approach then you'll need to make two simple changes:

Remove the ASCIIFileManager call

Most importantly, remove the leading & from your DCL_System_IO_AsciiFile declaration to change it from a reference to an instance.

I wish I'd made this change sooner - the refactoring wasn't all that difficult, the class is much, much easier to use, and I've added a new pool tool to the kit in the event I need similar functionality somewhere else.