I have a headless UWP app that is supposed to get data from a sensor every 10 minutes and send it to the cloud. When I run the code from the headless app on the Raspberry Pi it stops measuring after 3 or 4 hours, no error (I have a lot of logs). It is exactly 3 or 4 hours. If I start the app at 8, at 11 or 12 it just stops...
It looks like it is stopped because I have cancellation token in place that worked well in tests, but here it is not firing anymore. On the App manager in the Device Portal it appears that the app is running.
I also noticed in the Performance page in the Device Portal that the memory goes down with about 8 MB during the measurements.
The strange thing is that I ran the same code from a headed app on the RPi and on a laptop and it went very well. It worked continuously for over 16 hours until I stopped it. On both the laptop and the RPi there was no memory issue, the app used the same amount of RAM over the whole period.
What could cause this behavior when running as a headless app?
Here is how I call the code from the headless app:
BackgroundTaskDeferral deferral;
private ISettingsReader settings;
private ILogger logger;
private IFlowManager<PalmSenseMeasurement> flow;
private IServiceProvider services;
IBackgroundTaskInstance myTaskInstance;
public async void Run(IBackgroundTaskInstance taskInstance)
{
taskInstance.Canceled += TaskInstance_Canceled;
deferral = taskInstance.GetDeferral();
myTaskInstance = taskInstance;
try
{
SetProperties();
var flowTask = flow.RunFlowAsync();
await flowTask;
}
catch (Exception ex)
{
logger.LogCritical("#####---->Exception occured in StartupTask (Run): {0}", ex.ToString());
}
}
private void SetProperties()
{
services = SensorHubContainer.Services;
settings = services.GetService<ISettingsReader>();
flow = services.GetService<IFlowManager<PalmSenseMeasurement>>();
logger = services.GetService<ILogger<StartupTask>>();
}
private void TaskInstance_Canceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
logger.LogDebug("StartupTask.TaskInstance_Canceled() - {0}", reason.ToString());
deferral.Complete();
}
And here is how I call the code from the headed app:
private async Task GetMeasurementsAsync()
{
try
{
flow = services.GetService<IFlowManager<PalmSenseMeasurement>>();
await flow.RunFlowAsync();
}
catch (Exception ex)
{
Measurements.Add(new MeasurementResult() { ErrorMessage = ex.Message });
}
}
The RunFlowAsync method looks like this:
public async Task RunFlowAsync()
{
var loopInterval = settings.NoOfSecondsForLoopInterval;
while (true)
{
try
{
logger.LogInformation("Starting a new loop in {0} seconds...", loopInterval);
//check for previous unsent files
await resender.TryResendMeasuresAsync();
await Task.Delay(TimeSpan.FromSeconds(loopInterval));
await DoMeasureAndSend();
logger.LogInformation("Loop finished");
}
catch (Exception ex)
{
logger.LogError("Error in Flow<{0}>! Error {1}", typeof(T).FullName, ex);
#if DEBUG
Debug.WriteLine(ex.ToString());
#endif
}
}
}
The problem was from a 3rd party library that I had to use and it had to be called differently from a headless app. Internally it was creating its own TaskScheduler if SynchronizationContext.Current was null.