-
-
Notifications
You must be signed in to change notification settings - Fork 78
Feature/prevent blocking start async #1303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
908bb41
4c588b5
19a5f14
c85ee7c
d576dc0
bad7612
b5e4716
9fdc72b
8d95e98
cd9f387
cf48fc3
3ab4e9d
d4dd69e
a1870e3
04b1843
0578074
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace NetDaemon.Runtime; | ||
|
||
/// <summary> | ||
/// The NetDaemon runtime interface. | ||
/// </summary> | ||
public interface INetDaemonRuntime : IAsyncDisposable | ||
{ | ||
/// <summary> | ||
/// Method that can be awaited to ensure that the runtime is fully started and initialized (initial connection is created and cache is initialized). | ||
/// </summary> | ||
Task WaitForInitializationAsync(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,11 +9,13 @@ | |
IServiceProvider serviceProvider, | ||
ILogger<NetDaemonRuntime> logger, | ||
ICacheManager cacheManager) | ||
: IRuntime | ||
: IRuntime, INetDaemonRuntime | ||
{ | ||
private const string Version = "local build"; | ||
private const int TimeoutInSeconds = 5; | ||
|
||
private readonly TaskCompletionSource _initializationTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); | ||
|
||
private readonly HomeAssistantSettings _haSettings = settings.Value; | ||
|
||
private IAppModelContext? _applicationModelContext; | ||
|
@@ -26,11 +28,9 @@ | |
internal IReadOnlyCollection<IApplication> ApplicationInstances => | ||
_applicationModelContext?.Applications ?? []; | ||
|
||
private readonly TaskCompletionSource _startedAndConnected = new(); | ||
|
||
private Task _runnerTask = Task.CompletedTask; | ||
|
||
public async Task StartAsync(CancellationToken stoppingToken) | ||
public void Start(CancellationToken stoppingToken) | ||
{ | ||
logger.LogInformation("Starting NetDaemon runtime version {Version}.", Version); | ||
|
||
|
@@ -46,6 +46,7 @@ | |
{ | ||
_runnerCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); | ||
|
||
// Assign the runner so we can dispose it later. Note that this task contains the connection loop and will not end. We don't want to await it. | ||
_runnerTask = homeAssistantRunner.RunAsync( | ||
_haSettings.Host, | ||
_haSettings.Port, | ||
|
@@ -56,19 +57,16 @@ | |
_runnerCancellationSource.Token); | ||
|
||
// make sure we cancel the task if the stoppingToken is cancelled | ||
stoppingToken.Register(() => | ||
{ | ||
_startedAndConnected.TrySetCanceled(); | ||
}); | ||
// Make sure we only return after the connection is made and initialization is ready | ||
await _startedAndConnected.Task; | ||
stoppingToken.Register(() => _initializationTcs.TrySetCanceled()); | ||
} | ||
catch (OperationCanceledException) | ||
{ | ||
// Ignore and just stop | ||
} | ||
} | ||
|
||
public Task WaitForInitializationAsync() => _initializationTcs.Task; | ||
|
||
private async Task OnHomeAssistantClientConnected( | ||
IHomeAssistantConnection haConnection, | ||
CancellationToken cancelToken) | ||
|
@@ -80,7 +78,7 @@ | |
if (_applicationModelContext is not null) | ||
{ | ||
// Something wrong with unloading and disposing apps on restart of HA, we need to prevent apps loading multiple times | ||
logger.LogWarning("Applications were not successfully disposed during restart, skippin loading apps again"); | ||
logger.LogWarning("Applications were not successfully disposed during restart, skipping loading apps again"); | ||
return; | ||
} | ||
|
||
|
@@ -90,21 +88,18 @@ | |
|
||
await LoadNewAppContextAsync(haConnection, cancelToken); | ||
|
||
_startedAndConnected.TrySetResult(); | ||
// Signal anyone waiting that the runtime is now initialized | ||
_initializationTcs.TrySetResult(); | ||
} | ||
catch (Exception ex) | ||
{ | ||
if (!_startedAndConnected.Task.IsCompleted) | ||
if (!_initializationTcs.Task.IsCompleted) | ||
{ | ||
// This means this was the first time we connected and StartAsync is still awaiting _startedAndConnected | ||
// By setting the exception on the task it will propagate up. | ||
_startedAndConnected.SetException(ex); | ||
} | ||
else | ||
{ | ||
// There is none waiting for this event handler to finish so we need to Log the exception here | ||
logger.LogCritical(ex, "Error re-initializing after reconnect to Home Assistant"); | ||
_initializationTcs.SetException(ex); | ||
} | ||
logger.LogCritical(ex, "Error (re-)initializing after connect to Home Assistant"); | ||
} | ||
} | ||
|
||
|
@@ -150,13 +145,14 @@ | |
{ | ||
logger.LogError(e, "Error disposing applications"); | ||
} | ||
IsConnected = false; | ||
|
||
if (reason == DisconnectReason.Unauthorized) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code was important since we needed the NetDaemon not to retry connection if it was unauthorized. It should just log and think it is connected. It is a hacky way of doing ti when I see it now but that feature is needed not to make user account locked etc. Unless you handle this somewhere else in the code and I fail to see it. |
||
{ | ||
// We will exit the runtime if the token is unauthorized to avoid hammering the server | ||
_startedAndConnected.SetResult(); | ||
logger.LogInformation("Home Assistant runtime will dispose itself to stop automatic retrying to prevent user from being locked out."); | ||
await DisposeAsync(); | ||
} | ||
|
||
IsConnected = false; | ||
} | ||
|
||
private async Task DisposeApplicationsAsync() | ||
|
Uh oh!
There was an error while loading. Please reload this page.