<commit_before>﻿// Copyright (c) 2007-2017 ppy Pty Ltd <<EMAIL>>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.Statistics;
using osu.Framework.Threading;
using OpenTK;
using System.Threading.Tasks;
using osu.Framework.Caching;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using OpenTK.Input;
using OpenTK.Graphics;

namespace osu.Framework.Platform
{
    public abstract class GameHost : IIpcHost, IDisposable
    {
        public static GameHost Instance;

        public GameWindow Window;

        private FrameworkDebugConfigManager debugConfig;

        private FrameworkConfigManager config;

        private void setActive(bool isActive)
        {
            threads.ForEach(t => t.IsActive = isActive);

            setLatencyMode();

            if (isActive)
                Activated?.Invoke();
            else
                Deactivated?.Invoke();
        }

        private void setLatencyMode() => GCSettings.LatencyMode = IsActive ? activeGCMode : GCLatencyMode.Interactive;

        public bool IsActive => InputThread.IsActive;

        public bool IsPrimaryInstance { get; protected set; } = true;

        public event Action Activated;
        public event Action Deactivated;
        public event Func<bool> Exiting;
        public event Action Exited;

        public event Action<Exception> ExceptionThrown;

        public event Action<IpcMessage> MessageReceived;

        protected void OnMessageReceived(IpcMessage message) => MessageReceived?.Invoke(message);

        public virtual Task SendMessageAsync(IpcMessage message)
        {
            throw new NotImplementedException("This platform does not implement IPC.");
        }

        public virtual Clipboard GetClipboard() => null;

        public virtual Storage Storage { get; protected set; }

        private readonly List<GameThread> threads;

        public IEnumerable<GameThread> Threads => threads;

        public void RegisterThread(GameThread t)
        {
            threads.Add(t);
        }

        public GameThread DrawThread;
        public GameThread UpdateThread;
        public InputThread InputThread;

        private double maximumUpdateHz;

        public double MaximumUpdateHz
        {
            get
            {
                return maximumUpdateHz;
            }

            set
            {
                UpdateThread.ActiveHz = maximumUpdateHz = value;
            }
        }

        private double maximumDrawHz;

        public double MaximumDrawHz
        {
            get
            {
                return maximumDrawHz;
            }

            set
            {
                DrawThread.ActiveHz = maximumDrawHz = value;
            }
        }

        public double MaximumInactiveHz
        {
            get
            {
                return DrawThread.InactiveHz;
            }

            set
            {
                DrawThread.InactiveHz = value;
                UpdateThread.InactiveHz = value;
            }
        }

        private PerformanceMonitor inputMonitor => InputThread.Monitor;
        private PerformanceMonitor drawMonitor => DrawThread.Monitor;

        private Cached<string> fullPathBacking = new Cached<string>();
        public string FullPath => fullPathBacking.EnsureValid() ? fullPathBacking.Value : fullPathBacking.Refresh(() =>
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            UriBuilder uri = new UriBuilder(codeBase);
            return Uri.UnescapeDataString(uri.Path);
        });

        protected string Name { get; }

        public DependencyContainer Dependencies { get; } = new DependencyContainer();

        protected GameHost(string gameName = @"")
        {
            Instance = this;

            AppDomain.CurrentDomain.UnhandledException += exceptionHandler;

            Dependencies.Cache(this);
            Name = gameName;

            threads = new List<GameThread>
            {
                (DrawThread = new GameThread(DrawFrame, @"Draw")
                {
                    OnThreadStart = DrawInitialize,
                }),
                (UpdateThread = new GameThread(UpdateFrame, @"Update")
                {
                    OnThreadStart = UpdateInitialize,
                    Monitor = { HandleGC = true }
                }),
                (InputThread = new InputThread(null, @"Input")) //never gets started.
            };

            MaximumUpdateHz = GameThread.DEFAULT_ACTIVE_HZ;
            MaximumDrawHz = (DisplayDevice.Default?.RefreshRate ?? 0) * 4;

            var path = System.IO.Path.GetDirectoryName(FullPath);
            if (path != null)
                Environment.CurrentDirectory = path;
        }

        private void exceptionHandler(object sender, UnhandledExceptionEventArgs e)
        {
            var exception = (Exception)e.ExceptionObject;

            if (ExceptionThrown != null)
                ExceptionThrown.Invoke(exception);
            else
            {
                AppDomain.CurrentDomain.UnhandledException -= exceptionHandler;
                throw exception;
            }
        }<commit_msg>Fix unhandled exceptions not being fired on the main thread.<commit_after>﻿// Copyright (c) 2007-2017 ppy Pty Ltd <<EMAIL>>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Runtime.ExceptionServices;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.Statistics;
using osu.Framework.Threading;
using OpenTK;
using System.Threading.Tasks;
using osu.Framework.Caching;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using OpenTK.Input;
using OpenTK.Graphics;

namespace osu.Framework.Platform
{
    public abstract class GameHost : IIpcHost, IDisposable
    {
        public static GameHost Instance;

