Skip to content

Commit 4206442

Browse files
committed
Adds state support, better jit optimization
1 parent 571dbee commit 4206442

File tree

2 files changed

+76
-56
lines changed

2 files changed

+76
-56
lines changed

src/AsyncSingleton{T,T1}.cs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,16 @@ public sealed class AsyncSingleton<T, T1> : IAsyncSingleton<T, T1>
2727
private readonly Func<T1, T>? _syncFactory;
2828
private readonly Func<CancellationToken, T1, T>? _syncFactoryToken;
2929

30-
public AsyncSingleton(Func<T1, ValueTask<T>> factory)
31-
=> _asyncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
30+
public AsyncSingleton(Func<T1, ValueTask<T>> factory) => _asyncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
3231

33-
public AsyncSingleton(Func<CancellationToken, T1, ValueTask<T>> factory)
34-
=> _asyncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
32+
public AsyncSingleton(Func<CancellationToken, T1, ValueTask<T>> factory) =>
33+
_asyncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
3534

36-
public AsyncSingleton(Func<T1, T> factory)
37-
=> _syncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
35+
public AsyncSingleton(Func<T1, T> factory) => _syncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
3836

39-
public AsyncSingleton(Func<CancellationToken, T1, T> factory)
40-
=> _syncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
37+
public AsyncSingleton(Func<CancellationToken, T1, T> factory) => _syncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
4138

42-
public ValueTask<T> Get(T1 arg, CancellationToken cancellationToken = default)
43-
=> GetOrCreate(cancellationToken, arg);
39+
public ValueTask<T> Get(T1 arg, CancellationToken cancellationToken = default) => GetOrCreate(cancellationToken, arg);
4440

4541
private ValueTask<T> GetOrCreate(CancellationToken ct, T1 arg)
4642
{
@@ -51,24 +47,26 @@ private ValueTask<T> GetOrCreate(CancellationToken ct, T1 arg)
5147
return new ValueTask<T>(_instance!);
5248

5349
return Slow(ct, arg);
50+
}
5451

55-
async ValueTask<T> Slow(CancellationToken token, T1 a)
52+
private async ValueTask<T> Slow(CancellationToken token, T1 a)
53+
{
54+
using (await _lock.Lock(token)
55+
.NoSync())
5656
{
57-
using (await _lock.Lock(token).NoSync())
58-
{
59-
if (_disposed.Value)
60-
throw new ObjectDisposedException(typeof(AsyncSingleton<T, T1>).Name);
57+
if (_disposed.Value)
58+
throw new ObjectDisposedException(typeof(AsyncSingleton<T, T1>).Name);
6159

62-
if (_hasValue.Value)
63-
return _instance!;
60+
if (_hasValue.Value)
61+
return _instance!;
6462

65-
T created = await Create(token, a).NoSync();
63+
T created = await Create(token, a)
64+
.NoSync();
6665

67-
_instance = created!;
68-
_hasValue.Value = true;
66+
_instance = created!;
67+
_hasValue.Value = true;
6968

70-
return created;
71-
}
69+
return created;
7270
}
7371
}
7472

@@ -123,10 +121,12 @@ private T CreateSync(CancellationToken ct, T1 arg)
123121
return _syncFactory(arg);
124122

125123
if (_asyncFactoryToken is not null)
126-
return _asyncFactoryToken(ct, arg).AwaitSync();
124+
return _asyncFactoryToken(ct, arg)
125+
.AwaitSync();
127126

128127
if (_asyncFactory is not null)
129-
return _asyncFactory(arg).AwaitSync();
128+
return _asyncFactory(arg)
129+
.AwaitSync();
130130

131131
throw new InvalidOperationException("No initialization factory was configured.");
132132
}
@@ -146,7 +146,8 @@ public void Dispose()
146146
}
147147

148148
if (local is IAsyncDisposable ad)
149-
ad.DisposeAsync().AwaitSync();
149+
ad.DisposeAsync()
150+
.AwaitSync();
150151
else if (local is IDisposable d)
151152
d.Dispose();
152153

@@ -160,18 +161,20 @@ public async ValueTask DisposeAsync()
160161

161162
object? local;
162163

163-
using (await _lock.Lock().NoSync())
164+
using (await _lock.Lock()
165+
.NoSync())
164166
{
165167
_hasValue.Value = false;
166168
local = _instance;
167169
_instance = default;
168170
}
169171

170172
if (local is IAsyncDisposable ad)
171-
await ad.DisposeAsync().NoSync();
173+
await ad.DisposeAsync()
174+
.NoSync();
172175
else if (local is IDisposable d)
173176
d.Dispose();
174177

175178
GC.SuppressFinalize(this);
176179
}
177-
}
180+
}

src/AsyncSingleton{T}.cs

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,24 @@ public class AsyncSingleton<T> : IAsyncSingleton<T>
2626
private readonly Func<T>? _syncFactory;
2727
private readonly Func<CancellationToken, T>? _syncFactoryToken;
2828

29-
public AsyncSingleton(Func<ValueTask<T>> factory)
30-
=> _asyncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
29+
private readonly object? _state;
30+
private readonly Func<object, CancellationToken, ValueTask<T>>? _asyncFactoryTokenState;
3131

