package com.sleepycat.je.log; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.RunRecoveryException; import com.sleepycat.je.config.EnvironmentParams; import com.sleepycat.je.dbi.EnvironmentImpl; import com.sleepycat.je.utilint.PropUtil; import de.ovgu.cide.jakutil.*; class FSyncManager { private EnvironmentImpl envImpl; private long timeout; private volatile boolean fsyncInProgress; private FSyncGroup nextFSyncWaiters; FSyncManager( EnvironmentImpl envImpl) throws DatabaseException { timeout=PropUtil.microsToMillis(envImpl.getConfigManager().getLong(EnvironmentParams.LOG_FSYNC_TIMEOUT)); this.envImpl=envImpl; this.hook434(envImpl); fsyncInProgress=false; nextFSyncWaiters=new FSyncGroup(timeout,envImpl); } /** * Request that this file be fsynced to disk. This thread may or may not * actually execute the fsync, but will not return until a fsync has been * issued and executed on behalf of its write. There is a timeout period * specified by EnvironmentParam.LOG_FSYNC_TIMEOUT that ensures that no * thread gets stuck here indefinitely. * When a thread comes in, it will find one of two things. * 1. There is no fsync going on right now. This thread should go * ahead and fsync. * 2. There is an active fsync, wait until it's over before * starting a new fsync. * When a fsync is going on, all those threads that come along are grouped * together as the nextFsyncWaiters. When the current fsync is finished, * one of those nextFsyncWaiters will be selected as a leader to issue the * next fsync. The other members of the group will merely wait until the * fsync done on their behalf is finished. * When a thread finishes a fsync, it has to: * 1. wake up all the threads that were waiting for its fsync call. * 2. wake up one member of the next group of waiting threads (the * nextFsyncWaiters) so that thread can become the new leader * and issue the next fysnc call. * If a non-leader member of the nextFsyncWaiters times out, it will issue * its own fsync anyway, in case something happened to the leader. */ void fsync() throws DatabaseException { boolean doFsync=false; boolean isLeader=false; boolean needToWait=false; FSyncGroup inProgressGroup=null; FSyncGroup myGroup=null; synchronized (this) { this.hook435(); if (fsyncInProgress) { needToWait=true; myGroup=nextFSyncWaiters; } else { isLeader=true; doFsync=true; fsyncInProgress=true; inProgressGroup=nextFSyncWaiters; nextFSyncWaiters=new FSyncGroup(timeout,envImpl); } } if (needToWait) { int waitStatus=myGroup.waitForFsync(); if (waitStatus == FSyncGroup.DO_LEADER_FSYNC) { synchronized (this) { if (!fsyncInProgress) { isLeader=true; doFsync=true; fsyncInProgress=true; inProgressGroup=myGroup; nextFSyncWaiters=new FSyncGroup(timeout,envImpl); } } } else if (waitStatus == FSyncGroup.DO_TIMEOUT_FSYNC) { doFsync=true; this.hook436(); } } if (doFsync) { executeFSync(); synchronized (this) { this.hook437(); if (isLeader) { inProgressGroup.wakeupAll(); nextFSyncWaiters.wakeupOne(); fsyncInProgress=false; } } } } /** * Put the fsync execution into this method so it can be overridden for * testing purposes. */ protected void executeFSync() throws DatabaseException { envImpl.getFileManager().syncLogEnd(); } static class FSyncGroup { static int DO_TIMEOUT_FSYNC=0; static int DO_LEADER_FSYNC=1; static int NO_FSYNC_NEEDED=2; private volatile boolean fsyncDone; private long fsyncTimeout; private boolean leaderExists; private EnvironmentImpl envImpl; FSyncGroup( long fsyncTimeout, EnvironmentImpl envImpl){ this.fsyncTimeout=fsyncTimeout; fsyncDone=false; leaderExists=false; this.envImpl=envImpl; } synchronized boolean getLeader(){ if (fsyncDone) { return false; } else { if (leaderExists) { return false; } else { leaderExists=true; return true; } } } /** * Wait for either a turn to execute a fsync, or to find out that a * fsync was done on your behalf. * @return true if the fsync wasn't done, and this thread needs to * execute a fsync when it wakes up. This may be true because it's the * leader of its group, or because the wait timed out. */ synchronized int waitForFsync() throws RunRecoveryException { int status=0; if (!fsyncDone) { long startTime=System.currentTimeMillis(); while (true) { try { wait(fsyncTimeout); } catch ( InterruptedException e) { throw new RunRecoveryException(envImpl,"Unexpected interrupt while waiting for fsync",e); } if (fsyncDone) { status=NO_FSYNC_NEEDED; break; } else { if (!leaderExists) { leaderExists=true; status=DO_LEADER_FSYNC; break; } else { long now=System.currentTimeMillis(); if ((now - startTime) > fsyncTimeout) { status=DO_TIMEOUT_FSYNC; break; } } } } } return status; } synchronized void wakeupAll(){ fsyncDone=true; notifyAll(); } synchronized void wakeupOne(){ notify(); } } protected void hook434( EnvironmentImpl envImpl) throws DatabaseException { } protected void hook435() throws DatabaseException { } protected void hook436() throws DatabaseException { } protected void hook437() throws DatabaseException { } }