Alfresco repository filesystem oplocks implementation

Oplock support can be switched off using the 'disableOplocks' property

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18116 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer
2010-01-19 10:50:23 +00:00
parent 8db9d90a72
commit db17ae8831
5 changed files with 441 additions and 4 deletions

View File

@@ -25,15 +25,25 @@
package org.alfresco.filesys.state;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.locking.FileLock;
import org.alfresco.jlan.locking.LockConflictException;
import org.alfresco.jlan.locking.NotLockedException;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.filesys.ExistingOpLockException;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.TreeConnection;
import org.alfresco.jlan.server.filesys.pseudo.MemoryNetworkFile;
import org.alfresco.jlan.server.locking.LockManager;
import org.alfresco.jlan.server.locking.OpLockDetails;
import org.alfresco.jlan.server.locking.OpLockManager;
import org.alfresco.jlan.smb.OpLock;
import org.alfresco.jlan.smb.SMBStatus;
import org.alfresco.jlan.smb.server.SMBSrvPacket;
import org.alfresco.jlan.smb.server.SMBSrvSession;
import org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
/**
@@ -43,8 +53,25 @@ import org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
*
* @author gkspencer
*/
public class FileStateLockManager implements LockManager {
public class FileStateLockManager implements LockManager, OpLockManager, Runnable {
// Oplock break timeout
private static final long OpLockBreakTimeout = 5000L; // 5 seconds
// File state cache used for byte range locks/oplocks
private FileStateTable m_stateCache;
// Oplock breaks in progress
private Hashtable<String, OpLockDetails> m_oplockQueue;
// Oplock break timeout thread
private Thread m_expiryThread;
private boolean m_shutdown;
/**
* Lock a byte range within a file, or the whole file.
*
@@ -176,4 +203,294 @@ public class FileStateLockManager implements LockManager {
}
}
}
/**
* Enable oplock support by setting the file state table
*
* @param stateTable FileStateTable
*/
public final void setStateTable(FileStateTable stateTable) {
m_stateCache = stateTable;
// Create the oplock break queue
m_oplockQueue = new Hashtable<String, OpLockDetails>();
// Start the oplock break expiry thread
m_expiryThread = new Thread(this);
m_expiryThread.setDaemon(true);
m_expiryThread.setName("OpLockExpire");
m_expiryThread.start();
}
/**
* Check if there is an oplock for the specified path, return the oplock type.
*
* @param path String
* @return int
*/
public int hasOpLock(String path) {
// Check if oplocks/state cache are enabled
if ( m_stateCache == null)
return OpLock.TypeNone;
// Get the file state
FileState fstate = m_stateCache.findFileState(path);
if ( fstate != null && fstate.hasOpLock()) {
// Return the oplock type
OpLockDetails oplock = fstate.getOpLock();
if ( oplock != null)
return oplock.getLockType();
}
// No oplock
return OpLock.TypeNone;
}
/**
* Return the oplock details for a path, or null if there is no oplock on the path
*
* @param path String
* @return OpLockDetails
*/
public OpLockDetails getOpLockDetails(String path) {
// Check if oplocks/state cache are enabled
if ( m_stateCache == null)
return null;
// Get the file state
FileState fstate = m_stateCache.findFileState(path);
if ( fstate != null)
return fstate.getOpLock();
// No oplock
return null;
}
/**
* Grant an oplock, store the oplock details
*
* @param path String
* @param oplock OpLockDetails
* @return boolean
* @exception ExistingOpLockException If the file already has an oplock
*/
public boolean grantOpLock(String path, OpLockDetails oplock)
throws ExistingOpLockException {
// Check if oplocks/state cache are enabled
if ( m_stateCache == null)
return false;
// Get, or create, a file state
FileState fstate = m_stateCache.findFileState(path, false, true);
// Check if the file is already in use
if ( fstate.getOpenCount() != 1)
return false;
// Set the oplock
fstate.setOpLock( oplock);
return true;
}
/**
* Inform the oplock manager that an oplock break is in progress for the specified file/oplock
*
* @param path String
* @param oplock OpLockDetails
*/
public void informOpLockBreakInProgress(String path, OpLockDetails oplock) {
// Check if oplocks/state cache are enabled
if ( m_stateCache == null)
return;
// Add the oplock to the break in progress queue
synchronized ( m_oplockQueue) {
m_oplockQueue.put( path, oplock);
m_oplockQueue.notify();
}
}
/**
* Release an oplock
*
* @param path String
*/
public void releaseOpLock(String path) {
// Check if oplocks/state cache are enabled
if ( m_stateCache == null)
return;
// Get the file state
FileState fstate = m_stateCache.findFileState(path);
if ( fstate != null)
fstate.clearOpLock();
// Remove from the pending oplock break queue
synchronized ( m_oplockQueue) {
m_oplockQueue.remove( path);
}
}
/**
* Check for expired oplock break requests
*
* @return int
*/
public int checkExpiredOplockBreaks() {
// Check if there are ny oplock breaks in progress
if ( m_oplockQueue.size() == 0)
return 0;
// Check for oplock break requests that have expired
int expireCnt = 0;
long timeNow = System.currentTimeMillis();
Enumeration<String> opBreakKeys = m_oplockQueue.keys();
while ( opBreakKeys.hasMoreElements()) {
// Check the current oplock break
String path = opBreakKeys.nextElement();
OpLockDetails opLock = m_oplockQueue.get( path);
if ( opLock != null) {
// Check if the oplock break has timed out
if ( opLock.hasDeferredSession() && (opLock.getOplockBreakTime() + OpLockBreakTimeout) <= timeNow) {
// Get the deferred request details
SMBSrvSession sess = opLock.getDeferredSession();
SMBSrvPacket pkt = opLock.getDeferredPacket();
try {
// Return an error for the deferred file open request
if ( sess.sendAsyncErrorResponseSMB( pkt, SMBStatus.NTAccessDenied, SMBStatus.NTErr) == true) {
// DEBUG
if ( Debug.EnableDbg && sess.hasDebug( SMBSrvSession.DBG_OPLOCK))
sess.debugPrintln( "Oplock break timeout, oplock=" + opLock);
// Release the packet back to the pool
sess.getPacketPool().releasePacket( pkt);
}
else if ( Debug.EnableDbg && sess.hasDebug( SMBSrvSession.DBG_OPLOCK))
sess.debugPrintln( "Failed to send open reject, oplock break timed out, oplock=" + opLock);
}
catch ( IOException ex) {
}
// Remove the oplock break from the queue
m_oplockQueue.remove( path);
// Clear the deferred packet details
opLock.clearDeferredSession();
// Mark the oplock has having a failed oplock break
opLock.setOplockBreakFailed();
// Update the expired oplock break count
expireCnt++;
}
}
}
// Return the count of expired oplock breaks
return expireCnt;
}
/**
* Run the oplock break expiry
*/
public void run()
{
// Loop forever
m_shutdown = false;
while ( m_shutdown == false)
{
// Wait for an oplock break or sleep for a while if there are active oplock break requests
try
{
synchronized ( m_oplockQueue) {
if ( m_oplockQueue.size() == 0)
m_oplockQueue.wait();
}
// Oplock break added to the queue, wait a while before checking the queue
if ( m_oplockQueue.size() > 0)
Thread.sleep( OpLockBreakTimeout);
}
catch (InterruptedException ex)
{
}
// Check for shutdown
if ( m_shutdown == true)
return;
// Check for expired oplock break requests
checkExpiredOplockBreaks();
}
}
/**
* Request the oplock break expiry thread to shutdown
*/
public final void shutdownRequest() {
m_shutdown = true;
if ( m_expiryThread != null)
{
try {
m_expiryThread.interrupt();
}
catch (Exception ex) {
}
}
}
}