/// <summary> /// Performs undo or redo /// </summary> /// <param name="storeSnapshot"></param> /// <param name="isUndo"></param> /// <returns></returns> private int Revert(StoreSnapshot storeSnapshot, TransactionType transactionType) { Debug.Assert(transactionType == TransactionType.Undo || transactionType == TransactionType.Redo); int originalVersion = storeSnapshot.Version; int transactionToRevert = 0; try { transactionToRevert = this.StartTransaction(storeSnapshot, transactionType); if (0 < transactionToRevert) { foreach (ISnapTable snapTable in this.Tables) { snapTable.Revert(transactionToRevert); } return(this.Commit(withOmit: false)); } } catch { if (0 < transactionToRevert && storeSnapshot.IsEditor) { this.Rollback(); } throw; } return(originalVersion); }
/// <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); }
public ChangeEnumerator(StoreSnapshot store, int oldVersion, int newVersion) { Debug.Assert(0 < oldVersion && oldVersion <= newVersion && newVersion <= store.SnapStore.CompletedVersion); this.enumerator = store.Tables.GetEnumerator(); this.oldVersion = oldVersion; this.newVersion = newVersion; }
/// <summary> /// Constructs the table /// </summary> /// <param name="store"></param> /// <param name="name"></param> /// <param name="fields"></param> public TableSnapshot(StoreSnapshot store, string name, params IField <TRecord>[] fields) { if (fields == null) { throw new ArgumentNullException(nameof(fields)); } this.StoreSnapshot = store ?? throw new ArgumentNullException(nameof(store)); this.table = new SnapTable <TRecord>(store.SnapStore, name, 0, fields, true); this.StoreSnapshot.Add(this, this.table); }
public StoreSnapshot(StoreSnapshot store) : this(store.SnapStore.CheckFrozen()) { foreach (ISnapTable snapTable in this.SnapStore.Tables) { if (snapTable.IsUserTable) { this.primaryKeyHolder.Add((IPrimaryKeyHolder)snapTable.CreateTableSnapshot(this)); } } }
/// <summary> /// Starts new transaction. If successful then returns: /// - number of the started transaction if it is Edit transaction /// - number of transaction to revert if it is Undo/Redo transaction /// If transaction was not started then returns 0. /// </summary> /// <param name="newEditor">StoreSnapshot that owns the transaction</param> /// <param name="transactionType">Edit, Undo or Redo transaction</param> /// <returns></returns> private int StartTransaction(StoreSnapshot newEditor, TransactionType transactionType) { if (newEditor == null) { throw new ArgumentNullException("newEditor"); } Debug.Assert(transactionType == TransactionType.Edit || transactionType == TransactionType.Undo || transactionType == TransactionType.Redo, "Wrong transaction type"); this.CheckFrozen(); int transaction = 0; // any value of oldEditor other then null means this thread does not own the transaction // null means this thread is exclusive owner of transaction StoreSnapshot oldEditor = newEditor; bool success = false; RuntimeHelpers.PrepareConstrainedRegions(); try { RuntimeHelpers.PrepareConstrainedRegions(); try {} finally { oldEditor = Interlocked.CompareExchange <StoreSnapshot>(ref this.editor, newEditor, null); } if (oldEditor == null) { Debug.Assert(this.editor == newEditor, "Expecting to be current editor"); Debug.Assert(this.editorThread == null, "Editor thread should be null here"); if (this.version.Count == int.MaxValue) { throw new InvalidOperationException(Properties.Resources.ErrorTooManyTransactions); } this.editorThread = Thread.CurrentThread; transaction = (transactionType == TransactionType.Edit) ? this.version.Count + 1 : this.RevertVersion(transactionType); if (0 < transaction) { this.version.PrepareAdd(); success = true; } } } finally { if (success) { this.version.FixedAdd(ref transactionType); } else if (oldEditor == null) { Debug.Assert(this.editor == newEditor, "newEditor expected to be current editor"); this.editorThread = null; // this should be the first assignment this.editor = null; } } return(transaction); }
/// <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> /// Copy constructor /// </summary> /// <param name="storeSnapshot"></param> /// <param name="table"></param> internal TableSnapshot(StoreSnapshot storeSnapshot, SnapTable <TRecord> table) { this.StoreSnapshot = storeSnapshot; this.table = table; this.StoreSnapshot.Add(this, this.table); }
/// <summary> /// Create TableSnapshot base on the TRecord. /// </summary> /// <param name="storeSnapshot"></param> /// <returns></returns> public ITableSnapshot CreateTableSnapshot(StoreSnapshot storeSnapshot) { Debug.Assert(this.IsUserTable); return(new TableSnapshot <TRecord>(storeSnapshot, this)); }
/// <summary> /// Starts new transaction, performs redo and commits. /// If transaction was started successfully return new version of the store, otherwise return version of editor. /// </summary> /// <param name="storeSnapshot"></param> /// <returns></returns> public int Redo(StoreSnapshot storeSnapshot) { return(this.Revert(storeSnapshot, TransactionType.Redo)); }
/// <summary> /// Starts new transaction /// </summary> /// <param name="storeSnapshot">Owner of the transaction</param> /// <returns>true if the transaction started, false if other transaction is in progress</returns> public bool StartTransaction(StoreSnapshot storeSnapshot) { return(0 < this.StartTransaction(storeSnapshot, TransactionType.Edit)); }