示例#1
0
        private static unsafe void OnRuntimeChanged()
        {
            if (SharedRuntimeState.IsActiveRuntime)
            {
                if (!File.Exists(mainAssemblyPath))
                {
                    SharedRuntimeState.SetHotReloadData(null);

                    // Cancel the runtime swap
                    SharedRuntimeState.Instance->NextRuntime             = EDotNetRuntime.None;
                    SharedRuntimeState.Instance->IsActiveRuntimeComplete = 0;
                    return;
                }

                if (!mainContextRef.IsInvalid)
                {
                    UnloadMainContext();
                }
                Debug.Assert(mainContextRef.IsInvalid, "UnloadMainContext failed?");
                Debug.Assert(!LoadAssemblyWithoutContexts, "Assembly context loading is required in order to swap runtimes");

                SharedRuntimeState.Instance->IsActiveRuntimeComplete = 1;
            }
            else if (SharedRuntimeState.Instance->NextRuntime == SharedRuntimeState.CurrentRuntime)
            {
                SharedRuntimeState.Instance->ActiveRuntime           = SharedRuntimeState.CurrentRuntime;
                SharedRuntimeState.Instance->NextRuntime             = EDotNetRuntime.None;
                SharedRuntimeState.Instance->IsActiveRuntimeComplete = 0;
                ReloadMainContext();
            }
        }
示例#2
0
        private static void UpdateAssemblyWatchers()
        {
            string[] assemblyPaths = SharedRuntimeState.GetHotReloadAssemblyPaths();
            if (assemblyPaths != null)
            {
                HashSet <string> newAssemblyPaths     = new HashSet <string>();
                HashSet <string> removedAssemblyPaths = new HashSet <string>();

                foreach (string assemblyPath in assemblyWatchers.Keys)
                {
                    if (!assemblyPaths.Contains(assemblyPath))
                    {
                        removedAssemblyPaths.Add(assemblyPath);
                    }
                }

                foreach (string assemblyPath in assemblyPaths)
                {
                    if (!assemblyWatchers.ContainsKey(assemblyPath))
                    {
                        newAssemblyPaths.Add(assemblyPath);
                    }
                }

                foreach (string assemblyPath in removedAssemblyPaths)
                {
                    assemblyWatchers[assemblyPath].Dispose();
                    assemblyWatchers.Remove(assemblyPath);
                }

                foreach (string assemblyPath in newAssemblyPaths)
                {
                    if (Directory.Exists(Path.GetDirectoryName(assemblyPath)))
                    {
                        FileSystemWatcher assemblyWatcher = new FileSystemWatcher();
                        assemblyWatcher.Path                = Path.GetDirectoryName(assemblyPath);
                        assemblyWatcher.Filter              = Path.GetFileName(assemblyPath);
                        assemblyWatcher.NotifyFilter        = NotifyFilters.LastWrite;//NotifyFilters.CreationTime;
                        assemblyWatcher.EnableRaisingEvents = true;
                        assemblyWatcher.Changed            += AssemblyWatcher_Changed;

                        assemblyWatchers.Add(assemblyPath, assemblyWatcher);
                    }
                }
            }

            if (assemblyWatchers.Count == 0)
            {
                SharedRuntimeState.LogWarning("No assembly watchers active for hotreload (\"USharpRuntime reload\" command can be used instead)");
            }
        }
示例#3
0
 private static bool LoadWithoutUsingContexts()
 {
     try
     {
         AssemblyLoader loader = new AssemblyLoader(
             mainAssemblyPath, entryPointType, entryPointMethod, entryPointArg, false, Runtime.AssemblyContextRef.Invalid);
         loader.Load();
         return(true);
     }
     catch (Exception e)
     {
         MessageBox("Failed to load assembly \"" + mainAssemblyPath + "\" " +
                    Environment.NewLine + Environment.NewLine + e, errorMsgBoxTitle);
         SharedRuntimeState.SetHotReloadData(null);
         return(false);
     }
 }
