5.4.3 log::log (The Open Constructor)



next up previous
Next: 5.4.4 log::~log Up: 5.4 class log Previous: 5.4.2 log::log (The Create

5.4.3 log::log (The Open Constructor)

 

The open constructor will open the log file, attach to the shared memory structure, and initialize data members so that the log is operational. Each Minirel process will have an object of class log in private memory. The shared memory is accessed through global_shm_log_ptr. The following paragraphs describe the sequence of operations that the open constructor goes through.

The open constructor must mutually exclude other opens from occurring concurrently. If the opens of the first two transactions access the shared memory structures without mutual exclusion, each open may think that it was the first one and a race condition will occur. To prevent this, the open constructor downs the sl_sem semaphore before attempting anything else. Thus, sl_sem is used to mutually exclude opens from each other.

However, it may be argued that the internal semaphore that each shared memory segment has (by virtue of deriving from class SharedRegion) can be used for mutual exclusion. This semaphore is used by all methods to mutually exclude access to the shared memory structure. Unfortunately, this will not work for the open constructors of the first set of transactions if they all start their opens more or less simultaneously. Consider the following code fragment:

    global_shm_log_ptr->lock();            //  down the semaphore
    if (global_shm_log_ptr->sl_n_xact > 0) {
        global_shm_log_ptr->sl_n_xact++;
        global_shm_log_ptr->unlock();
        return;
    }
    global_shm_log_ptr->sl_n_xact = 1;

    //
    //  We are the first transaction.  Initialize shared memory
    //  structures.  Fill in the information by reading the header
    //  page from the log file.  Call recovery manager to perform
    //  restart.
    //
    ...
This segment will not work because the recovery manager will attempt to read the log. Log::read will try to down the semaphore again and will block everyone forever. We have to unlock before calling recovery.

However, if we unlock before recovery, it could happen that some other transaction enters its open, detects that it is not the first, increments sl_n_xact, does not perform recovery, and starts reading and writing the log. Unfortunately, since recovery has not run yet, the log will be corrupted if another transaction attempts to read and write it.

It may appear that the second transaction went along corrupting the log because it saw that sl_n_xact was 1. However, setting sl_n_xact to 1 finally (instead of at the start) is not a correct solution. If this were done, the second transaction will also think that it is the first one in the system and perform restart recovery. There will be two restarts running concurrently. Since the recovery manager assumes that at most one copy of it is running in restart recovery mode at any time, this solution is incorrect.

The correct solution is that we must mutually exclude opens from each other. In that case, no other transaction can open the log while another transaction is attempting an open. Also, the first transaction gets to run restart recovery without the danger of another transaction doing the same. Further, since we do a global_shm_log_ptr->unlock() before restart recovery, the recovery manager can safely issue log::read and log::write calls. No other transaction will simultaneously be trying to execute global_shm_log_ptr->lock() because it is guaranteed that only the first transaction will run recovery. Downing sl_sem prevents other transactions from entering the system.

The following argument demonstrates that there is no deadlock:

Thus, every open requests the downs in the same order and other methods never down sl_sem. Therefore, there can never be a deadlock.

Since every method ups semaphores that it downs and there is no deadlock, there is no possibility of starvation.

Initialization of shared memory structures, etc., is straightforward. It will not be described here.

There is one further problem that must be resolved. The main() function of a Minirel process instantiates a global object of class log by executing:

            extern log *logMgr;
            char        log_file_pathname[MAXPATHNAME];
            Status      error_status;

            logMgr = new log(log_file_pathname, error_status);

Therefore, logMgr has not been initialized when the open constructor is in progress. However, the open constructor, on detecting that it is the first transaction to use the database, will start restart recovery. Restart recovery will then read log records by invoking:

            logMgr->read(...);
This will fail because logMgr has not been initialized as yet. The solution we adopted was to execute
            logMgr = this;
in the open constructor code just before calling restart recovery.



next up previous
Next: 5.4.4 log::~log Up: 5.4 class log Previous: 5.4.2 log::log (The Create



ajitk@cs.wisc.edu, cjin@cs.wisc.edu