public void Clear()
        {
            try                     { ScriptRunInfo = new ConcurrentDictionary <string, ScriptInfo>(ScriptRunInfo.Where(S => S.Value.RunningInstances > 0).ToArray()); }
            catch (Exception error) { ScriptRunInfo.Clear(); Log($"EmpyrionScripting Mod: Clear => {error}", LogLevel.Error); }

            WaitForExec.Clear();
            ExecQueue = new ConcurrentQueue <IScriptRootData>();
        }
 public void Add(IScriptRootData data)
 {
     if (WaitForExec.TryAdd(data.ScriptId, data))
     {
         ExecQueue.Enqueue(data);
     }
     else
     {
         lock (ExecQueue) WaitForExec.AddOrUpdate(data.ScriptId, data, (i, d) => data);
     }
 }
        private void ExecScript(object state)
        {
            if (!(state is IScriptRootData data))
            {
                return;
            }

            try
            {
                if (data.E.EntityType == EntityType.Proxy)
                {
                    WaitForExec.TryRemove(data.ScriptId, out _);
                    return;
                }

                if (!ScriptRunInfo.TryGetValue(data.ScriptId, out var info))
                {
                    info = new ScriptInfo();
                }

                info.LastStart = DateTime.Now;
                info.Count++;

                Interlocked.Increment(ref info.RunningInstances);
                processScript(data);
                Interlocked.Decrement(ref info.RunningInstances);

                lock (ExecQueue) WaitForExec.TryRemove(data.ScriptId, out _);

                info.ExecTime += DateTime.Now - info.LastStart;

                ScriptRunInfo.AddOrUpdate(data.ScriptId, info, (id, i) => info);

                Interlocked.Increment(ref _MainCount);
                if (MainCount > ScriptsCount)
                {
                    if (Interlocked.Exchange(ref _MainCount, 0) > 0 && (DateTime.Now - LastIterationUpdate).TotalSeconds >= 1)
                    {
                        LastIterationUpdate = DateTime.Now;
                        Interlocked.Increment(ref _Iteration);
                    }
                }
            }
            catch (Exception error)
            {
                Log($"EmpyrionScripting Mod: ExecNext {data.ScriptId} => {error}", LogLevel.Debug);
            }
        }
 public void CheckForEmergencyRestart(PlayfieldScriptData playfield)
 {
     lock (ExecQueue)
     {
         if (ExecQueue.IsEmpty && WaitForExec.Count > 0 && BackgroundWorkerToDo.Count == 0)
         {
             Log($"ExecQueue.IsEmpty restart [{playfield.PlayfieldName}]... #{WaitForExec.Count} => {WaitForExec.Aggregate("Pendingscripts:", (E, L) => E + "\n" + L.Key)}", LogLevel.Message);
             ThreadPool.GetAvailableThreads(out var availableWorkerThreads, out var availableCompletionPortThreads);
             Log($"ThreadPool available: WorkerThreads:{availableWorkerThreads} CompletionPortThreads:{availableCompletionPortThreads}", LogLevel.Message);
             ThreadPool.GetMaxThreads(out var maxWorkerThreads, out var maxCompletionPortThreads);
             Log($"ThreadPool max: WorkerThreads:{maxWorkerThreads} CompletionPortThreads:{maxCompletionPortThreads}", LogLevel.Message);
             ThreadPool.GetMinThreads(out var minWorkerThreads, out var minCompletionPortThreads);
             Log($"ThreadPool min: WorkerThreads:{minWorkerThreads} CompletionPortThreads:{minCompletionPortThreads}", LogLevel.Message);
             WaitForExec.Clear(); // robust error restart with fresh data
         }
     }
 }
        public void Add(IScriptRootData data)
        {
            if (ScriptNeedsMainThread.TryGetValue(data.ScriptId, out var needMainThread))
            {
                data.ScriptNeedsMainThread = needMainThread;
            }
            else
            {
                ScriptNeedsMainThread.TryAdd(data.ScriptId, false);
            }

            if (WaitForExec.TryAdd(data.ScriptId, data))
            {
                ExecQueue.Enqueue(data.ScriptId);
            }
            else
            {
                WaitForExec.AddOrUpdate(data.ScriptId, data, (scriptId, oldData) => data);
            }
        }
        public void ExecNext(int maxCount, int scriptsSyncExecution, ref int syncExecCount, Stopwatch scriptLoopTimeLimiter)
        {
            if (BackgroundWorkerToDo.Count > maxCount / 2)
            {
                return;
            }

            var scriptLoopTimeLimiterStopwatch = scriptLoopTimeLimiter;
            var timeLimitBackgroundReached     = new Func <bool>(() => scriptLoopTimeLimiterStopwatch.ElapsedMilliseconds > EmpyrionScripting.Configuration.Current.ScriptLoopBackgroundTimeLimiterMS);

            TimeLimitSyncReached = new Func <bool>(() => scriptLoopTimeLimiterStopwatch.ElapsedMilliseconds > EmpyrionScripting.Configuration.Current.ScriptLoopSyncTimeLimiterMS);

            Log($"ExecNext: {WaitForExec.Count} -> {ExecQueue.Count}", LogLevel.Debug);

            for (int i = maxCount - 1; i >= 0; i--)
            {
                if (ExecQueue.TryDequeue(out var scriptId) && WaitForExec.TryGetValue(scriptId, out var data))
                {
                    if (data.ScriptNeedsMainThread)
                    {
                        Interlocked.Increment(ref syncExecCount);

                        if (syncExecCount > scriptsSyncExecution)
                        {
                            ExecQueue.Enqueue(scriptId);
                        }
                        else
                        {
                            ((ScriptRootData)data).ScriptLoopTimeLimitReached = TimeLimitSyncReached;
                            ExecNext(data);
                        }
                    }
                    else
                    {
                        ((ScriptRootData)data).ScriptLoopTimeLimitReached = timeLimitBackgroundReached;
                        ExecNext(data);
                    }
                }