示例#4
0
        private static unsafe void OnRuntimeChanged()
        {
            if (SharedRuntimeState.IsActiveRuntime)
            {
                if (!File.Exists(mainAssemblyPath))
                {
                    SharedRuntimeState.SetHotReloadData(null);                    

                    // Cancel the runtime swap
                    SharedRuntimeState.Instance->NextRuntime = EDotNetRuntime.None;
                    SharedRuntimeState.Instance->IsActiveRuntimeComplete = 0;
                    SharedRuntimeState.Instance->Reload = false;
                    return;
                }

                if (SharedRuntimeState.Instance->Reload)
                {
                    // This is a reload as opposed to a runtime swap, reload it now and return
                    SharedRuntimeState.Instance->Reload = false;

                    // Only reload if we are using assembly contexts (AppDomain / AssemblyLoadContext)
                    if (!LoadAssemblyWithoutContexts)
                    {
                        ReloadMainContext();
                    }
                    return;
                }

                if (!mainContextRef.IsInvalid)
                {
                    UnloadMainContext();
                }
                Debug.Assert(mainContextRef.IsInvalid, "UnloadMainContext failed?");
                Debug.Assert(!LoadAssemblyWithoutContexts, "Assembly context loading is required in order to swap runtimes");
                
                SharedRuntimeState.Instance->IsActiveRuntimeComplete = 1;
            }
            else if (SharedRuntimeState.Instance->NextRuntime == SharedRuntimeState.CurrentRuntime)
            {
                SharedRuntimeState.Instance->ActiveRuntime = SharedRuntimeState.CurrentRuntime;
                SharedRuntimeState.Instance->NextRuntime = EDotNetRuntime.None;
                SharedRuntimeState.Instance->IsActiveRuntimeComplete = 0;
                ReloadMainContext();
            }
        }
示例#5
0
        public static void Unload()
        {
            DateTime beginUnloadTime = DateTime.Now;

            FMessage.Log("BeginUnload: " + beginUnloadTime.TimeOfDay);

            HotReload.OnUnload();

            HotReload.Data.BeginUnloadTime = beginUnloadTime;
            byte[] data = HotReload.Data.Save();
            HotReload.Data.Close();

            SharedRuntimeState.SetHotReloadData(data);

            TimeSpan endUnloadTime = DateTime.Now.TimeOfDay;

            FMessage.Log("EndUnload: " + endUnloadTime + " (" + (endUnloadTime - beginUnloadTime.TimeOfDay) + ")");
        }
示例#6
0
        public static int DllMain(string arg)
        {
            try
            {
                Args args = new Args(arg);

                if (!SharedRuntimeState.Initialized)
                {
                    SharedRuntimeState.Initialize((IntPtr)args.GetInt64("RuntimeState"));

                    AssemblyContextRef currentContext;
                    AssemblyContextRef.TryParse(args.GetString("AssemblyContext"), out currentContext);
                    AssemblyContext.Initialize(currentContext);
                    CurrentAssemblyContext.Initialize(currentContext);
                }

                if (args.GetBool("Preloading"))
                {
                    Preloading = true;
                    IntPtr address = (IntPtr)args.GetInt64("RegisterFuncs");
                    if (address != IntPtr.Zero)
                    {
                        NativeFunctions.RegisterFunctions(address);
                        Preloaded = true;
                    }
                    Preloading = false;
                    return(0);
                }
                else
                {
                    unsafe
                    {
                        SharedRuntimeState.Instance->ActiveRuntime = SharedRuntimeState.CurrentRuntime;
                    }

                    DateTime beginUnload = default(DateTime);
                    TimeSpan beginReload = DateTime.Now.TimeOfDay;

                    bool isReloading = false;
                    using (var timing = HotReload.Timing.Create(HotReload.Timing.TotalLoadTime))
                    {
                        using (var subTiming = HotReload.Timing.Create(HotReload.Timing.DataStore_Load))
                        {
                            // If this is a hot-reload then set up the data store
                            HotReload.Data = HotReload.DataStore.Load(SharedRuntimeState.GetHotReloadData());
                            beginUnload    = HotReload.Data.BeginUnloadTime;
                        }

                        HotReload.IsReloading = args.GetBool("Reloading");
                        isReloading           = HotReload.IsReloading;

                        IntPtr address = (IntPtr)args.GetInt64("RegisterFuncs");
                        if (address != IntPtr.Zero)
                        {
                            NativeFunctions.RegisterFunctions(address);
                        }
                    }

                    SharedRuntimeState.SetHotReloadAssemblyPaths(HotReloadAssemblyPaths);

                    TimeSpan endTime = DateTime.Now.TimeOfDay;
                    FMessage.Log("BeginReload: " + beginReload + " (BeginUnload-BeginReload: " + (beginReload - beginUnload.TimeOfDay) + ")");
                    FMessage.Log("EndReload: " + endTime + " (BeginUnload-EndReload: " + (endTime - beginUnload.TimeOfDay) + ")");
                    HotReload.Timing.Print(isReloading);
                    HotReload.Timing.PrintAll();
                    return(0);
                }
            }
            catch (Exception e)
            {
                string exceptionStr = "Entry point exception (UnrealEngine.Runtime): " + e;
                if (SharedRuntimeState.Initialized)
                {
                    SharedRuntimeState.LogError(exceptionStr);
                    SharedRuntimeState.MessageBox(exceptionStr, "Error");
                }
                return(1005);// AssemblyLoaderError.Exception
            }
        }
