public SliceOfCallbacks(InvokersHolder holder, Slice slice)
 {
     this.holder = holder;
     callbacks   = slice.Array;
     count       = slice.Count;
 }
Ejemplo n.º 2
0
 public abstract void Remove(InvokersHolder holder);
        private bool ConcurrentPurge()
        {
            const int    LowAfterMilliseconds    = 180 * 1000; // Trim after 60 seconds for low pressure.
            const int    MediumAfterMilliseconds = 90 * 1000;  // Trim after 60 seconds for medium pressure.
            const double HighPressureThreshold   = .90;        // Percent of GC memory pressure threshold we consider "high".
            const double MediumPressureThreshold = .70;        // Percent of GC memory pressure threshold we consider "medium".

            int currentMilliseconds = Environment.TickCount;

            for (int j = 0; j < PurgeAttempts; j++)
            {
                // The callback was called before finishing the previous purge.
                if ((state & IS_PURGING) != 0)
                {
                    return(true);
                }

                if (state == IS_DISPOSED_OR_DISPOSING)
                {
                    return(false);
                }

                MassiveWriteBegin();
                {
                    if (state == IS_DISPOSED_OR_DISPOSING)
                    {
                        WriteEnd();
                        return(false);
                    }

                    if (holdersCount == 0)
                    {
                        WriteEnd();
                        return(true);
                    }

                    int trimMilliseconds;

#if NET5_0_OR_GREATER
                    GCMemoryInfo memoryInfo = GC.GetGCMemoryInfo();

                    if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * HighPressureThreshold)
                    {
                        trimMilliseconds = 0;
                    }
                    else if (memoryInfo.MemoryLoadBytes >= memoryInfo.HighMemoryLoadThresholdBytes * MediumPressureThreshold)
                    {
                        trimMilliseconds = MediumAfterMilliseconds;
                    }
                    else
                    {
                        trimMilliseconds = LowAfterMilliseconds;
                    }
#else
                    trimMilliseconds = 0;
#endif

                    if (millisecondsTimeStamp == 0)
                    {
                        millisecondsTimeStamp = currentMilliseconds;
                    }

                    if ((currentMilliseconds - millisecondsTimeStamp) <= trimMilliseconds)
                    {
                        WriteEnd();
                        return(true);
                    }

                    state = IS_PURGING;

                    Debug.Assert(holders is not null, "Impossible state, since holders initialization happens at the same time AutoPurger is instantiated.");
                    if (holdersCount > 1)
                    {
                        purgingIndex += (int)(Parallel.For(0, holdersCount, options ??= new(), autoPurgeAction ??= new Action <int, ParallelLoopState>((index, loop) =>
                        {
                            if (loop.ShouldExitCurrentIteration)
                            {
                                return;
                            }

                            if ((state & IS_CANCELATION_REQUESTED) != 0)
                            {
                                loop.Stop();
                            }

                            Utils.ExpectAssignableType <InvokersHolder>(holders[(purgingIndex + index) % holdersCount]).Purge();
                        })).LowestBreakIteration ?? 0);
                    }
                    else if (holdersCount == 1)
                    {
                        Utils.ExpectAssignableType <InvokersHolder>(holders[0]).Purge();
                    }

                    bool isCancelationRequested = (state & IS_CANCELATION_REQUESTED) != 0;

                    int holdersCountOld        = holdersCount;
                    int holdersCount_          = holdersCountOld;
                    InvariantObject[] holders_ = holders;
                    // TODO: This loop could actually be done without locking the whole event manager,
                    // as long as the `holders` array is locked.
                    for (int i = 0; i < holdersCount_; i++)
                    {
                        InvokersHolder holder = Utils.ExpectAssignableType <InvokersHolder>(holders_[i].Value);
                        if (holder.RemoveIfEmpty())
                        {
                            holdersPerType.Remove(holder.GetType());
                            managersPerType[holder.GetEventType()].Remove(holder);

                            while (true)
                            {
                                if (--holdersCount_ == i)
                                {
                                    goto end;
                                }
                                holder = Utils.ExpectAssignableType <InvokersHolder>(holders_[holdersCount_].Value);
                                if (!holder.RemoveIfEmpty())
                                {
                                    break;
                                }
                                holdersPerType.Remove(holder.GetType());
                                managersPerType[holder.GetEventType()].Remove(holder);
                            }
                            holders_[i] = holders_[i + 1];
                        }
                    }
end:
                    if (!ArrayUtils.TryShrink(ref holders_, holdersCount_) && holdersCount_ != holdersCountOld)
                    {
                        Array.Clear(holders_, holdersCount_, holdersCountOld - holdersCount_);
                    }
                    holders      = holders_;
                    holdersCount = holdersCount_;

                    Lock(ref stateLock);
                    {
                        state = 0;
                    }
                    Unlock(ref stateLock);

                    WriteEnd();

                    if (isCancelationRequested && Thread.Yield())
                    {
                        continue;
                    }

                    return(true);
                }
            }

            Debug.Fail("Impossible state.");

            return(true);
        }