/// <summary> /// reuse the current transaction's created rec-lock /// </summary> /// <param name="ctx"></param> /// <returns></returns> private LockEntry GetCanReuseRecLock(ref LockContext ctx) { var entry = default(LockEntry); var locks = RecLocks.GetValueOrDefault(ctx.Page); if (locks == null) { return(null); } for (var node = locks.First; node != null; node = node.Next) { var lockEntry = node.Value; if (lockEntry.Transaction == ctx.Transaction && lockEntry.IsExclusive == ctx.Flags.IsExclusive()) { entry = lockEntry; break; } } if (entry != null) { entry.SetBit(ctx.RecordIndex, 1); } return(entry); }
/// <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> /// 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> /// 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> /// lock a table /// </summary> /// <param name="ctx"></param> /// <returns></returns> public DBResult Lock(ref LockContext ctx) { lock (SyncRoot) { return(ctx.Flags.IsTable() ? LockTab(ref ctx, out var _) : LockRec(ref ctx, out var _)); } }
/// <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 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); }