private void ReleaseLock(LockInfo lockInfo) { DebugContract.Requires(lockInfo, "lockInfo"); if (lockInfo.Lock.IsWriteLockHeld) { lockInfo.Lock.ExitWriteLock(); } if (lockInfo.Lock.IsUpgradeableReadLockHeld) { lockInfo.Lock.ExitUpgradeableReadLock(); } _trace.WriteTrace(TraceCategory.LockManager, "Release lock for {0} in tx {1}", lockInfo.Ressource, lockInfo.SessionId); // Suppression des locks associés à cette transaction si il n'y en a pas en attente. if (lockInfo.DecRef() == 0) { Debug.Assert(lockInfo.Lock.WaitingWriteCount == 0 && lockInfo.Lock.WaitingUpgradeCount == 0); _sync.EnterWriteLock(); try { _locks.Remove(lockInfo.Ressource); } finally { _sync.ExitWriteLock(); } } }
///------------------------------------------------------------------------------------------------- /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged /// resources. /// </summary> ///------------------------------------------------------------------------------------------------- public void Dispose() { if (_lockInfo != null) { _lockManager.ReleaseLock(_lockInfo); } _lockInfo = null; _lockManager = null; }
internal LockWrapper(LockManager lockManager = null, LockInfo lockInfo = null) { _lockInfo = lockInfo; _lockManager = lockManager; }
///------------------------------------------------------------------------------------------------- /// <summary> /// Acquires the lock. /// </summary> /// <exception cref="DeadLockException"> /// Thrown when a Dead Lock error condition occurs. /// </exception> /// <exception cref="SerializableTransactionException"> /// Thrown when a Serializable Transaction error condition occurs. /// </exception> /// <param name="session"> /// The session. /// </param> /// <param name="key"> /// The key. /// </param> /// <param name="mode"> /// The mode. /// </param> /// <returns> /// An IDisposable. /// </returns> ///------------------------------------------------------------------------------------------------- public IDisposable AcquireLock(ISession session, object key, LockType mode) { Contract.Requires(key, "key"); Contract.Requires(session, "session"); //if (session.IsDisposing) //{ // throw new Exception("Impossible d'acquerir un lock sur une session en train de se terminer. Si vous avez lancé des traitements en parralléles pendant la session, assurez vous qu'ils soient terminé quand la session est disposé."); //} LockInfo info; var deadlockTimeLimit = DeadLockLimitTimeInMs; // Un lock est tenu par la transaction englobante. // Il sera libéré quand celle ci se terminera var txId = session.SessionId; var resource = key.ToString(); _trace.WriteTrace(TraceCategory.LockManager, "Try to acquire lock for {0} in tx {1}", resource, txId); // On va essayer d'obtenir un lock virtuel pour la ressource demandée // Un lock virtuel est une structure qui contient le lock physique et le map entre la transaction qui a obtenu le lock et la ressource concernée _sync.EnterWriteLock(); try { // Etat du lock virtuel avant d'essayer d'avoir le lock physique if (!_locks.TryGetValue(resource, out info)) { // Ce lock n'a jamais été traité, on le crée info = new LockInfo { SessionId = txId, Ressource = resource, Lock = new ReaderWriterLockSlim(), SessionStatus = TransactionStatus.Active, Mode = mode }; // Nouveau lock, on peut obtenir tout de suite le lock physique if ((mode & LockType.Exclusive) == LockType.Exclusive) { info.Lock.EnterWriteLock(); } else { info.Lock.EnterUpgradeableReadLock(); } _locks[resource] = info; session.Locks.Add(info); info.AddRef(); _trace.WriteTrace(TraceCategory.LockManager, "Lock acquired for {0} in tx {1}", resource, txId); return(new LockWrapper(this, info)); } // Ce lock existe dèjà. // Pas grave si c'est dans le même thread if (session.SessionId == info.SessionId) { if (info.Mode != mode) { if (info.Mode == LockType.Shared) { info.Lock.EnterWriteLock(); info.Mode = mode; info.Promoted = true; } } _trace.WriteTrace(TraceCategory.LockManager, "Use lock from parent transaction for {0} in tx {1}", resource, txId); return(new LockWrapper()); // Il n'est pas possible de libèrer ce lock ici } info.AddRef(); _trace.WriteTrace(TraceCategory.LockManager, "Waiting lock for {0} in tx {1}", resource, txId); } finally { _sync.ExitWriteLock(); } // Le lock est dèjà pris. // On va essayer de l'obtenir mais avec un time out (pour pouvoir traiter le dead lock) if (((LockType.Exclusive & mode) == LockType.Exclusive ? !info.Lock.TryEnterWriteLock(deadlockTimeLimit) : !info.Lock.TryEnterUpgradeableReadLock(deadlockTimeLimit))) { _trace.WriteTrace(TraceCategory.LockManager, "Deadlock for {0} in tx {1}", resource, txId); if (info.DecRef() == 0) { _sync.EnterWriteLock(); try { if (info.DecRef() == 0) { _locks.Remove(resource); } } finally { _sync.ExitWriteLock(); } } // On ne l'a pas throw new DeadLockException(); // c'est un dead lock, on abandonne } _trace.WriteTrace(TraceCategory.LockManager, "Transaction {0} has released lock for {1}", info.SessionId, resource); // On vient d'avoir le lock // Si on est pas dans le scope de la transaction qui tenait le lock et que le niveau de sérialisation est Serialize alors // Si la transaction qui tenait le lock s'est correctement terminé il y a conflit if (session.SessionIsolationLevel == SessionIsolationLevel.Serializable && session.SessionId != info.SessionId) { // Est ce que la transaction concurrente s'est bien terminée ? // Si concurrentTransaction est null, c'est que la transaction référencée s'est bien terminée et qu'elle a été purgé par le vaccum if (info.SessionStatus == TransactionStatus.Committed) { // Si on est en mode exclusive, on va générer une erreur if ((mode & LockType.Exclusive) == LockType.Exclusive) { if ((mode & LockType.ExclusiveWait) != LockType.ExclusiveWait) { _trace.WriteTrace(TraceCategory.LockManager, "Serialize transaction error for {0}", resource); _sync.EnterWriteLock(); try { if (info.DecRef() == 0) { _locks.Remove(resource); } } finally { _sync.ExitWriteLock(); } info.Lock.ExitWriteLock(); throw new SerializableTransactionException(resource); } } } } // Sinon tout est OK _trace.WriteTrace(TraceCategory.LockManager, "Lock acquired (after waiting) for {0} in tx {1}", resource, txId); info.SessionId = txId; info.SessionStatus = TransactionStatus.Active; info.Mode = mode; _sync.EnterWriteLock(); try { session.Locks.Add(info); } finally { _sync.ExitWriteLock(); } return(new LockWrapper(this, info)); }