Skip to content
This repository was archived by the owner on Jan 25, 2026. It is now read-only.

Commit 32f1188

Browse files
committed
feat(file-service): 添加等待结果的异步实现
1 parent 8e3c9d1 commit 32f1188

3 files changed

Lines changed: 134 additions & 3 deletions

File tree

IO/FileService.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.Linq;
66
using System.Threading;
7+
using System.Threading.Tasks;
78
using PCL.Core.App;
89
using PCL.Core.UI;
910
using PCL.Core.Utils;
@@ -157,7 +158,7 @@ public static void RegisterDefaultProcess(FileMatch match, FileProcess process)
157158
private static readonly ConcurrentQueue<IFileTask> _PendingTasks = [];
158159

159160
private static readonly ConcurrentDictionary<FileItem, AtomicVariable<object>> _ProcessResults = [];
160-
private static readonly ConcurrentDictionary<FileItem, ManualResetEventSlim> _WaitForResultEvents = [];
161+
private static readonly ConcurrentDictionary<FileItem, AsyncManualResetEvent> _WaitForResultEvents = [];
161162
private static readonly AutoResetEvent _ContinueEvent = new(false);
162163
private static bool _running = true;
163164

@@ -300,7 +301,7 @@ public static bool TryGetResult(FileItem item, out AnyType? result, bool remove
300301
if (remove) _ProcessResults.TryRemove(item, out _);
301302
return true;
302303
}
303-
304+
304305
/// <param name="item">which file to get the result</param>
305306
/// <param name="remove">whether remove from the temp dictionary after successfully get the value</param>
306307
/// <returns>a <b>nullable</b> value</returns>
@@ -322,7 +323,7 @@ public static bool TryGetResult(FileItem item, out AnyType? result, bool remove
322323
{
323324
var success = TryGetResult(item, out var result, remove);
324325
if (success) return result;
325-
var waitEvent = _WaitForResultEvents.GetOrAdd(item, _ => new ManualResetEventSlim(false));
326+
var waitEvent = _WaitForResultEvents.GetOrAdd(item, _ => new AsyncManualResetEvent());
326327
var waitResult = true;
327328
if (timeout is { } t) waitResult = waitEvent.Wait(t);
328329
else waitEvent.Wait();
@@ -331,6 +332,26 @@ public static bool TryGetResult(FileItem item, out AnyType? result, bool remove
331332
return result;
332333
}
333334

335+
/// <param name="item">which file to wait for the result</param>
336+
/// <param name="cancelToken">the cancellation token to stop waiting</param>
337+
/// <param name="remove">whether remove from the temp dictionary after successfully get the value</param>
338+
/// <returns>
339+
/// a value, or <c>null</c> if the result is really <c>null</c>, or else, something is boom -
340+
/// I don't know what is wrong but in a word there is something wrong :D
341+
/// </returns>
342+
public static async Task<AnyType?> WaitForResultAsync(FileItem item, CancellationToken cancelToken = default, bool remove = true)
343+
{
344+
var success = TryGetResult(item, out var result, remove);
345+
if (success) return result;
346+
var waitEvent = _WaitForResultEvents.GetOrAdd(item, _ => new AsyncManualResetEvent());
347+
var waitResult = true;
348+
cancelToken.Register(() => waitResult = false);
349+
await waitEvent.WaitAsync(cancelToken);
350+
if (!waitResult) return null;
351+
TryGetResult(item, out result);
352+
return result;
353+
}
354+
334355
#endregion
335356

336357
private static void _Initialize()

PCL.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@
210210
<Compile Include="Utils\Secret\Identify.cs" />
211211
<Compile Include="Utils\SemVer.cs" />
212212
<Compile Include="Utils\StringStream.cs" />
213+
<Compile Include="Utils\Threading\AsyncManualResetEvent.cs" />
213214
<Compile Include="Utils\Threading\DualThreadPool.cs" />
214215
<Compile Include="Utils\Threading\LimitedConcurrencyLevelTaskScheduler.cs" />
215216
<Compile Include="Utils\VarIntHelper.cs" />
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace PCL.Core.Utils.Threading;
6+
7+
// Partly generated by gpt-5-mini (20250808)
8+
public sealed class AsyncManualResetEvent : IDisposable
9+
{
10+
private readonly object _syncLock = new();
11+
private TaskCompletionSource<bool> _tcs = new(TaskCreationOptions.RunContinuationsAsynchronously);
12+
private readonly ManualResetEventSlim _mre = new(false);
13+
private bool _disposed;
14+
15+
public AsyncManualResetEvent(bool initialState = false)
16+
{
17+
if (!initialState) return;
18+
_tcs.SetResult(true);
19+
_mre.Set();
20+
}
21+
22+
/// <summary>
23+
/// 事件是否已触发。
24+
/// </summary>
25+
public bool IsSet
26+
{
27+
get { lock (_syncLock) { return _tcs.Task.IsCompleted; } }
28+
}
29+
30+
/// <summary>
31+
/// 异步等待。
32+
/// </summary>
33+
/// <param name="cancellationToken">用于结束等待的取消信号</param>
34+
public Task WaitAsync(CancellationToken cancellationToken = default)
35+
{
36+
TaskCompletionSource<bool> t;
37+
lock (_syncLock) { t = _tcs; }
38+
if (!cancellationToken.CanBeCanceled || t.Task.IsCompleted) return t.Task;
39+
return _WaitWithCancellationAsync(t.Task, cancellationToken);
40+
}
41+
42+
private static async Task _WaitWithCancellationAsync(Task waitTask, CancellationToken ct)
43+
{
44+
var cancelTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
45+
using (ct.Register(s => ((TaskCompletionSource<bool>)s!).TrySetResult(true), cancelTcs))
46+
{
47+
var completed = await Task.WhenAny(waitTask, cancelTcs.Task).ConfigureAwait(false);
48+
if (completed == cancelTcs.Task) ct.ThrowIfCancellationRequested();
49+
await waitTask.ConfigureAwait(false); // propagate exceptions if any
50+
}
51+
}
52+
53+
/// <summary>
54+
/// 同步等待。
55+
/// </summary>
56+
public void Wait() => _mre.Wait();
57+
58+
/// <summary>
59+
/// 同步等待,并在超时后结束。
60+
/// </summary>
61+
/// <param name="millisecondsTimeout">等待超时的毫秒数</param>
62+
/// <returns>若已触发事件则为 <c>true</c>,否则为 <c>false</c></returns>
63+
public bool Wait(int millisecondsTimeout) => _mre.Wait(millisecondsTimeout);
64+
65+
/// <summary>
66+
/// 同步等待,并在超时后结束。
67+
/// </summary>
68+
/// <param name="timeout">等待超时</param>
69+
/// <returns>若已触发事件则为 <c>true</c>,否则为 <c>false</c></returns>
70+
public bool Wait(TimeSpan timeout) => _mre.Wait(timeout);
71+
72+
/// <summary>
73+
/// 同步等待,并传递用于结束等待的取消信号。
74+
/// </summary>
75+
/// <param name="cancellationToken">用于结束等待的取消信号</param>
76+
public void Wait(CancellationToken cancellationToken) => _mre.Wait(cancellationToken);
77+
78+
/// <summary>
79+
/// 触发事件。
80+
/// </summary>
81+
public void Set()
82+
{
83+
lock (_syncLock)
84+
{
85+
_tcs.TrySetResult(true); // Use TrySetResult to avoid exceptions on repeated Set
86+
_mre.Set();
87+
}
88+
}
89+
90+
/// <summary>
91+
/// 重置事件。
92+
/// </summary>
93+
public void Reset()
94+
{
95+
lock (_syncLock)
96+
{
97+
if (!_tcs.Task.IsCompleted) return; // already reset
98+
_tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
99+
_mre.Reset();
100+
}
101+
}
102+
103+
public void Dispose()
104+
{
105+
if (_disposed) return;
106+
_mre.Dispose();
107+
_disposed = true;
108+
}
109+
}

0 commit comments

Comments
 (0)