        public GameWindow Window;

        private FrameworkDebugConfigManager debugConfig;

        private FrameworkConfigManager config;

        private void setActive(bool isActive)
        {
            threads.ForEach(t => t.IsActive = isActive);

            setLatencyMode();

            if (isActive)
                Activated?.Invoke();
            else
                Deactivated?.Invoke();
        }

        private void setLatencyMode() => GCSettings.LatencyMode = IsActive ? activeGCMode : GCLatencyMode.Interactive;

        public bool IsActive => InputThread.IsActive;

        public bool IsPrimaryInstance { get; protected set; } = true;

        public event Action Activated;
        public event Action Deactivated;
        public event Func<bool> Exiting;
        public event Action Exited;

        /// <summary>
        /// An unhandled exception was thrown. Return true to ignore and continue running.
        /// </summary>
        public event Func<Exception, bool> ExceptionThrown;

        public event Action<IpcMessage> MessageReceived;

        protected void OnMessageReceived(IpcMessage message) => MessageReceived?.Invoke(message);

        public virtual Task SendMessageAsync(IpcMessage message)
        {
            throw new NotImplementedException("This platform does not implement IPC.");
        }

        public virtual Clipboard GetClipboard() => null;

        public virtual Storage Storage { get; protected set; }

        private readonly List<GameThread> threads;

        public IEnumerable<GameThread> Threads => threads;

        public void RegisterThread(GameThread t)
        {
            threads.Add(t);
        }

        public GameThread DrawThread;
        public GameThread UpdateThread;
        public InputThread InputThread;

        private double maximumUpdateHz;

        public double MaximumUpdateHz
        {
            get
            {
                return maximumUpdateHz;
            }

            set
            {
                UpdateThread.ActiveHz = maximumUpdateHz = value;
            }
        }

        private double maximumDrawHz;

        public double MaximumDrawHz
        {
            get
            {
                return maximumDrawHz;
            }

            set
            {
                DrawThread.ActiveHz = maximumDrawHz = value;
            }
        }

        public double MaximumInactiveHz
        {
            get
            {
                return DrawThread.InactiveHz;
            }

            set
            {
                DrawThread.InactiveHz = value;
                UpdateThread.InactiveHz = value;
            }
        }

        private PerformanceMonitor inputMonitor => InputThread.Monitor;
        private PerformanceMonitor drawMonitor => DrawThread.Monitor;

        private Cached<string> fullPathBacking = new Cached<string>();
        public string FullPath => fullPathBacking.EnsureValid() ? fullPathBacking.Value : fullPathBacking.Refresh(() =>
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            UriBuilder uri = new UriBuilder(codeBase);
            return Uri.UnescapeDataString(uri.Path);
        });

        protected string Name { get; }

        public DependencyContainer Dependencies { get; } = new DependencyContainer();

        protected GameHost(string gameName = @"")
        {
            Instance = this;

            AppDomain.CurrentDomain.UnhandledException += exceptionHandler;

            Dependencies.Cache(this);
            Name = gameName;

            threads = new List<GameThread>
            {
                (DrawThread = new GameThread(DrawFrame, @"Draw")
                {
                    OnThreadStart = DrawInitialize,
                }),
                (UpdateThread = new GameThread(UpdateFrame, @"Update")
                {
                    OnThreadStart = UpdateInitialize,
                    Monitor = { HandleGC = true }
                }),
                (InputThread = new InputThread(null, @"Input")) //never gets started.
            };

            MaximumUpdateHz = GameThread.DEFAULT_ACTIVE_HZ;
            MaximumDrawHz = (DisplayDevice.Default?.RefreshRate ?? 0) * 4;

            var path = System.IO.Path.GetDirectoryName(FullPath);
            if (path != null)
                Environment.CurrentDirectory = path;
        }

        private void exceptionHandler(object sender, UnhandledExceptionEventArgs e)
        {
            var exception = (Exception)e.ExceptionObject;

            var exInfo = ExceptionDispatchInfo.Capture(exception);

            if (ExceptionThrown?.Invoke(exception) != true)
            {
                AppDomain.CurrentDomain.UnhandledException -= exceptionHandler;

                //we want to throw this exception on the input thread to interrupt window and also headless execution.
                InputThread.Scheduler.Add(() => { exInfo.Throw(); });
            }
        }
{'fraction_non_alphanumeric': 0.076312839527464, 'fraction_numerical': 0.0017245839441234802, 'mean_word_length': 1.958673469387755, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 46, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4606685', 'n_tokens_mistral': 2898, 'n_tokens_neox': 2722, 'n_words': 696}