public virtual TValue Next <TValue>([NotNull] Func <long> getNewLowValue) { Check.NotNull(getNewLowValue, nameof(getNewLowValue)); var poolIndexToUse = _poolIndex; _poolIndex = (_poolIndex + 1) % _pool.Length; var newValue = GetNextValue(poolIndexToUse); // If the chosen value is outside of the current block then we need a new block. // It is possible that other threads will use all of the new block before this thread // gets a chance to use the new new value, so use a while here to do it all again. while (newValue.Low >= newValue.High) { lock (_locks[_poolIndex]) { // Once inside the lock check to see if another thread already got a new block, in which // case just get a value out of the new block instead of requesting one. if (newValue.High == _pool[poolIndexToUse].High) { var newCurrent = getNewLowValue(); newValue = new HiLoValue(newCurrent, newCurrent + _blockSize); _pool[poolIndexToUse] = newValue; } else { newValue = GetNextValue(poolIndexToUse); } } } return((TValue)Convert.ChangeType(newValue.Low, typeof(TValue))); }
/// <summary> /// Gets a value to be assigned to a property. /// </summary> /// <typeparam name="TValue"> The type of values being generated. </typeparam> /// <param name="getNewLowValue"> /// A function to get the next low value if needed. /// </param> /// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param> /// <returns> The value to be assigned to a property. </returns> public virtual async Task <TValue> NextAsync <TValue>( [NotNull] Func <CancellationToken, Task <long> > getNewLowValue, CancellationToken cancellationToken = default) { Check.NotNull(getNewLowValue, nameof(getNewLowValue)); var newValue = GetNextValue(); // If the chosen value is outside of the current block then we need a new block. // It is possible that other threads will use all of the new block before this thread // gets a chance to use the new new value, so use a while here to do it all again. while (newValue.Low >= newValue.High) { using (await _asyncLock.LockAsync()) { // Once inside the lock check to see if another thread already got a new block, in which // case just get a value out of the new block instead of requesting one. if (newValue.High == _currentValue.High) { var newCurrent = await getNewLowValue(cancellationToken); newValue = new HiLoValue(newCurrent, newCurrent + _blockSize); _currentValue = newValue; } else { newValue = GetNextValue(); } } } return(ConvertResult <TValue>(newValue)); }
/// <summary> /// Gets a value to be assigned to a property. /// </summary> /// <typeparam name="TValue"> The type of values being generated. </typeparam> /// <param name="getNewLowValue"> /// A function to get the next low value if needed. /// </param> /// <returns> The value to be assigned to a property. </returns> public virtual TValue Next <TValue>([NotNull] Func <long> getNewLowValue) { Check.NotNull(getNewLowValue, nameof(getNewLowValue)); var newValue = GetNextValue(); // If the chosen value is outside of the current block then we need a new block. // It is possible that other threads will use all of the new block before this thread // gets a chance to use the new value, so use a while here to do it all again. while (newValue.Low >= newValue.High) { _semaphoreSlim.Wait(); try { // Once inside the lock check to see if another thread already got a new block, in which // case just get a value out of the new block instead of requesting one. if (newValue.High == _currentValue.High) { var newCurrent = getNewLowValue(); newValue = new HiLoValue(newCurrent, newCurrent + _blockSize); _currentValue = newValue; } else { newValue = GetNextValue(); } } finally { _semaphoreSlim.Release(); } } return(ConvertResult <TValue>(newValue)); }
/// <summary> /// Initializes a new instance of the <see cref="HiLoValueGeneratorState" /> class. /// </summary> /// <param name="blockSize"> /// The number of sequential values that can be used, starting from the low value, before /// a new low value must be fetched from the database. /// </param> public HiLoValueGeneratorState(int blockSize) { if (blockSize <= 0) { throw new ArgumentOutOfRangeException(nameof(blockSize), CoreStrings.HiLoBadBlockSize); } _blockSize = blockSize; _currentValue = new HiLoValue(-1, 0); }
public HiLoValueGeneratorState(int blockSize, int poolSize) { _blockSize = blockSize; _pool = new HiLoValue[poolSize]; _locks = new object[poolSize]; for (var i = 0; i < poolSize; i++) { _pool[i] = new HiLoValue(-1, 0); _locks[i] = new object(); } }
public HiLoValueGeneratorState(int blockSize, int poolSize) { if (blockSize <= 0) { throw new ArgumentOutOfRangeException(nameof(blockSize), Strings.HiLoBadBlockSize); } if (poolSize <= 0) { throw new ArgumentOutOfRangeException(nameof(poolSize), Strings.HiLoBadPoolSize); } _blockSize = blockSize; _pool = new HiLoValue[poolSize]; _locks = new object[poolSize]; for (var i = 0; i < poolSize; i++) { _pool[i] = new HiLoValue(-1, 0); _locks[i] = new object(); } }
private static TValue ConvertResult <TValue>(HiLoValue newValue) => (TValue)Convert.ChangeType(newValue.Low, typeof(TValue), CultureInfo.InvariantCulture);