diff --git a/DHCPServer/Library/DHCPServer.cs b/DHCPServer/Library/DHCPServer.cs index 1c12de0..fa662cf 100644 --- a/DHCPServer/Library/DHCPServer.cs +++ b/DHCPServer/Library/DHCPServer.cs @@ -14,6 +14,7 @@ public class DHCPServer : IDHCPServer { private const int ClientInformationWriteRetries = 10; + private readonly SemaphoreSlim _taskSemaphoreSlim = new(1, 1); private IPEndPoint _endPoint = new(IPAddress.Loopback, 67); private IUDPSocket _socket = default!; private IPAddress _subnetMask = IPAddress.Any; @@ -34,7 +35,8 @@ public class DHCPServer : IDHCPServer private readonly AutoPumpQueue _updateClientInfoQueue; private readonly Random _random = new(); private CancellationTokenSource _cancellationTokenSource = new(); - private Task? _mainTask; + private Task? _receiveTask; + private Task? _timerTask; #region IDHCPServer Members @@ -235,7 +237,7 @@ public DHCPServer(ILogger logger, string? clientInfoPath, IUDPSocketFactory udpS _logger = logger; _clientInfoPath = clientInfoPath; _udpSocketFactory = udpSocketFactory; - _hostName = System.Environment.MachineName; + _hostName = Environment.MachineName; } public void Start() @@ -262,7 +264,8 @@ public void Start() Trace($"Starting DHCP server '{_endPoint}'"); _active = true; _socket = _udpSocketFactory.Create(_endPoint, 2048, true, 10); - _mainTask = Task.Run(async () => { await MainTask(_cancellationTokenSource.Token); }); + _receiveTask = Task.Run(async () => await ReceiveTask(_cancellationTokenSource.Token)); + _timerTask = Task.Run(async () => await TimerTask(_cancellationTokenSource.Token)); Trace("DHCP Server start succeeded"); } catch(Exception e) @@ -339,21 +342,29 @@ private void Stop(Exception? reason) notify = true; _cancellationTokenSource.Cancel(); - if(_mainTask!= null) + AwaitTask(_receiveTask); + AwaitTask(_timerTask); + + _receiveTask = null; + _timerTask = null; + + _socket.Dispose(); + Trace("Stopped"); + + void AwaitTask(Task task) { - try + if(task != null) { - _mainTask.GetAwaiter().GetResult(); - } - catch(Exception ex) - { - _logger?.LogError(ex, $"Exception during {nameof(Stop)}"); + try + { + task.GetAwaiter().GetResult(); + } + catch(Exception ex) + { + _logger?.LogError(ex, $"Exception during {nameof(Stop)}"); + } } - _mainTask = null; } - - _socket.Dispose(); - Trace("Stopped"); } if(notify) @@ -825,57 +836,76 @@ private async Task OfferClient(DHCPMessage dhcpMessage, DHCPClient client) await SendOFFER(dhcpMessage, client.IPAddress, _leaseTime); } - private async Task MainTask(CancellationToken cancellationToken) + private async Task ReceiveTask(CancellationToken cancellationToken) { - var timer = new PeriodicTimer(TimeSpan.FromSeconds(1)); - Task<(IPEndPoint,ReadOnlyMemory)>? receiveTask = default; - Task? timerTask = default; - while(!cancellationToken.IsCancellationRequested) { - receiveTask ??= _socket.Receive(cancellationToken); - timerTask ??= timer.WaitForNextTickAsync(cancellationToken).AsTask(); - - var completedTask = await Task.WhenAny(receiveTask, timerTask); + bool enteredSemaphore = false; - if(completedTask == receiveTask) + try { - try - { - (var ipEndPoint, var data) = await receiveTask; - await OnReceive(ipEndPoint, data); - } - catch(UDPSocketException ex) - { - if(ex.IsFatal) - { - _logger?.LogError(ex, $"fatal exception in {nameof(MainTask)}"); - throw; - } - } - finally + (IPEndPoint ipEndPoint, ReadOnlyMemory data) = await _socket.Receive(cancellationToken); + await _taskSemaphoreSlim.WaitAsync(cancellationToken); + enteredSemaphore = true; + await OnReceive(ipEndPoint, data); + } + catch(UDPSocketException ex) + { + if(ex.IsFatal) { - receiveTask = null; + _logger?.LogError(ex, $"fatal exception in {nameof(ReceiveTask)}"); + return; } } - else if(completedTask == timerTask) + catch + { + // Ignored. + } + finally + { + if(enteredSemaphore) + _taskSemaphoreSlim.Release(); + } + } + } + + private async Task TimerTask(CancellationToken cancellationToken) + { + PeriodicTimer timer = new(TimeSpan.FromSeconds(1)); + + try + { + while(!cancellationToken.IsCancellationRequested) { + bool enteredSemaphore = false; + try { - await timerTask; + await timer.WaitForNextTickAsync(cancellationToken); + await _taskSemaphoreSlim.WaitAsync(cancellationToken); + enteredSemaphore = true; CheckLeaseExpiration(); } finally { - timerTask = null; + if(enteredSemaphore) + _taskSemaphoreSlim.Release(); } } } + catch + { + // Ignored. + } + finally + { + timer.Dispose(); + } } private DHCPClient? GetKnownClient(DHCPClient client) { - return _clients.TryGetValue(client, out var value) ? value : null; + return _clients.GetValueOrDefault(client); } private async Task OnReceive(IPEndPoint endPoint, ReadOnlyMemory data) @@ -1050,7 +1080,7 @@ private async Task OnReceive(IPEndPoint endPoint, ReadOnlyMemory data) client.State = DHCPClient.TState.Assigned; client.LeaseStartTime = DateTime.Now; client.LeaseDuration = _leaseTime; - _=_clients.TryAdd(client, client); + _ = _clients.TryAdd(client, client); await SendACK(dhcpMessage, dhcpMessage.ClientIPAddress, client.LeaseDuration); } else