using System.Threading; namespace ZeroLevel.Services.Async { /// <summary> /// Allocates Ids for instances on demand. 0 is an invalid/unassigned Id. Ids may be non-unique in very long-running systems. This is similar to the Id system used by <see cref="System.Threading.Tasks.Task"/> and <see cref="System.Threading.Tasks.TaskScheduler"/>. /// </summary> /// <typeparam name="TTag">The type for which ids are generated.</typeparam> // ReSharper disable UnusedTypeParameter internal static class IdManager<TTag> // ReSharper restore UnusedTypeParameter { /// <summary> /// The last id generated for this type. This is 0 if no ids have been generated. /// </summary> // ReSharper disable StaticFieldInGenericType private static int _lastId; // ReSharper restore StaticFieldInGenericType /// <summary> /// Returns the id, allocating it if necessary. /// </summary> /// <param name="id">A reference to the field containing the id.</param> public static int GetId(ref int id) { // If the Id has already been assigned, just use it. if (id != 0) return id; // Determine the new Id without modifying "id", since other threads may also be determining the new Id at the same time. int newId; // The Increment is in a while loop to ensure we get a non-zero Id: // If we are incrementing -1, then we want to skip over 0. // If there are tons of Id allocations going on, we want to skip over 0 no matter how many times we get it. do { newId = Interlocked.Increment(ref _lastId); } while (newId == 0); // Update the Id unless another thread already updated it. Interlocked.CompareExchange(ref id, newId, 0); // Return the current Id, regardless of whether it's our new Id or a new Id from another thread. return id; } } }