Skip to content

Commit fdb0f43

Browse files
committed
Add ILifetimeService interface and AppTokenLifetimeService implementation
1 parent d4af795 commit fdb0f43

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

ComponentModel/ILifetimeService.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
namespace Ecng.ComponentModel;
2+
3+
using System;
4+
using System.Threading;
5+
6+
/// <summary>
7+
/// Interface for application lifecycle management.
8+
/// Provides abstraction over shutdown and restart operations.
9+
/// </summary>
10+
public interface ILifetimeService
11+
{
12+
/// <summary>
13+
/// Gets the cancellation token that is triggered when the application is shutting down.
14+
/// </summary>
15+
CancellationToken Token { get; }
16+
17+
/// <summary>
18+
/// Initiates application shutdown.
19+
/// </summary>
20+
void Shutdown();
21+
22+
/// <summary>
23+
/// Initiates application restart.
24+
/// </summary>
25+
void Restart();
26+
}
27+
28+
/// <summary>
29+
/// Default implementation of <see cref="ILifetimeService"/> using <see cref="AppToken"/>.
30+
/// </summary>
31+
/// <param name="restart">Delegate to handle restart logic.</param>
32+
public class AppTokenLifetimeService(Action restart) : ILifetimeService
33+
{
34+
private readonly Action _restart = restart ?? throw new ArgumentNullException(nameof(restart));
35+
36+
/// <inheritdoc />
37+
public CancellationToken Token => AppToken.Value;
38+
39+
/// <inheritdoc />
40+
public void Shutdown() => AppToken.Shutdown();
41+
42+
/// <inheritdoc />
43+
public void Restart()
44+
{
45+
_restart();
46+
Shutdown();
47+
}
48+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
namespace Ecng.Tests.ComponentModel;
2+
3+
using Ecng.ComponentModel;
4+
5+
[TestClass]
6+
public class LifetimeServiceTests : BaseTestClass
7+
{
8+
private class MockLifetimeService : ILifetimeService
9+
{
10+
private readonly CancellationTokenSource _cts = new();
11+
12+
public CancellationToken Token => _cts.Token;
13+
14+
public bool ShutdownCalled { get; private set; }
15+
public bool RestartCalled { get; private set; }
16+
17+
public void Shutdown()
18+
{
19+
ShutdownCalled = true;
20+
_cts.Cancel();
21+
}
22+
23+
public void Restart()
24+
{
25+
RestartCalled = true;
26+
_cts.Cancel();
27+
}
28+
}
29+
30+
[TestMethod]
31+
public void Token_InitiallyNotCancelled()
32+
{
33+
var service = new MockLifetimeService();
34+
service.Token.IsCancellationRequested.AssertFalse();
35+
}
36+
37+
[TestMethod]
38+
public void Shutdown_CancelsToken()
39+
{
40+
var service = new MockLifetimeService();
41+
42+
service.Shutdown();
43+
44+
service.ShutdownCalled.AssertTrue();
45+
service.Token.IsCancellationRequested.AssertTrue();
46+
}
47+
48+
[TestMethod]
49+
public void Restart_CancelsToken()
50+
{
51+
var service = new MockLifetimeService();
52+
53+
service.Restart();
54+
55+
service.RestartCalled.AssertTrue();
56+
service.Token.IsCancellationRequested.AssertTrue();
57+
}
58+
59+
[TestMethod]
60+
public void Token_CanBeUsedWithTaskDelay()
61+
{
62+
var service = new MockLifetimeService();
63+
64+
var task = Task.Delay(10000, service.Token);
65+
66+
service.Shutdown();
67+
68+
ThrowsExactly<TaskCanceledException>(() => task.GetAwaiter().GetResult());
69+
}
70+
71+
[TestMethod]
72+
public void MultipleShutdownCalls_DoNotThrow()
73+
{
74+
var service = new MockLifetimeService();
75+
76+
service.Shutdown();
77+
// Second call should not throw
78+
service.Shutdown();
79+
80+
service.Token.IsCancellationRequested.AssertTrue();
81+
}
82+
83+
[TestMethod]
84+
public void AppTokenLifetimeService_TokenReturnsAppToken()
85+
{
86+
var service = new AppTokenLifetimeService(() => { });
87+
88+
service.Token.AssertEqual(AppToken.Value);
89+
}
90+
91+
[TestMethod]
92+
public void AppTokenLifetimeService_RestartCallsDelegate()
93+
{
94+
var restartCalled = false;
95+
var service = new AppTokenLifetimeService(() => restartCalled = true);
96+
97+
service.Restart();
98+
99+
restartCalled.AssertTrue();
100+
}
101+
102+
[TestMethod]
103+
public void AppTokenLifetimeService_NullRestartThrows()
104+
{
105+
ThrowsExactly<ArgumentNullException>(() => new AppTokenLifetimeService(null));
106+
}
107+
}

0 commit comments

Comments
 (0)