private void threadSpin() { var lastPileCompact = DateTime.UtcNow; log(MessageType.Info, "threadSpin()", "Entering..."); try { var wasActive = App.Active; //remember whether app was active during start //this is needed so that CacheStore works without app container (using NOPApplication) in which case //service must be .Disposed() to stop this thread var timer = Stopwatch.StartNew(); while ((App.Active | !wasActive) && Running) { var utcNow = DateTime.UtcNow; var tc = m_Tables.Count; var maxTimePerTableMs = THREAD_MAX_TIME_FOR_SWEEP_ALL_MS / (tc != 0 ? tc : 1); if (maxTimePerTableMs < 2) { maxTimePerTableMs = 2; } foreach (var table in m_Tables) { if (!Running) { break; } try { var sweptToEnd = table.Sweep(timer, maxTimePerTableMs); if (m_InstrumentationEnabled) { Instrumentation.CacheTableSwept.Happened(Name); //for cache Instrumentation.CacheTableSwept.Happened(Name + "." + table.Name); //for table } if (sweptToEnd) { if (table.Count == 0) { if (!table.SweepWhenBecameEmpty.HasValue) { table.SweepWhenBecameEmpty = utcNow; } else if ((utcNow - table.SweepWhenBecameEmpty.Value).TotalSeconds > EMPTY_TABLE_LIFE_SEC) { m_Tables.Unregister(table);//can mutate m_Tables because foreach does a snapshot via GetEnumerator } } else { table.SweepWhenBecameEmpty = null; } } } catch (Exception error) { log(MessageType.Critical, "threadSpin().foreach.Sweep", "Leaked exception while sweeping table '{0}': {1}'" .Args(table.Name, error.ToMessageWithType()), error); } } try { dumpInstruments(); } catch (Exception error) { log(MessageType.Critical, "threadSpin().dumpInstruments", "Leaked exception dumping instrumentation: {0}'" .Args(error.ToMessageWithType()), error); } try { if ((utcNow - lastPileCompact).TotalSeconds > 5 * 60) { var freed = m_Pile.Compact(); lastPileCompact = utcNow; if (freed > 0) { log(MessageType.Info, "threadSpin().pile.compact()", "Freed {0:n0} bytes".Args(freed)); } } } catch (Exception error) { log(MessageType.Critical, "threadSpin().pile.compact()", "Leaked exception compacting pile: {0}'" .Args(error.ToMessageWithType()), error); } m_Trigger.WaitOne(THREAD_MIN_GRANULARITY_MS + ExternalRandomGenerator.Instance.NextScaledRandomInteger(0, THREAD_GRANULARITY_VARIANCE_MS)); } //while } catch (Exception e) { log(MessageType.Emergency, "threadSpin()", "Leaked exception, the tables are not swept anymore" + e.ToMessageWithType(), e); } log(MessageType.Info, "threadSpin()", "...Exiting"); }