32-
public AsyncSingleton(Func<CancellationToken, ValueTask<T>> factory)
33-
=> _asyncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
32+
public AsyncSingleton(Func<ValueTask<T>> factory) => _asyncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
3433

35-
public AsyncSingleton(Func<T> factory)
36-
=> _syncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
34+
public AsyncSingleton(Func<CancellationToken, ValueTask<T>> factory) => _asyncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
3735

38-
public AsyncSingleton(Func<CancellationToken, T> factory)
39-
=> _syncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
36+
public AsyncSingleton(object state, Func<object, CancellationToken, ValueTask<T>> factory)
37+
{
38+
_state = state ?? throw new ArgumentNullException(nameof(state));
39+
_asyncFactoryTokenState = factory ?? throw new ArgumentNullException(nameof(factory));
40+
}
41+
42+
public AsyncSingleton(Func<T> factory) => _syncFactory = factory ?? throw new ArgumentNullException(nameof(factory));
43+
44+
public AsyncSingleton(Func<CancellationToken, T> factory) => _syncFactoryToken = factory ?? throw new ArgumentNullException(nameof(factory));
4045

41-
public ValueTask<T> Get(CancellationToken cancellationToken = default)
42-
=> GetOrCreate(cancellationToken);
46+
public ValueTask<T> Get(CancellationToken cancellationToken = default) => GetOrCreate(cancellationToken);
4347

4448
public virtual ValueTask<T> GetOrCreate(CancellationToken cancellationToken = default)
4549
{
@@ -51,24 +55,26 @@ public virtual ValueTask<T> GetOrCreate(CancellationToken cancellationToken = de
5155
return new ValueTask<T>((T)_instance!);
5256

5357
return Slow(cancellationToken);
58+
}
5459

55-
async ValueTask<T> Slow(CancellationToken ct)
60+
private async ValueTask<T> Slow(CancellationToken ct)
61+
{
62+
using (await _lock.Lock(ct)
63+
.NoSync())
5664
{
57-
using (await _lock.Lock(ct).NoSync())
58-
{
59-
if (_disposed.Value)
60-
throw new ObjectDisposedException(typeof(AsyncSingleton<T>).Name);
65+
if (_disposed.Value)
66+
throw new ObjectDisposedException(typeof(AsyncSingleton<T>).Name);
6167

62-
if (_hasValue.Value)
63-
return (T)_instance!;
68+
if (_hasValue.Value)
69+
return (T)_instance!;
6470

65-
T created = await Create(ct).NoSync();
71+
T created = await Create(ct)
72+
.NoSync();
6673

67-
_instance = created!;
68-
_hasValue.Value = true;
74+
_instance = created!;
75+
_hasValue.Value = true;
6976

70-
return created;
71-
}
77+
return created;
7278
}
7379
}
7480

@@ -99,6 +105,9 @@ public T GetSync(CancellationToken cancellationToken = default)
99105

100106
private ValueTask<T> Create(CancellationToken ct)
101107
{
108+
if (_asyncFactoryTokenState is not null)
109+
return _asyncFactoryTokenState(_state!, ct);
110+
102111
if (_asyncFactoryToken is not null)
103112
return _asyncFactoryToken(ct);
104113

@@ -116,17 +125,22 @@ private ValueTask<T> Create(CancellationToken ct)
116125

117126
private T CreateSync(CancellationToken ct)
118127
{
128+
if (_asyncFactoryTokenState is not null)
129+
return _asyncFactoryTokenState(_state!, ct).AwaitSync();
130+
119131
if (_syncFactoryToken is not null)
120132
return _syncFactoryToken(ct);
121133

122134
if (_syncFactory is not null)
123135
return _syncFactory();
124136

125137
if (_asyncFactoryToken is not null)
126-
return _asyncFactoryToken(ct).AwaitSync();
138+
return _asyncFactoryToken(ct)
139+
.AwaitSync();
127140

128141
if (_asyncFactory is not null)
129-
return _asyncFactory().AwaitSync();
142+
return _asyncFactory()
143+
.AwaitSync();
130144

131145
throw new InvalidOperationException("No initialization factory was configured.");
132146
}
@@ -147,7 +161,8 @@ public void Dispose()
147161

148162
// Prefer async disposal if supported (even in sync Dispose).
149163
if (local is IAsyncDisposable ad)
150-
ad.DisposeAsync().AwaitSync();
164+
ad.DisposeAsync()
165+
.AwaitSync();
151166
else if (local is IDisposable d)
152167
d.Dispose();
153168

@@ -161,18 +176,20 @@ public async ValueTask DisposeAsync()
161176

162177
object? local;
163178

164-
using (await _lock.Lock().NoSync())
179+
using (await _lock.Lock()
180+
.NoSync())
165181
{
166182
_hasValue.Value = false;
167183
local = _instance;
168184
_instance = null;
169185
}
170186

171187
if (local is IAsyncDisposable ad)
172-
await ad.DisposeAsync().NoSync();
188+
await ad.DisposeAsync()
189+
.NoSync();
173190
else if (local is IDisposable d)
174191
d.Dispose();
175192

176193
GC.SuppressFinalize(this);
177194
}
178-
}
195+
}

0 commit comments

Comments
 (0)