/// <summary> /// lock a record /// </summary> /// <param name="ctx"></param> /// <param name="entry"></param> /// <returns></returns> public DBResult LockRec(ref LockContext ctx, out LockEntry entry) { var locks = RecLocks.GetValueOrDefault(ctx.Page); if (locks == null || locks.Count == 0) { entry = CreateRecLock(ref ctx); return(DBResult.Success); } if (IsHeldTabLock(ref ctx, out entry)) { return(DBResult.Success); } if (IsHeldRecLock(ref ctx, out entry)) { return(DBResult.Success); } if (IsOthersHeldOrWaitConflictRecLock(ctx.Transaction, ctx.Page, ctx.RecordIndex, ctx.Flags)) { ctx.Flags |= LockFlags.Waiting; return(CreateRecLockForWait(ref ctx, out entry)); } entry = GetCanReuseRecLock(ref ctx) ?? CreateRecLock(ref ctx); return(DBResult.Success); }
/// <summary> /// create a new table-lock /// </summary> /// <param name="ctx"></param> /// <returns></returns> private LockEntry CreateTabLock(ref LockContext ctx) { if (!TabLocks.TryGetValue(ctx.Index, out var list)) { list = new LinkedList <LockEntry>(); TabLocks[ctx.Index] = list;; } var tx = ctx.Transaction; var entry = new LockEntry(ctx.Page, ctx.Flags, ctx.RecordCount); if (entry.IsWaiting) { tx.WaitLock = entry; } list.AddLast(entry); tx.Locks.AddLast(entry); entry.GNode = list.Last; entry.TNode = tx.Locks.Last; entry.Index = ctx.Index; entry.Transaction = ctx.Transaction; entry.Thread = Thread.CurrentThread.ManagedThreadId; return(entry); }
/// <summary> /// un-lock-tab and try wake up from the next to the end /// </summary> /// <param name="entry"></param> /// <param name="next"></param> /// <returns></returns> internal DBResult UnLockTab(LockEntry entry, LockEntry next) { while (next != null) { if (!next.IsWaiting) { next = FindNextLockEntry(next); continue; } if (IsOthersHeldOrWaitConflictTabLock(next.Transaction, next.Index, next.Flags, next)) { next = FindNextLockEntry(next); continue; } next.Flags |= ~LockFlags.Waiting; next.Transaction.WaitLock = null; next.Transaction.WaitEvent.Set(); next.Transaction.State = TransactionState.Running; next = FindNextLockEntry(next); } return(DBResult.Success); }
/// <summary> /// un-lock-rec and try wake up from the next to the end /// </summary> /// <param name="entry"></param> /// <param name="next"></param> /// <returns></returns> internal DBResult UnLockRec(LockEntry entry, LockEntry next) { while (next != null) { if (!next.IsWaiting) { next = FindNextLockEntry(next); continue; } var slot = next.GetFirstBitSlot(); if (slot < 0 || entry.GetBit(slot) == 0) { next = FindNextLockEntry(next); continue; } if (IsOthersHeldOrWaitConflictRecLock(next.Transaction, next.Page, slot, next.Flags, next)) { next = FindNextLockEntry(next); continue; } next.Flags &= ~LockFlags.Waiting; next.Transaction.WaitLock = null; next.Transaction.WaitEvent.Set(); next.Transaction.State = TransactionState.Running; next = FindNextLockEntry(next); } return(DBResult.Success); }
/// <summary> /// check current transaction has held the table's lock >=ctx-lock-level and not been in wait state /// </summary> /// <param name="ctx"></param> /// <returns></returns> private bool IsHeldTabLock(ref LockContext ctx, out LockEntry entry) { var locks = TabLocks.GetValueOrDefault(ctx.Index); if (locks == null) { entry = null; return(false); } for (var node = locks.First; node != null; node = node.Next) { var lockEntry = node.Value; if (lockEntry.Transaction != ctx.Transaction) { continue; } if (lockEntry.IsExclusive || !ctx.Flags.IsExclusive()) { entry = lockEntry; return(true); } } entry = null; return(false); }
/// <summary> /// create new rec lock then waitting /// </summary> /// <param name="ctx"></param> /// <param name="entry"></param> /// <returns></returns> private DBResult CreateRecLockForWait(ref LockContext ctx, out LockEntry entry) { var recEntry = CreateRecLock(ref ctx); if (!recEntry.IsWaiting) { recEntry.Flags |= LockFlags.Waiting; } entry = recEntry; entry.Transaction.WaitLock = recEntry; entry.Transaction.State = TransactionState.Waitting; //check dead-lock if (IsCausedDeadLock(recEntry)) { entry.Flags &= ~LockFlags.Waiting; entry.SetBit(ctx.RecordIndex, 0); entry.Transaction.WaitLock = null; return(DBResult.DeadLock); } entry.Transaction.WaitEvent.Reset(); return(DBResult.WaitLock); }
/// <summary> /// check if the lock caused dead-lock /// </summary> /// <param name="initTx"></param> /// <returns></returns> private bool IsCausedDeadLock(Transaction initTx, LockEntry checkEntry) { var entry = checkEntry; if (entry.Transaction.DeadFlags == 1) { return(true); } var lockBit = entry.IsTable ? 0 : entry.GetFirstBitSlot(); if (lockBit < 0) { return(false); } while (true) { var prevEntry = FindPrevLockEntry(entry); if (prevEntry == null) { checkEntry.Transaction.DeadFlags = 1; return(false); } if (prevEntry.Transaction.State != TransactionState.Waitting || prevEntry.Transaction == checkEntry.Transaction || (!prevEntry.IsTable && prevEntry.GetBit(lockBit) == 0)) { entry = prevEntry; continue; } if (IsLockConflict(checkEntry, prevEntry)) { if (prevEntry.Transaction == initTx) { return(true); } if (prevEntry.Transaction.State == TransactionState.Waitting && IsCausedDeadLock(initTx, prevEntry.Transaction.WaitLock)) { return(true); } } entry = prevEntry; } }
/// <summary> /// check if the lock caused dead-lock /// </summary> /// <param name="entry"></param> /// <returns></returns> private bool IsCausedDeadLock(LockEntry entry) { if (entry.Transaction.Locks.Count == 1 || !entry.IsWaiting) { return(false); } foreach (var trx in EngineEnviorment.Transactions.Values) { trx.DeadFlags = 0; } return(IsCausedDeadLock(entry.Transaction, entry)); }
private unsafe LockEntry SplitRecLock(LockEntry fromEntry, PagePosition to, int mid) { if (fromEntry == null) { return(null); } var count = fromEntry.Bits.Length - (mid >> 3) + 1; var buffer = stackalloc byte[count]; var bites = new byte[fromEntry.Bits.Length]; Array.Copy(fromEntry.Bits, bites, bites.Length); fromEntry.CopyBitsTo(mid, buffer); fromEntry.TuncateBits(mid); if (fromEntry.IsEmpty && fromEntry.Page.PageNumber != 0) { fromEntry.Bits = bites; fromEntry.CopyBitsTo(mid, buffer); fromEntry.TuncateBits(mid); } for (var i = 0; i < count; i++) { if (buffer[i] == 0) { continue; } var toEntry = new LockEntry() { Bits = new byte[count], Page = to, Flags = fromEntry.Flags, Index = fromEntry.Index, Thread = fromEntry.Thread, Transaction = fromEntry.Transaction }; Unsafe.CopyBlockUnaligned(ref toEntry.Bits[0], ref *buffer, (uint)count); return(toEntry); } return(null); }
/// <summary> /// </summary> /// <param name="ctx"></param> /// <returns></returns> public DBResult UnLock(LockEntry entry) { lock (SyncRoot) { if (entry == null) { return(DBResult.Success); } var list = entry.GNode.List; var next = FindNextLockEntry(entry); list.Remove(entry.GNode); return(entry.IsTable ? UnLockTab(entry, next) : UnLockRec(entry, next)); } }
internal bool IsLockConflict(LockEntry entry, LockEntry other) { if (entry == other || entry.Transaction == other.Transaction) { return(false); } if (entry.IsTable && entry.IsExclusive || other.IsTable && other.IsExclusive) { return(true); } else if (entry.IsTable || other.IsTable) { return(true); } else { return(entry.IsExclusive || other.IsExclusive); } }
/// <summary> /// create new rec lock then waitting /// </summary> /// <param name="ctx"></param> /// <param name="entry"></param> /// <returns></returns> private DBResult CreateTabLockForWait(ref LockContext ctx, out LockEntry entry) { var tabEntry = CreateTabLock(ref ctx); if (!tabEntry.IsWaiting) { tabEntry.Flags |= LockFlags.Waiting; } entry = tabEntry; entry.Transaction.WaitLock = tabEntry; entry.Transaction.State = TransactionState.Waitting; //check dead-lock if (IsCausedDeadLock(tabEntry)) { entry.Flags &= ~LockFlags.Waiting; entry.Transaction.WaitLock = null; return(DBResult.DeadLock); } entry.Transaction.WaitEvent.Reset(); return(DBResult.WaitLock); }
/// <summary> /// lock a table /// </summary> /// <param name="ctx"></param> /// <param name="entry"></param> /// <returns></returns> public DBResult LockTab(ref LockContext ctx, out LockEntry entry) { var locks = TabLocks.GetValueOrDefault(ctx.Index); if (locks == null || locks.Count == 0) { entry = CreateTabLock(ref ctx); return(DBResult.Success); } if (IsHeldTabLock(ref ctx, out entry)) { return(DBResult.Success); } if (IsOthersHeldOrWaitConflictTabLock(ctx.Transaction, ctx.Index, ctx.Flags)) { ctx.Flags |= LockFlags.Waiting; return(CreateTabLockForWait(ref ctx, out entry)); } entry = CreateTabLock(ref ctx); return(DBResult.Success); }
internal LockEntry FindPrevLockEntry(LockEntry entry) { return(entry?.GNode?.Previous?.Value); }
internal LockEntry FindNextLockEntry(LockEntry entry) { return(entry?.GNode?.Next?.Value); }
/// <summary> /// is other's transaction has held the table's lock and conflict with the ctx-lock /// </summary> /// <param name="ctx"></param> /// <returns></returns> private bool IsOthersHeldOrWaitConflictTabLock(Transaction tx, TableIndex index, LockFlags flags, LockEntry endEntry = null) { var locks = TabLocks.GetValueOrDefault(index); if (locks == null) { return(false); } for (var node = locks.First; node != null; node = node.Next) { var entry = node.Value; if (entry == endEntry) { break; } if (entry.Transaction == tx) { continue; } if (entry.IsExclusive || flags.IsExclusive()) { return(true); } } return(false); }
/// <summary> /// is other's transaction has held the recrod's lock and conflict with the ctx-lock /// </summary> /// <param name="ctx"></param> /// <returns></returns> private bool IsOthersHeldOrWaitConflictRecLock(Transaction tx, PagePosition page, int slot, LockFlags flags, LockEntry endEntry = null) { var locks = RecLocks.GetValueOrDefault(page); if (locks == null) { return(false); } for (var node = locks.First; node != null; node = node.Next) { var entry = node.Value; if (entry == endEntry) { break; } if (entry.Transaction == tx || entry.GetBit(slot) == 0) { continue; } if (entry.IsExclusive || flags.IsExclusive()) { return(true); } } return(false); }