/// <summary> /// Allocates a generic fragment with a specified length. /// </summary> /// <param name="size">The number of bytes to allocate.</param> /// <param name="tries"> /// The number of fails before switching to another lane. /// If 0 the HighwaySettings.LaneAllocTries value is used. /// </param> /// <param name="awaitMS">The awaitMS for each try</param> /// <returns>A new fragment.</returns> /// <exception cref="System.ArgumentOutOfRangeException"> /// If size is negative or greater than HighwaySettings.MAX_LANE_CAPACITY. /// </exception> /// <exception cref="System.MemoryLaneException"> /// Code.NotInitialized: when the lanes are not initialized. /// Code.NewLaneAllocFail: after an unsuccessful attempt to allocate a fragment in a dedicated new lane. /// One should never see this one! /// </exception> /// <exception cref="ObjectDisposedException">If the MemoryCarriage is disposed.</exception> public F Alloc(int size, int tries = 0, int awaitMS = 5) { if (isDisposed) { throw new ObjectDisposedException("MemoryCarriage"); } if (Lanes == null || Lanes.AllocatedSlots == 0) { throw new MemoryLaneException(MemoryLaneException.Code.NotInitialized); } if (size < 0 || size > HighwaySettings.MAX_LANE_CAPACITY) { throw new ArgumentOutOfRangeException("size"); } if (tries == 0) { tries = settings.LaneAllocTries; } F frag = null; var lanesCount = Lanes.ItemsCount; // Start from the oldest lane and cycle all lanes a few times before making a new lane for (var laps = 0; laps < settings.LapsBeforeNewLane; laps++) { for (var i = 0; i < lanesCount; i++) { var lane = Lanes[i]; if (lane != null && !lane.IsClosed && !lane.IsDisposed) { frag = createFragment(lane, size, tries, awaitMS); if (frag != null) { Volatile.Write(ref lastAllocTickAnyLane, DateTime.Now.Ticks); return(frag); } } } } // limits the concurrent lane allocations if (noluckGate.Wait(settings.NewLaneAllocationTimeoutMS)) { // Try again, there is at least one new lane if (lanesCount < Lanes.ItemsCount) { noluckGate.Release(); return(Alloc(size, tries, awaitMS)); } else { try { // [i] No luck, create a new lane and do not publish it before getting a fragment var ccMakers = settings.ConcurrentNewLaneAllocations - noluckGate.CurrentCount; var nextCapacity = settings.NextCapacity(Lanes.AppendIndex + ccMakers); var cap = size > nextCapacity ? size : nextCapacity; var ml = allocLane(cap, true); // Could be null if the MaxLanesCount or MaxBytesCount exceptions are ignored. // The consumer can infer that by checking if the fragment is null. if (ml != null) { frag = createFragment(ml, size, tries, awaitMS); if (frag == null) { throw new MemoryLaneException( MemoryLaneException.Code.NewLaneAllocFail, string.Format("Failed to allocate {0} bytes on a dedicated lane.", size)); } Lanes.Append(ml); Volatile.Write(ref lastAllocTickAnyLane, DateTime.Now.Ticks); } } finally { noluckGate.Release(); } } } return(frag); }