示例#7
0
 private static unsafe void MessageBox(string text, string title)
 {
     SharedRuntimeState.MessageBox(text, title);
 }
示例#8
0
        public static int DllMain(string arg)
        {
            try
            {
                Args args = new Args(arg);

                SharedRuntimeState.Initialize((IntPtr)args.GetInt64("RuntimeState"));
                Runtime.AssemblyContext.Initialize();

                mainAssemblyPath = args.GetString("MainAssembly");
                if (!string.IsNullOrEmpty(mainAssemblyPath))
                {
                    if (string.IsNullOrEmpty(mainAssemblyPath) || !File.Exists(mainAssemblyPath))
                    {
                        return((int)AssemblyLoaderError.MainAssemblyNotFound);
                    }
                    mainAssemblyDirectory = Path.GetDirectoryName(mainAssemblyPath);
                }
                else
                {
                    return((int)AssemblyLoaderError.MainAssemblyPathNotProvided);
                }

                IntPtr addTickerAddr      = (IntPtr)args.GetInt64("AddTicker");
                IntPtr isInGameThreadAddr = (IntPtr)args.GetInt64("IsInGameThread");
                if (addTickerAddr == IntPtr.Zero || isInGameThreadAddr == IntPtr.Zero)
                {
                    return((int)AssemblyLoaderError.GameThreadHelpersNull);
                }
                GameThreadHelper.Init(addTickerAddr, isInGameThreadAddr, OnRuntimeChanged);

                Debug.Assert(GameThreadHelper.IsInGameThread());

                entryPointArg = arg;

                string currentAssemblyPath     = Assembly.GetExecutingAssembly().Location;
                string currentAssemblyFileName = Path.GetFileNameWithoutExtension(currentAssemblyPath);
                currentAssemblyDirectory = Path.GetDirectoryName(currentAssemblyPath);

                if (!IsSameOrSubDirectory(currentAssemblyDirectory, mainAssemblyDirectory))
                {
                    return((int)AssemblyLoaderError.MainAssemblyPathNotProvided);
                }

                // If there is already a loaded runtime only do a pre-load
                if (SharedRuntimeState.GetLoadedRuntimes() != EDotNetRuntime.None)
                {
                    Debug.Assert(mainContextRef.IsInvalid);

                    // Make sure the main assembly path exists
                    if (!File.Exists(mainAssemblyPath))
                    {
                        return((int)AssemblyLoaderError.LoadFailed);
                    }

                    // Make sure we are using assmbly contexts loadding otherwise hotreload wont work which defeats the purpose of
                    // using multiple runtimes
                    if (LoadAssemblyWithoutContexts)
                    {
                        return((int)AssemblyLoaderError.LoadFailed);
                    }

                    // Preload now and then do a full load when NextRuntime is set to this runtime type
                    PreloadNextContext();

                    // Watch for assembly changes (the paths should have been set up by the full load in the other runtime)
                    UpdateAssemblyWatchers();

                    return(0);
                }

                unsafe
                {
                    SharedRuntimeState.Instance->ActiveRuntime = SharedRuntimeState.CurrentRuntime;
                }

                bool loaded;
                if (LoadAssemblyWithoutContexts)
                {
                    loaded = LoadWithoutUsingContexts();
                }
                else
                {
                    loaded = ReloadMainContext();
                }
                if (!loaded)
                {
                    unsafe
                    {
                        SharedRuntimeState.Instance->ActiveRuntime = EDotNetRuntime.None;
                    }
                    return((int)AssemblyLoaderError.LoadFailed);
                }
            }
            catch (Exception e)
            {
                string exceptionStr = "Entry point exception (Loader): " + e;
                if (SharedRuntimeState.Initialized)
                {
                    SharedRuntimeState.LogError(exceptionStr);
                    SharedRuntimeState.MessageBox(exceptionStr, errorMsgBoxTitle);
                }
                return((int)AssemblyLoaderError.Exception);
            }

            return(0);
        }
