mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-09 12:46:47 +02:00
Fix crash on querying when plugins are initializing
This commit is contained in:
@@ -10,16 +10,9 @@ namespace Wox.Commands
|
|||||||
{
|
{
|
||||||
public class SystemCommand : BaseCommand
|
public class SystemCommand : BaseCommand
|
||||||
{
|
{
|
||||||
private List<PluginPair> systemPlugins;
|
|
||||||
|
|
||||||
public SystemCommand()
|
|
||||||
{
|
|
||||||
systemPlugins = Plugins.AllPlugins.Where(o => o.Metadata.PluginType == PluginType.System).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispatch(Query query)
|
public override void Dispatch(Query query)
|
||||||
{
|
{
|
||||||
foreach (PluginPair pair in systemPlugins)
|
foreach (PluginPair pair in Plugins.AllPlugins.Where(o => o.Metadata.PluginType == PluginType.System))
|
||||||
{
|
{
|
||||||
PluginPair pair1 = pair;
|
PluginPair pair1 = pair;
|
||||||
ThreadPool.QueueUserWorkItem(state =>
|
ThreadPool.QueueUserWorkItem(state =>
|
||||||
|
|||||||
134
Wox/Helper/Forker.cs
Normal file
134
Wox/Helper/Forker.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Wox.Helper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a caller-friendly wrapper around parallel actions.
|
||||||
|
/// http://stackoverflow.com/a/540380
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Forker
|
||||||
|
{
|
||||||
|
int running;
|
||||||
|
private readonly object joinLock = new object(), eventLock = new object();
|
||||||
|
|
||||||
|
/// <summary>Raised when all operations have completed.</summary>
|
||||||
|
public event EventHandler AllComplete
|
||||||
|
{
|
||||||
|
add { lock (eventLock) { allComplete += value; } }
|
||||||
|
remove { lock (eventLock) { allComplete -= value; } }
|
||||||
|
}
|
||||||
|
private EventHandler allComplete;
|
||||||
|
/// <summary>Raised when each operation completes.</summary>
|
||||||
|
public event EventHandler<ParallelEventArgs> ItemComplete
|
||||||
|
{
|
||||||
|
add { lock (eventLock) { itemComplete += value; } }
|
||||||
|
remove { lock (eventLock) { itemComplete -= value; } }
|
||||||
|
}
|
||||||
|
private EventHandler<ParallelEventArgs> itemComplete;
|
||||||
|
|
||||||
|
private void OnItemComplete(object state, Exception exception)
|
||||||
|
{
|
||||||
|
EventHandler<ParallelEventArgs> itemHandler = itemComplete; // don't need to lock
|
||||||
|
if (itemHandler != null) itemHandler(this, new ParallelEventArgs(state, exception));
|
||||||
|
if (Interlocked.Decrement(ref running) == 0)
|
||||||
|
{
|
||||||
|
EventHandler allHandler = allComplete; // don't need to lock
|
||||||
|
if (allHandler != null) allHandler(this, EventArgs.Empty);
|
||||||
|
lock (joinLock)
|
||||||
|
{
|
||||||
|
Monitor.PulseAll(joinLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds a callback to invoke when each operation completes.</summary>
|
||||||
|
/// <returns>Current instance (for fluent API).</returns>
|
||||||
|
public Forker OnItemComplete(EventHandler<ParallelEventArgs> handler)
|
||||||
|
{
|
||||||
|
if (handler == null) throw new ArgumentNullException("handler");
|
||||||
|
ItemComplete += handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds a callback to invoke when all operations are complete.</summary>
|
||||||
|
/// <returns>Current instance (for fluent API).</returns>
|
||||||
|
public Forker OnAllComplete(EventHandler handler)
|
||||||
|
{
|
||||||
|
if (handler == null) throw new ArgumentNullException("handler");
|
||||||
|
AllComplete += handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Waits for all operations to complete.</summary>
|
||||||
|
public void Join()
|
||||||
|
{
|
||||||
|
Join(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Waits (with timeout) for all operations to complete.</summary>
|
||||||
|
/// <returns>Whether all operations had completed before the timeout.</returns>
|
||||||
|
public bool Join(int millisecondsTimeout)
|
||||||
|
{
|
||||||
|
lock (joinLock)
|
||||||
|
{
|
||||||
|
if (CountRunning() == 0) return true;
|
||||||
|
Thread.SpinWait(1); // try our luck...
|
||||||
|
return (CountRunning() == 0) ||
|
||||||
|
Monitor.Wait(joinLock, millisecondsTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Indicates the number of incomplete operations.</summary>
|
||||||
|
/// <returns>The number of incomplete operations.</returns>
|
||||||
|
public int CountRunning()
|
||||||
|
{
|
||||||
|
return Interlocked.CompareExchange(ref running, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Enqueues an operation.</summary>
|
||||||
|
/// <param name="action">The operation to perform.</param>
|
||||||
|
/// <returns>The current instance (for fluent API).</returns>
|
||||||
|
public Forker Fork(ThreadStart action) { return Fork(action, null); }
|
||||||
|
|
||||||
|
/// <summary>Enqueues an operation.</summary>
|
||||||
|
/// <param name="action">The operation to perform.</param>
|
||||||
|
/// <param name="state">An opaque object, allowing the caller to identify operations.</param>
|
||||||
|
/// <returns>The current instance (for fluent API).</returns>
|
||||||
|
public Forker Fork(ThreadStart action, object state)
|
||||||
|
{
|
||||||
|
if (action == null) throw new ArgumentNullException("action");
|
||||||
|
Interlocked.Increment(ref running);
|
||||||
|
ThreadPool.QueueUserWorkItem(delegate
|
||||||
|
{
|
||||||
|
Exception exception = null;
|
||||||
|
try { action(); }
|
||||||
|
catch (Exception ex) { exception = ex; }
|
||||||
|
OnItemComplete(state, exception);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Event arguments representing the completion of a parallel action.</summary>
|
||||||
|
public class ParallelEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
private readonly object state;
|
||||||
|
private readonly Exception exception;
|
||||||
|
internal ParallelEventArgs(object state, Exception exception)
|
||||||
|
{
|
||||||
|
this.state = state;
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The opaque state object that identifies the action (null otherwise).</summary>
|
||||||
|
public object State { get { return state; } }
|
||||||
|
|
||||||
|
/// <summary>The exception thrown by the parallel action, or null if it completed without exception.</summary>
|
||||||
|
public Exception Exception { get { return exception; } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,13 @@ namespace Wox.PluginLoader
|
|||||||
{
|
{
|
||||||
private static string debuggerMode = null;
|
private static string debuggerMode = null;
|
||||||
private static List<PluginPair> plugins = new List<PluginPair>();
|
private static List<PluginPair> plugins = new List<PluginPair>();
|
||||||
|
private static ManualResetEvent initializing = null;
|
||||||
|
|
||||||
public static void Init()
|
public static void Init()
|
||||||
{
|
{
|
||||||
|
if (initializing != null) return;
|
||||||
|
|
||||||
|
initializing = new ManualResetEvent(false);
|
||||||
plugins.Clear();
|
plugins.Clear();
|
||||||
BasePluginLoader.ParsePluginsConfig();
|
BasePluginLoader.ParsePluginsConfig();
|
||||||
|
|
||||||
@@ -28,6 +32,7 @@ namespace Wox.PluginLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins.AddRange(new CSharpPluginLoader().LoadPlugin());
|
plugins.AddRange(new CSharpPluginLoader().LoadPlugin());
|
||||||
|
Forker forker = new Forker();
|
||||||
foreach (IPlugin plugin in plugins.Select(pluginPair => pluginPair.Plugin))
|
foreach (IPlugin plugin in plugins.Select(pluginPair => pluginPair.Plugin))
|
||||||
{
|
{
|
||||||
IPlugin plugin1 = plugin;
|
IPlugin plugin1 = plugin;
|
||||||
@@ -35,7 +40,7 @@ namespace Wox.PluginLoader
|
|||||||
if (pluginPair != null)
|
if (pluginPair != null)
|
||||||
{
|
{
|
||||||
PluginMetadata metadata = pluginPair.Metadata;
|
PluginMetadata metadata = pluginPair.Metadata;
|
||||||
ThreadPool.QueueUserWorkItem(o => plugin1.Init(new PluginInitContext()
|
forker.Fork(() => plugin1.Init(new PluginInitContext()
|
||||||
{
|
{
|
||||||
Plugins = plugins,
|
Plugins = plugins,
|
||||||
CurrentPluginMetadata = metadata,
|
CurrentPluginMetadata = metadata,
|
||||||
@@ -58,11 +63,26 @@ namespace Wox.PluginLoader
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem(o =>
|
||||||
|
{
|
||||||
|
forker.Join();
|
||||||
|
initializing.Set();
|
||||||
|
initializing = null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<PluginPair> AllPlugins
|
public static List<PluginPair> AllPlugins
|
||||||
{
|
{
|
||||||
get { return plugins; }
|
get
|
||||||
|
{
|
||||||
|
var init = initializing;
|
||||||
|
if (init != null)
|
||||||
|
{
|
||||||
|
init.WaitOne();
|
||||||
|
}
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool HitThirdpartyKeyword(Query query)
|
public static bool HitThirdpartyKeyword(Query query)
|
||||||
|
|||||||
@@ -111,6 +111,7 @@
|
|||||||
<Compile Include="Commands\SystemCommand.cs" />
|
<Compile Include="Commands\SystemCommand.cs" />
|
||||||
<Compile Include="Helper\DataWebRequestFactory.cs" />
|
<Compile Include="Helper\DataWebRequestFactory.cs" />
|
||||||
<Compile Include="Helper\FontHelper.cs" />
|
<Compile Include="Helper\FontHelper.cs" />
|
||||||
|
<Compile Include="Helper\Forker.cs" />
|
||||||
<Compile Include="Helper\SyntaxSugars.cs" />
|
<Compile Include="Helper\SyntaxSugars.cs" />
|
||||||
<Compile Include="Helper\WindowIntelopHelper.cs" />
|
<Compile Include="Helper\WindowIntelopHelper.cs" />
|
||||||
<Compile Include="ProgramSourceSetting.xaml.cs">
|
<Compile Include="ProgramSourceSetting.xaml.cs">
|
||||||
|
|||||||
Reference in New Issue
Block a user