The PersisentStorage class (See Figure 3.9) is the control tower of the persistent storage classes. The most significant characteristic of the class is that the instance must be unique in the process. Only one instance can be instantiated in the process.
The class has four important member variables - arenaTable, entryTable, baseDir, and totalWindowSize. The arenaTable is the table of the current Arena objects. The entryTable is the table of the Entry objects. The baseDir is the directory name for the Arena storage files. The totalWindowSize is the current size of mapped Windows in total.
The constructor takes the baseDir as the only parameter. It initializes the arenaTable and the entryTable by probing the entries of the base directory. If there are already some Arena storage files, it instantiates the Arena objects for the storage files. If there is the file for the entry points in the base directory, it opens the file and initializes the entryTable by the contents.
The destructor synchronizes all the Arenas and destructs them. The Arenas synchronize all the Windows in turn. The destructor stores the current contents of the entryTable in the entry point file.
The file name of each Arena storage file has the fixed format Arena00000001.psa, where the hexadecimal number indicates its arenaID and the extension .psa represents the initials of `Persistent Storage Arena.' More detailed discussions on the arenaID appear in the Section 3.3.4. On the other hand, the file for the entry points is named EntryPoint.psa.
The operation allocate(size) requests a storage block bigger than the size bytes in the appropriate Arena. When there are no Arenas for the storage block, it creates an Arena. When there are no free blocks in the appropriate Arena, the operation makes the Arena grow for a new free block. If the allocation of the storage block succeeds, it returns the PersistentPointer object that points the block.
The operation destroy(persistentPointer) restores the allocated block pointed by the persistentPointer to the free blocks in the Arena. The persistentPointer object must be grabbing the object when the operation is invoked.
The operation synchronize() synchronizes all the mapping with the storage files. It takes long time to complete all the synchronization operations.
The operations getEntryPoint() and setEntryPoint() are the accessors for the entryTable. The need for the mechanism of entry points comes from the life cycles of persistent pointers, which are longer than a process. In a normal case, the process manipulates the persistent storage and constructs some graphs in it. If the process should terminate without registering the clues to the graphs in the entryTable, other processes, which are usually the same program, could not find any clues to the stored graphs in the persistent storage.
An entry point is a pair of a PersistentPointer and an integer EntryID. If the EntryID is assigned properly, it can serve as the clue to the paired PersistentPointer object. (See Section 3.3.10)
The operations grab(), release(), grabReadOnly(). and releaseReadOnly() are not public ones, but they are invoked by the corresponding operations in the PersistentPointer class. In other words, these operations are redirected from the PersistentPointer. This mechanism is achieved by the static operation getThePS(), which returns the pointer to the unique instance of the PersistentStorage. The global variable ps is initialized by the constructor for pointing the unique instance. These operations are also redirected to the appropriate Arena objects.
The remaining two operations - notifyMapping() and notifyUnmapping() are also not public. Whenever the Window object is instantiated and destructed, they are invoked for calculating the total size of the mapped Windows in the main memory. The size is used by the private operation cleanUp() to decide whether to clean up the removable Windows for reducing the usage of the main memory.