示例#9
0
        private static bool ReloadMainContext(bool threaded = true)
        {
            if (!GameThreadHelper.IsInGameThread())
            {
                bool result = false;
                GameThreadHelper.Run(delegate { result = ReloadMainContext(); });
                return(result);
            }

            if (!SharedRuntimeState.IsActiveRuntime)
            {
                return(false);
            }

            if (!File.Exists(mainAssemblyPath))
            {
                SharedRuntimeState.SetHotReloadData(null);
                return(false);
            }

            if (!mainContextRef.IsInvalid)
            {
                UnloadMainContext(threaded);
            }

            string entryPointArgEx = entryPointArg;
            bool   firstLoad       = preloadContextWaitHandle == null;

            if (firstLoad)
            {
                PreloadNextContext(threaded);
            }
            else
            {
                entryPointArgEx += "|Reloading=true";
            }

            preloadContextWaitHandle.WaitOne(Timeout.Infinite);
            preloadContextWaitHandle.Reset();

            if (!preloadFailed)
            {
                Debug.Assert(!preloadedContextRef.IsInvalid, "Preloaded context shouldn't be invalid");

                mainContextRef      = preloadedContextRef;
                preloadedContextRef = Runtime.AssemblyContextRef.Invalid;

                entryPointArgEx += "|AssemblyContext=" + mainContextRef.Format();

                try
                {
                    AssemblyLoader loader = new AssemblyLoader(mainAssemblyPath, entryPointType, entryPointMethod, entryPointArgEx, false, mainContextRef);
                    mainContextRef.DoCallBack(loader.Load);
                    UpdateAssemblyWatchers();
                }
                catch (Exception e)
                {
                    MessageBox("Failed to create assembly context for \"" + mainAssemblyPath + "\" " +
                               Environment.NewLine + Environment.NewLine + e, errorMsgBoxTitle);
                }
            }

            PreloadNextContext(threaded);
            SharedRuntimeState.SetHotReloadData(null);
            return(true);
        }
