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)"); } }
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 + "'"); } } } }
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)"); } }