/// <summary> ///Commits current transaction. Optionally if <paramref name="withOmit"/> set to true erases transaction from undo history /// </summary> /// <param name="withOmit"></param> /// <returns></returns> public int Commit(bool withOmit) { this.ValidateModification(); int v = this.Version; RuntimeHelpers.PrepareConstrainedRegions(); try {} finally { if (withOmit) { ValueList <TransactionType> .Address address = this.version.ItemAddress(this.version.Count - 1); Debug.Assert(address.Page[address.Index] == TransactionType.Edit, "Only edit transactions can be omitted"); address.Page[address.Index] = TransactionType.Omit; } this.CompletedVersion = v; this.editorThread = null; this.editor = null; LockFreeSync.WriteBarrier(); } EventHandler handler = this.Committed; if (handler != null) { handler(this, EventArgs.Empty); } return(v); }
/// <summary> /// Shrinks list to new size. /// It is guarantee that space allocated by PrepareAdd will not be freed here. /// </summary> /// <param name="newSize"></param> public void Shrink(int newSize) { int oldSize = this.Count; if (newSize < oldSize) { this.Count = newSize; LockFreeSync.WriteBarrier(); for (int i = newSize >> LogPageSize; i < this.page.Length; i++) { TRow[] p = this.page[i]; if (p == null) { return; } for (int j = newSize & IndexOnPageMask; j < p.Length; j++) { if (oldSize <= newSize++) { return; } p[j] = default; } } } else if (oldSize < newSize) { throw new ArgumentOutOfRangeException(nameof(newSize)); } }
/// <summary> /// Gets record in the specified version /// </summary> /// <param name="rowId"></param> /// <param name="version"></param> /// <param name="data"></param> public void GetData(RowId rowId, int version, out TRecord data) { if (version != 0) { this.ValidateVersion(version); } int pointIndex = this.snap.Count - 1; ValueList <Snap> .Address snapAddress = this.snap.ItemAddress(pointIndex); while (version < snapAddress.Page[snapAddress.Index].Version) { snapAddress = this.snap.ItemAddress(--pointIndex); } if (!(0 <= rowId.Value && rowId.Value < snapAddress.Page[snapAddress.Index].TableSize)) { throw new ArgumentOutOfRangeException(nameof(rowId)); } // cache log size here. the original can only grow in time int logSize = snapAddress.Page[snapAddress.Index].LogSize; ValueList <Row> .Address rowAddress = this.table.ItemAddress(rowId.Value); if (rowAddress.Page[rowAddress.Index].LogIndex < logSize) { // so the latest version of data was requested. data = rowAddress.Page[rowAddress.Index].Data; bool isDeleted = rowAddress.Page[rowAddress.Index].IsDeleted; LockFreeSync.ReadBarrier(); // check if the row is still of the latest version if (rowAddress.Page[rowAddress.Index].LogIndex < logSize) { // if it is still latest return. if (isDeleted) { data = default; throw new ArgumentOutOfRangeException(nameof(rowId)); } return; } } // older version of the row requested. The data is in the log. Log is immutable so easy to read. ValueList <Log> .Address logAddress = this.log.ItemAddress(rowAddress.Page[rowAddress.Index].LogIndex); while (logSize <= logAddress.Page[logAddress.Index].LogIndex) { Debug.Assert(0 < logAddress.Page[logAddress.Index].LogIndex, "Log entry at 0 does not contain any real data and used as a stub"); logAddress = this.log.ItemAddress(logAddress.Page[logAddress.Index].LogIndex); } if (logAddress.Page[logAddress.Index].IsDeleted) { data = default; throw new ArgumentOutOfRangeException(nameof(rowId)); } data = logAddress.Page[logAddress.Index].Data; }
/// <summary> /// Adds new element assuming enough memory for it already exist. This method should be prepared by PrepareAdd call. /// </summary> /// <param name="row"></param> /// <returns></returns> public int FixedAdd(ref TRow row) { int index = this.Count; //if(index == int.MaxValue) { // throw new OverflowException(); //} int pageIndex = index >> LogPageSize; int itemIndex = index & IndexOnPageMask; this.page[pageIndex][itemIndex] = row; LockFreeSync.WriteBarrier(); this.Count++; return(index); }
/// <summary> /// Reverts current transaction /// </summary> public void Rollback() { this.ValidateModification(); RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { foreach (ISnapTable snapTable in this.Tables) { snapTable.Rollback(); } ValueList <TransactionType> .Address address = this.version.ItemAddress(this.version.Count - 1); address.Page[address.Index] = TransactionType.Omit; this.CompletedVersion = this.Version; this.editorThread = null; this.editor = null; LockFreeSync.WriteBarrier(); } }
/// <summary> /// Ensures memory is allocated to accommodate for one more element /// </summary> public void PrepareAdd() { int index = this.Count; if (int.MaxValue - 1 <= index) { throw new InvalidOperationException(Properties.Resources.ErrorValueListTooBig); } int pageIndex = index >> LogPageSize; if (pageIndex == this.page.Length) { TRow[][] p = this.page; Array.Resize <TRow[]>(ref p, p.Length * 2); LockFreeSync.WriteBarrier(); this.page = p; } int itemIndex = index & IndexOnPageMask; if (itemIndex == 0 && this.page[pageIndex] == null) { this.page[pageIndex] = new TRow[PageSize]; } }