Skip to content

Commit dc0d8f2

Browse files
authored
Fixes #4429 - Timeouts lost (#4430)
1 parent 0f72cf8 commit dc0d8f2

File tree

9 files changed

+1358
-79
lines changed

9 files changed

+1358
-79
lines changed

.github/workflows/api-docs.yml

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
name: Build and publish API docs
1+
name: Build and publish v2 API docs
22

33
on:
44
push:
5-
branches: [v1_release, v2_develop]
5+
branches: [v2_develop]
66

77
permissions:
88
id-token: write
99
pages: write
1010

1111
jobs:
1212
deploy:
13-
name: Build and Deploy API docs to github-pages ${{ github.ref_name }}
13+
name: Build and Deploy v2 API docs to github-pages ${{ github.ref_name }}
1414
environment:
1515
name: github-pages
1616
url: ${{ steps.deployment.outputs.page_url }}
@@ -20,7 +20,6 @@ jobs:
2020
uses: actions/checkout@v4
2121

2222
- name: DocFX Build
23-
#if: github.ref_name == 'v1_release' || github.ref_name == 'v1_develop'
2423
working-directory: docfx
2524
run: |
2625
dotnet tool install -g docfx
@@ -30,27 +29,15 @@ jobs:
3029
continue-on-error: false
3130

3231
- name: Setup Pages
33-
#if: github.ref_name == 'v1_release' || github.ref_name == 'v1_develop'
3432
uses: actions/configure-pages@v5
3533

3634
- name: Upload artifact
37-
#if: github.ref_name == 'v1_release' || github.ref_name == 'v1_develop'
3835
uses: actions/upload-pages-artifact@v3
3936
with:
4037
path: docfx/_site
4138

4239
- name: Deploy to GitHub Pages
43-
if: github.ref_name == 'v2_release' || github.ref_name == 'v2_develop'
4440
id: deployment
4541
uses: actions/deploy-pages@v4
4642
with:
4743
token: ${{ secrets.GITHUB_TOKEN }}
48-
49-
# - name: v1_release Repository Dispatch ${{ github.ref_name }}
50-
# if: github.ref_name == 'v2_develop'
51-
# uses: peter-evans/repository-dispatch@v3
52-
# with:
53-
# token: ${{ secrets.V2DOCS_TOKEN }}
54-
# repository: gui-cs/Terminal.GuiV1Docs
55-
# event-type: v2_develop_push
56-
# client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'

Terminal.Gui/App/Timeout/TimedEvents.cs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -201,32 +201,47 @@ private long NudgeToUniqueKey (long k)
201201
private void RunTimersImpl ()
202202
{
203203
long now = GetTimestampTicks ();
204-
SortedList<long, Timeout> copy;
205204

206-
// lock prevents new timeouts being added
207-
// after we have taken the copy but before
208-
// we have allocated a new list (which would
209-
// result in lost timeouts or errors during enumeration)
210-
lock (_timeoutsLockToken)
205+
// Process due timeouts one at a time, without blocking the entire queue
206+
while (true)
211207
{
212-
copy = _timeouts;
213-
_timeouts = new ();
214-
}
208+
Timeout? timeoutToExecute = null;
209+
long scheduledTime = 0;
215210

216-
foreach ((long k, Timeout timeout) in copy)
217-
{
218-
if (k < now)
211+
// Find the next due timeout
212+
lock (_timeoutsLockToken)
219213
{
220-
if (timeout.Callback! ())
214+
if (_timeouts.Count == 0)
215+
{
216+
break; // No more timeouts
217+
}
218+
219+
// Re-evaluate current time for each iteration
220+
now = GetTimestampTicks ();
221+
222+
// Check if the earliest timeout is due
223+
scheduledTime = _timeouts.Keys [0];
224+
225+
if (scheduledTime >= now)
221226
{
222-
AddTimeout (timeout.Span, timeout);
227+
// Earliest timeout is not yet due, we're done
228+
break;
223229
}
230+
231+
// This timeout is due - remove it from the queue
232+
timeoutToExecute = _timeouts.Values [0];
233+
_timeouts.RemoveAt (0);
224234
}
225-
else
235+
236+
// Execute the callback outside the lock
237+
// This allows nested Run() calls to access the timeout queue
238+
if (timeoutToExecute != null)
226239
{
227-
lock (_timeoutsLockToken)
240+
bool repeat = timeoutToExecute.Callback! ();
241+
242+
if (repeat)
228243
{
229-
_timeouts.Add (NudgeToUniqueKey (k), timeout);
244+
AddTimeout (timeoutToExecute.Span, timeoutToExecute);
230245
}
231246
}
232247
}

Tests/UnitTestsParallelizable/Application/ApplicationImplTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,11 +380,10 @@ private bool IdleExit (IApplication app)
380380
if (app.TopRunnableView != null)
381381
{
382382
app.RequestStop ();
383-
384-
return true;
385383
}
386384

387-
return true;
385+
// Return false so the timer does not repeat
386+
return false;
388387
}
389388

390389
[Fact]

Tests/UnitTestsParallelizable/Application/ApplicationTests.cs

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,6 @@ public class ApplicationTests (ITestOutputHelper output)
1111
{
1212
private readonly ITestOutputHelper _output = output;
1313

14-
[Fact]
15-
public void AddTimeout_Fires ()
16-
{
17-
IApplication app = Application.Create ();
18-
app.Init ("fake");
19-
20-
uint timeoutTime = 100;
21-
var timeoutFired = false;
22-
23-
// Setup a timeout that will fire
24-
app.AddTimeout (
25-
TimeSpan.FromMilliseconds (timeoutTime),
26-
() =>
27-
{
28-
timeoutFired = true;
29-
30-
// Return false so the timer does not repeat
31-
return false;
32-
}
33-
);
34-
35-
// The timeout has not fired yet
36-
Assert.False (timeoutFired);
37-
38-
// Block the thread to prove the timeout does not fire on a background thread
39-
Thread.Sleep ((int)timeoutTime * 2);
40-
Assert.False (timeoutFired);
41-
42-
app.StopAfterFirstIteration = true;
43-
app.Run<Runnable> ();
44-
45-
// The timeout should have fired
46-
Assert.True (timeoutFired);
47-
48-
app.Dispose ();
49-
}
5014

5115
[Fact]
5216
public void Begin_Null_Runnable_Throws ()
@@ -281,10 +245,6 @@ public void Run_Iteration_Fires ()
281245

282246
void Application_Iteration (object? sender, EventArgs<IApplication?> e)
283247
{
284-
if (iteration > 0)
285-
{
286-
Assert.Fail ();
287-
}
288248

289249
iteration++;
290250
app.RequestStop ();

0 commit comments

Comments
 (0)