Skip to content

Commit

Permalink
修复一个潜在的锁死问题。
Browse files Browse the repository at this point in the history
  • Loading branch information
Codespilot committed Dec 23, 2023
1 parent e30ffc0 commit 267f94e
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 6 deletions.
5 changes: 3 additions & 2 deletions Source/Starfish.Client/StarfishConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ public class StarfishConfigurationProvider : ConfigurationProvider, IDisposable

private readonly string _cacheFile;

private static readonly CountdownEvent _waitHandle = new(1);
private readonly CountdownEvent _waitHandle = new(1);

private readonly IEnumerator<string> _hosts;
private static readonly char[] _separator = [',', ';'];
private readonly char[] _separator = [',', ';'];

public StarfishConfigurationProvider(ConfigurationClientOptions options)
{
Expand Down Expand Up @@ -112,6 +112,7 @@ private static string Decompress(byte[] data, int count)

public void Dispose()
{
_waitHandle.Dispose();
HostChanged -= OnHostChanged;
GC.SuppressFinalize(this);
}
Expand Down
41 changes: 38 additions & 3 deletions Source/Starfish.Redis/RedisConfigurationClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,27 @@

namespace Microsoft.Extensions.Configuration.Redis;

/// <summary>
/// The Redis configuration client.
/// </summary>
internal class RedisConfigurationClient : IAsyncDisposable
{
private readonly int _database;
private readonly string _key;
private readonly bool _keyspaceEnabled;

private readonly SemaphoreSlim _semaphoreSlim = new(0, 1);
private readonly CountdownEvent _waitHandle = new(1);

private Timer _timer;
private ISubscriber _subscriber;

/// <summary>
/// Initializes a new instance of the <see cref="RedisConfigurationClient"/> class.
/// </summary>
/// <param name="connectionString"></param>
/// <param name="database"></param>
/// <param name="key"></param>
/// <param name="keyspaceEnabled"></param>
public RedisConfigurationClient(string connectionString, int database, string key, bool keyspaceEnabled)
{
_database = database;
Expand All @@ -22,13 +32,23 @@ public RedisConfigurationClient(string connectionString, int database, string ke
Connect(connectionString);
}

/// <summary>
/// Gets or sets the Redis connection.
/// </summary>
private ConnectionMultiplexer Connection { get; set; }

/// <summary>
/// Gets or sets the cancellation source.
/// </summary>
private CancellationTokenSource CancellationSource { get; set; }

/// <summary>
/// Loads the configuration from Redis.
/// </summary>
/// <returns></returns>
public async Task<Dictionary<string, string>> LoadAsync()
{
await _semaphoreSlim.WaitAsync();
_waitHandle.Wait(TimeSpan.FromSeconds(30));

if (Connection == null)
{
Expand All @@ -43,6 +63,10 @@ public async Task<Dictionary<string, string>> LoadAsync()
});
}

/// <summary>
/// Connects to Redis.
/// </summary>
/// <param name="connectionString"></param>
private async void Connect(string connectionString)
{
try
Expand Down Expand Up @@ -70,10 +94,15 @@ await _subscriber.SubscribeAsync(channel, (_, _) =>
}
finally
{
_semaphoreSlim.Release();
_waitHandle.Signal();
}
}

/// <summary>
/// Reads the Redis value.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static string ReadRedisValue(RedisValue value)
{
if (value.IsNull)
Expand All @@ -86,6 +115,8 @@ private static string ReadRedisValue(RedisValue value)

public async ValueTask DisposeAsync()
{
_waitHandle.Dispose();

if (Connection != null)
{
await Connection.CloseAsync();
Expand All @@ -106,6 +137,10 @@ public async ValueTask DisposeAsync()
CancellationSource?.Dispose();
}

/// <summary>
/// Watches the Redis key changes.
/// </summary>
/// <returns></returns>
public IChangeToken Watch()
{
CancellationSource?.Dispose();
Expand Down
8 changes: 7 additions & 1 deletion Source/Starfish.Redis/RedisConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
namespace Microsoft.Extensions.Configuration.Redis;

/// <summary>
///
/// The configuration provider using Redis.
/// </summary>
public sealed class RedisConfigurationProvider : ConfigurationProvider, IAsyncDisposable
{
private readonly RedisConfigurationClient _client;

private readonly IDisposable _changeToken;

/// <summary>
/// Initializes a new instance of the <see cref="RedisConfigurationProvider"/> class.
/// </summary>
/// <param name="source"></param>
public RedisConfigurationProvider(RedisConfigurationSource source)
{
_client = new RedisConfigurationClient(source.ConnectionString, source.Database, source.Key, source.KeyspaceEnabled);
Expand All @@ -21,13 +25,15 @@ public RedisConfigurationProvider(RedisConfigurationSource source)
}
}

/// <inheritdoc />
public override void Load()
{
Data = _client.LoadAsync()
.GetAwaiter()
.GetResult();
}

/// <inheritdoc />
public async ValueTask DisposeAsync()
{
_changeToken?.Dispose();
Expand Down

0 comments on commit 267f94e

Please sign in to comment.