• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DemoBytom / DemoEngine / 22501366615

27 Feb 2026 07:49PM UTC coverage: 29.406% (+0.09%) from 29.317%
22501366615

push

coveralls.net

web-flow
Async refactor - `StaThreadRequest` and `AsyncServiceScopes` (#512)

A warning was reported due to incorrectly disposing types that only implement `IAsyncDisposable`:
```
Demo.Engine Warning: 0 : AUTOFAC: A synchronous Dispose has been attempted, but the tracked object of type 'Demo.Engine.Core.Services.MainLoopService' only implements IAsyncDisposable. This will result in an inefficient blocking dispose. Consider either implementing IDisposable on 'Demo.Engine.Core.Services.MainLoopService' or disposing of the scope/container with DisposeAsync.
```

The solution was to use an `AsyncServiceScope` retreived via `_scopeFactory.CreateAsyncScope()` instead `IServiceScope` from `_scopeFactory.CreateScope()`.
This led to a bigger refactor as scopes were also created within `RenderingSurface` which in turn then required a `IAsyncDisposable` implementation. Changes in `RenderingSurface` required changes in `StaThreadRequests` and so on. Turtles all the way down 🐢🐢🐢
Currently all async methods are properly awaited and don't deadlock. But some `.ConfigureAwaits` might need to be added. This will be evaluated later.

Fixes #511

Co-authored-by: Michał Dembski <DemoBytom@users.noreply.github.com>

644 of 2190 relevant lines covered (29.41%)

0.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Demo.Engine.Core/Services/LoggingExtensions.cs
1
// Copyright © Michał Dembski and contributors.
2
// Distributed under MIT license. See LICENSE file in the root for more information.
3

4
using Demo.Engine.Core.Features.StaThread;
5
using Demo.Engine.Core.ValueObjects;
6
using Microsoft.Extensions.Logging;
7

8
namespace Demo.Engine.Core.Services;
9

10
// cannot be file scoped!
11
internal static partial class LoggingExtensions
12
{
13
    private const string SERVICE_IS_STARTING = "{ServiceName} starting! v{Version}";
14
    private const string SERVICE_IS_WORKING = "{ServiceName} working! v{Version}";
15
    private const string SERVICE_IS_STOPPING = "{ServiceName} stopping!";
16
    private const string SERVICE_FAILED_WITH_ERROR = "{ServiceName} failed with error! {ErrorMessage}";
17

18
    private const string AVERAGE_FPS = "{SurfaceId}: Avg. frame (ms): {Millisecond}, fps: {FPS}";
19
    private const string AVERAGE_UPS = "Avg. update (ms): {Millisecond}, ups: {UPS}";
20

21
    private const string MAIN_LOOP_RENDERING_SURFACE_NOT_FOUND = "Rendering surface {SurfaceId} not found!";
22
    private const string STA_THREAD_WAS_CANCELLED = "STA thread operation was canceled.";
23

24
    [LoggerMessage(
25
        Level = LogLevel.Debug,
26
        Message = SERVICE_IS_STARTING)]
27
    internal static partial void LogServiceIsStarting(
28
        this ILogger logger,
29
        string serviceName,
30
        string version);
31

32
    [LoggerMessage(
33
        Level = LogLevel.Debug,
34
        Message = SERVICE_IS_WORKING)]
35
    internal static partial void LogServiceIsWorking(
36
        this ILogger logger,
37
        string serviceName,
38
        string version);
39

40
    [LoggerMessage(
41
        Level = LogLevel.Debug,
42
        Message = SERVICE_IS_STOPPING)]
43
    internal static partial void LogServiceStopping(
44
        this ILogger logger,
45
        string serviceName);
46

47
    [LoggerMessage(
48
        Level = LogLevel.Critical,
49
        Message = SERVICE_FAILED_WITH_ERROR)]
50
    private static partial void LogServiceFailedWithError(
51
        this ILogger logger,
52
        Exception exception,
53
        string serviceName,
54
        string errorMessage);
55

56
    internal static void LogServiceFailedWithError(
57
        this ILogger logger,
58
        Exception exception,
59
        string serviceName)
60
        => logger.LogServiceFailedWithError(
×
61
            exception,
×
62
            serviceName,
×
63
            exception.Message);
×
64

65
    [LoggerMessage(
66
        Level = LogLevel.Trace,
67
        Message = AVERAGE_FPS)]
68
    internal static partial void LogAverageSurfaceFps(
69
        this ILogger logger,
70
        RenderingSurfaceId surfaceId,
71
        float millisecond,
72
        ulong fps);
73

74
    [LoggerMessage(
75
        Level = LogLevel.Trace,
76
        Message = AVERAGE_UPS)]
77
    internal static partial void LogAverageUps(
78
        this ILogger logger,
79
        float millisecond,
80
        ulong ups);
81

82
    internal static void LogMainLoopFailedWithError(
83
        this ILogger<MainLoopService> logger,
84
        Exception exception)
85
        => logger.LogServiceFailedWithError(
×
86
            exception,
×
87
            nameof(MainLoopService),
×
88
            exception.Message);
×
89

90
    [LoggerMessage(
91
        Level = LogLevel.Critical,
92
        Message = MAIN_LOOP_RENDERING_SURFACE_NOT_FOUND)]
93
    internal static partial void LogRenderingSurfaceNotFound(
94
        this ILogger<MainLoopService> logger,
95
        RenderingSurfaceId surfaceId);
96

97
    [LoggerMessage(
98
        Level = LogLevel.Debug,
99
        Message = STA_THREAD_WAS_CANCELLED)]
100
    internal static partial void LogStaThreadServiceWasCancelled(
101
        this ILogger<StaThreadService> logger);
102
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc