Exemplo n.º 1
0
        public void Run(ActionTrigger trigger, TriggerArgs args, int muteMod)
        {
            //perf.first();
            Action actionWrapper = () => {
                var o = trigger.options;

                var oldOpt = o.thread == TOThread.New ? default : opt.scope.all(inherit: false);
                             try {
                                 _MuteMod(ref muteMod);

                                 string sTrigger = null; long startTime = 0;
                                 //perf.next();
                                 if (script.role == SRole.MiniProgram)
                                 {
                                     sTrigger = trigger.ToString();
                                     Api.QueryPerformanceCounter(out startTime);
                                     print.TaskEvent_("AS " + sTrigger, startTime, trigger.sourceFile, trigger.sourceLine);
                                     //perf.next();
                                 }

                                 var baArgs = new TOBAArgs(args);        //struct
#if true
                                 o.before?.Invoke(baArgs);
#else
                                 if (o.before != null)
                                 {
                                     bool called = false;
                                     if (t_beforeCalled == null)
                                     {
                                         t_beforeCalled = new List <Action <bool> > {
                                             o.before
                                         }
                                     }
                                     ;
                                     else if (!t_beforeCalled.Contains(o.before))
                                     {
                                         t_beforeCalled.Add(o.before);
                                     }
                                     else
                                     {
                                         called = true;
                                     }
                                     o.before(!called);
                                 }
#endif
                                 try {
                                     //perf.nw();
                                     trigger.Run(args);

                                     if (sTrigger != null)
                                     {
                                         print.TaskEvent_("AE", startTime, trigger.sourceFile, trigger.sourceLine);
                                     }
                                 }
                                 catch (Exception e1) {
                                     if (sTrigger != null)
                                     {
                                         print.TaskEvent_("AF", startTime, trigger.sourceFile, trigger.sourceLine);
                                     }

                                     baArgs.Exception = e1;
                                     print.it(e1);
                                 }
                                 o.after?.Invoke(baArgs);
                             }
                             catch (Exception e2) {
                                 print.it(e2);
                             }
                             finally {
                                 oldOpt.Dispose();
                                 if (o.flags.Has(TOFlags.Single))
                                 {
                                     _d.TryRemove(trigger, out _);
                                 }
                                 if (o.thread != TOThread.Main)
                                 {
                                     toolbar.TriggerActionEndedInNonmainThread_();
                                 }
                             }
            };
            //never mind: we should not create actionWrapper if cannot run. But such cases are rare. Fast and small, about 64 bytes.

            var opt1     = trigger.options;
            int threadId = opt1.thread;

            if (threadId >= 0)               //dedicated thread
            {
                _Thread h = null; foreach (var v in _a)
                {
                    if (v.id == threadId)
                    {
                        h = v; break;
                    }
                }
                if (h == null)
                {
                    _a.Add(h = new _Thread(threadId));
                }
                if (h.RunAction(actionWrapper, trigger))
                {
                    return;
                }
            }
            else if (threadId == TOThread.Main)
            {
                actionWrapper();
                return;
                //note: can reenter. Probably it is better than to cancel if already running.
            }
            else
            {
                bool canRun = true;
                bool single = opt1.flags.Has(TOFlags.Single);
                if (single)
                {
                    _d ??= new();
                    if (_d.TryGetValue(trigger, out var tt))
                    {
                        switch (tt)
                        {
                        case Thread thread:
                            if (thread.IsAlive)
                            {
                                canRun = false;
                            }
                            break;

                        case Task task:
                            //print.it(task.Status);
                            switch (task.Status)
                            {
                            case TaskStatus.RanToCompletion:
                            case TaskStatus.Faulted:
                            case TaskStatus.Canceled: break;

                            default: canRun = false; break;
                            }
                            break;
                        }
                    }
                }

                if (canRun)
                {
                    if (threadId == TOThread.New)
                    {
                        var thread = new Thread(actionWrapper.Invoke)
                        {
                            IsBackground = true
                        };
                        //if (!opt1.flags.Has(TOFlags.MtaThread))
                        thread.SetApartmentState(ApartmentState.STA);
                        if (single)
                        {
                            _d[trigger] = thread;
                        }
                        try { thread.Start(); }
                        catch (OutOfMemoryException) {                         //too many threads, probably 32-bit process
                            if (single)
                            {
                                _d.TryRemove(trigger, out _);
                            }
                            _OutOfMemory();
                            //SHOULDDO: before starting thread, warn if there are too many action threads.
                            //	In 32-bit process normally fails at ~3000 threads.
                            //	Unlikely to fail in 64-bit process, but at ~15000 threads starts to hang temporarily, which causes hook timeout, slow mouse, other anomalies.
                        }
                    }
                    else                         //thread pool
                    {
                        var task = new Task(actionWrapper);
                        if (single)
                        {
                            _d[trigger] = task;
                        }
                        task.Start();
                    }
                    return;
                }
            }

            if (muteMod != 0)
            {
                ThreadPool.QueueUserWorkItem(_ => _MuteMod(ref muteMod));
            }
        }
Exemplo n.º 2
0
        public void Run(ActionTrigger trigger, TriggerArgs args, int muteMod)
        {
            Action actionWrapper = () => {
                var opt = trigger.options;
                try {
                    _MuteMod(ref muteMod);

                    string sTrigger = null;
                    if (ATask.Role == ATRole.MiniProgram)
                    {
                        Util.Log_.Run.Write($"Trigger action started. Trigger: {sTrigger = trigger.ToString()}");
                    }

                    AOpt.Reset();

                    var baArgs = new TOBAArgs(args);                     //struct
#if true
                    opt.before?.Invoke(baArgs);
#else
                    if (opt.before != null)
                    {
                        bool called = false;
                        if (t_beforeCalled == null)
                        {
                            t_beforeCalled = new List <Action <bool> > {
                                opt.before
                            }
                        }
                        ;
                        else if (!t_beforeCalled.Contains(opt.before))
                        {
                            t_beforeCalled.Add(opt.before);
                        }
                        else
                        {
                            called = true;
                        }
                        opt.before(!called);
                    }
#endif
                    try {
                        trigger.Run(args);

                        if (sTrigger != null)
                        {
                            Util.Log_.Run.Write($"Trigger action ended. Trigger: {sTrigger}");
                        }
                    }
                    catch (Exception e1) {
                        if (sTrigger != null)
                        {
                            Util.Log_.Run.Write($"Unhandled exception in trigger action. Trigger: {sTrigger}. Exception: {e1.ToStringWithoutStack()}");
                        }

                        baArgs.Exception = e1;
                        AOutput.Write(e1);
                    }
                    opt.after?.Invoke(baArgs);
                }
                catch (Exception e2) {
                    AOutput.Write(e2);
                }
                finally {
                    if (opt.thread < -1 && opt.ifRunning == 0)
                    {
                        _d.TryRemove(trigger, out _);
                    }
                }
            };
            //never mind: we should not create actionWrapper if cannot run. But such cases are rare. Fast and small, about 64 bytes.

            int threadId = trigger.options.thread;

            if (threadId >= 0)              //dedicated thread
            {
                _Thread h = null; foreach (var v in _a)
                {
                    if (v.id == threadId)
                    {
                        h = v; break;
                    }
                }
                if (h == null)
                {
                    _a.Add(h = new _Thread(threadId));
                }
                if (h.RunAction(actionWrapper, trigger))
                {
                    return;
                }
            }
            else if (threadId == -1)                //main thread
            {
                actionWrapper();
                return;
                //note: can reenter. Probably it is better than to cancel if already running.
            }
            else
            {
                bool canRun         = true;
                bool singleInstance = trigger.options.ifRunning == 0;
                if (singleInstance)
                {
                    _d ??= new System.Collections.Concurrent.ConcurrentDictionary <ActionTrigger, object>();
                    if (_d.TryGetValue(trigger, out var tt))
                    {
                        switch (tt)
                        {
                        case Thread thread:
                            if (thread.IsAlive)
                            {
                                canRun = false;
                            }
                            break;

                        case Task task:
                            //AOutput.Write(task.Status);
                            switch (task.Status)
                            {
                            case TaskStatus.RanToCompletion:
                            case TaskStatus.Faulted:
                            case TaskStatus.Canceled: break;

                            default: canRun = false; break;
                            }
                            break;
                        }
                    }
                }

                if (canRun)
                {
                    if (threadId == -2)                      //new thread
                    {
                        var thread = new Thread(actionWrapper.Invoke)
                        {
                            IsBackground = true
                        };
                        thread.SetApartmentState(ApartmentState.STA);
                        if (singleInstance)
                        {
                            _d[trigger] = thread;
                        }
                        try { thread.Start(); }
                        catch (OutOfMemoryException) {                        //too many threads, probably 32-bit process
                            if (singleInstance)
                            {
                                _d.TryRemove(trigger, out _);
                            }
                            _OutOfMemory();
                            //SHOULDDO: before starting thread, warn if there are too many action threads.
                            //	In 32-bit process normally fails at ~3000 threads.
                            //	Unlikely to fail in 64-bit process, but at ~15000 threads starts to hang temporarily, which causes hook timeout, slow mouse, other anomalies.
                        }
                    }
                    else                         //thread pool
                    {
                        var task = new Task(actionWrapper);
                        if (singleInstance)
                        {
                            _d[trigger] = task;
                        }
                        task.Start();
                    }
                    return;
                }
            }

            if (muteMod != 0)
            {
                ThreadPool.QueueUserWorkItem(_ => _MuteMod(ref muteMod));
            }
        }