/// <summary> /// local helper method to initialize the value /// </summary> /// <returns>The inititialized T value</returns> private T LazyInitValue() { Boxed boxed = null; LazyThreadSafetyMode mode = Mode; if (mode == LazyThreadSafetyMode.None) { boxed = CreateValue(); m_boxed = boxed; } else if (mode == LazyThreadSafetyMode.PublicationOnly) { boxed = CreateValue(); if (boxed == null || Interlocked.CompareExchange(ref m_boxed, boxed, null) != null) { // If CreateValue returns null, it means another thread successfully invoked the value factory // and stored the result, so we should just take what was stored. If CreateValue returns non-null // but another thread set the value we should just take what was stored. boxed = (Boxed)m_boxed; } else { // We successfully created and stored the value. At this point, the value factory delegate is // no longer needed, and we don't want to hold onto its resources. m_valueFactory = ALREADY_INVOKED_SENTINEL; } } else { object threadSafeObj = Volatile.Read(ref m_threadSafeObj); bool lockTaken = false; try { if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL) { Monitor.Enter(threadSafeObj, ref lockTaken); } else { Contract.Assert(m_boxed != null); } if (m_boxed == null) { boxed = CreateValue(); m_boxed = boxed; Volatile.Write(ref m_threadSafeObj, ALREADY_INVOKED_SENTINEL); } else // got the lock but the value is not null anymore, check if it is created by another thread or faulted and throw if so { boxed = m_boxed as Boxed; if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder { LazyInternalExceptionHolder exHolder = m_boxed as LazyInternalExceptionHolder; Contract.Assert(exHolder != null); exHolder.m_edi.Throw(); } } } finally { if (lockTaken) { Monitor.Exit(threadSafeObj); } } } Contract.Assert(boxed != null); return(boxed.m_value); }
/// <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); }