示例#10
0
        private static void AssemblyWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (!SharedRuntimeState.IsActiveRuntime)
            {
                return;
            }

            lock (assemblyWatchers)
            {
                // Require 500 milliseconds between updates to avoid multiple reloads
                //
                // Note: This may result in a genuine change to be missed which means
                // that some user action will be needed
                //
                // PossibleFix: Make this delayed and only catch the latest one? (will slow
                // reloads based on delay interval)
                if (lastAssemblyUpdate < DateTime.Now - TimeSpan.FromMilliseconds(500) && !isAssemblyWatcherReloading)
                {
                    bool complete = false;
                    bool hasChanged = false;

                    const int tries = 20;
                    const int sleep = 40;// 40*20 = 800 milliseconds of attempts (due to file locks whilst being written by AssemblyRewriter)
                    for (int i = 0; i < tries; i++)
                    {
                        try
                        {
                            if (File.Exists(e.FullPath))
                            {
                                using (FileStream reader = File.Open(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                                {
                                    if (reader.Length > 8)
                                    {
                                        reader.Position = reader.Length - 8;
                                        byte[] buffer = new byte[8];
                                        reader.Read(buffer, 0, buffer.Length);
                                        long signature = BitConverter.ToInt64(buffer, 0);
                                        if (signature == 3110675979262317867)// "+UEsRW++"
                                        {
                                            hasChanged = true;
                                        }
                                    }
                                }
                            }
                            complete = true;
                            break;
                        }
                        catch (IOException)
                        {
                            Thread.Sleep(sleep);
                        }
                        catch (Exception exception)
                        {
                            // Some unknown exception
                            SharedRuntimeState.LogWarning("Exception whilst hotreloading '" + e.FullPath + "'\n" + exception);
                            complete = true;
                            break;
                        }
                    }

                    if (hasChanged)
                    {
                        isAssemblyWatcherReloading = true;
                        ReloadMainContext();
                        lastAssemblyUpdate = DateTime.Now;
                        isAssemblyWatcherReloading = false;
                    }
                    else if (!complete)
                    {
                        SharedRuntimeState.LogWarning("Hotreload timed out for '" + e.FullPath + "'");
                    }
                }
            }
        }
示例#11
0
        private static void UpdateAssemblyWatchers()
        {
            string platformName = SharedRuntimeState.GetPlatformName();
            if (SharedRuntimeState.CurrentRuntime == EDotNetRuntime.Mono &&
                !string.IsNullOrEmpty(platformName) && platformName.ToLower() == "mac")
            {
                // libmono-native-compat.dylib (required by FileSystemWatcher) either doesn't load or is corrupted?
                // It crashes the Mono runtime when doing a symbol lookup (not sure which symbol)
                //
                // abort_with_payload
                // dyld::fastBindLazySymbol(ImageLoader**, unsigned long)
                // dyld_stub_binder
                // ---- managed frames  ----
                // mono_jit_runtime_invoke
                return;
            }

            string[] assemblyPaths = SharedRuntimeState.GetHotReloadAssemblyPaths();
            if (assemblyPaths != null)
            {
                HashSet<string> newAssemblyPaths = new HashSet<string>();
                HashSet<string> removedAssemblyPaths = new HashSet<string>();

                foreach (string assemblyPath in assemblyWatchers.Keys)
                {
                    if (!assemblyPaths.Contains(assemblyPath))
                    {
                        removedAssemblyPaths.Add(assemblyPath);
                    }
                }

                foreach (string assemblyPath in assemblyPaths)
                {
                    if (!assemblyWatchers.ContainsKey(assemblyPath))
                    {
                        newAssemblyPaths.Add(assemblyPath);
                    }
                }

                foreach (string assemblyPath in removedAssemblyPaths)
                {
                    assemblyWatchers[assemblyPath].Dispose();
                    assemblyWatchers.Remove(assemblyPath);
                }

                foreach (string assemblyPath in newAssemblyPaths)
                {
                    if (Directory.Exists(Path.GetDirectoryName(assemblyPath)))
                    {
                        FileSystemWatcher assemblyWatcher = new FileSystemWatcher();
                        assemblyWatcher.Path = Path.GetDirectoryName(assemblyPath);
                        assemblyWatcher.Filter = Path.GetFileName(assemblyPath);
                        assemblyWatcher.NotifyFilter = NotifyFilters.LastWrite;//NotifyFilters.CreationTime;
                        assemblyWatcher.EnableRaisingEvents = true;
                        assemblyWatcher.Changed += AssemblyWatcher_Changed;

                        assemblyWatchers.Add(assemblyPath, assemblyWatcher);
                    }
                }
            }

            if (assemblyWatchers.Count == 0)
            {
                SharedRuntimeState.LogWarning("No assembly watchers active for hotreload (\"USharpRuntime reload\" command can be used instead)");
            }
        }