/// <summary> /// Shifts the gear and blocks until all operations in the old drive mode complete. /// If OnGearShift is not null it's launched in a new Task, wrapped in a try catch block, /// swallowing all potential exceptions. /// </summary> /// <param name="g">The new concurrent mode.</param> /// <param name="f">Guarantees the execution of f() within the lock scope, in case that other shifts are waiting.</param> /// <param name="timeout">In milliseconds, by default is -1, which is indefinitely.</param> /// <returns>The old gear.</returns> /// <exception cref="System.SynchronizationException">Code.SignalAwaitTimeout</exception> public TesseractGear Clutch(TesseractGear g, Action f = null, int timeout = -1) { // One call at a time lock (shiftLock) { int old = -1; if (Drive != g) { // There must be no other resets anywhere gearShift.Reset(); old = Interlocked.Exchange(ref i[DRIVE], (int)g); // Wait for all concurrent operations to finish if (Volatile.Read(ref i[CCOPS]) > 0) { if (!gearShift.WaitOne(timeout)) { throw new SynchronizationException(SynchronizationException.Code.SignalAwaitTimeout); } } if (OnGearShift != null) { Task.Run(() => { try { OnGearShift(g); } catch { } }); } } else { old = (int)g; } // Give a chance to at least one function to be executed, // in case there are competing shifts. if (f != null) { f(); } return((TesseractGear)old); } }
/// <summary> /// Expands or shrinks the virtual array to the number of SIDE tiles fitting the requested length. /// If the AppendIndex is greater that the new length, it's cut to length -1. /// If shrinking and counting, the number of not-null values (ItemsCount) is also updated. /// The Drive must be P when shrinking. /// </summary> /// <param name="length">The new length.</param> /// <param name="expand">The intent of the caller.</param> /// <exception cref="System.ArgumentOutOfRangeException">If length is negative</exception> /// <exception cref="System.InvalidOperationException"> /// If the Drive is not P when shrinking.</exception> public bool Resize(int length, bool expand) { Interlocked.Increment(ref i[CCOPS]); TesseractGear inDrive = Drive; try { var slots = Volatile.Read(ref i[SLOTS]); if (length < 0) { throw new ArgumentOutOfRangeException("length"); } if (length == slots) { return(false); } lock (commonLock) { var als = Volatile.Read(ref i[SLOTS]); if (length == als) { return(false); } if (length > als) { return(expand ? alloc(length, als) > als : false); } else { if (expand) { return(false); } if (inDrive != TesseractGear.P) { throw new InvalidOperationException("Wrong drive"); } var toSize = length > 0 ? length + SIDE : 0; var p = new TesseractPos(als); while (als > toSize) { p.D2--; if (p.D2 < 1) { blocks[p.D0][p.D1] = null; p.D1--; if (p.D1 < 1) { p.D0--; p.D1 = SIDE - 1; } p.D2 = SIDE - 1; } else { blocks[p.D0][p.D1][p.D2] = null; } als -= SIDE; } Volatile.Write(ref i[SLOTS], als); if (AppendIndex >= length) { Interlocked.Exchange(ref i[INDEX], length - 1); } if (CountNotNulls) { int notNull = 0; foreach (var c in NotNullItems()) { notNull++; } Interlocked.Exchange(ref i[NNCNT], notNull); } return(true); } } } finally { if (Interlocked.Decrement(ref i[CCOPS]) == 0 && inDrive != Drive) { gearShift.Set(); } } }