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

DemoBytom / DemoEngine / 13230565482

09 Feb 2025 11:21PM UTC coverage: 10.29% (-0.2%) from 10.456%
13230565482

push

coveralls.net

DemoBytom
Further refactoring the main loop WIP

227 of 2206 relevant lines covered (10.29%)

22935.06 hits per line

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

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

4
using System.Collections.Concurrent;
5
using System.Reflection;
6
using System.Runtime.InteropServices;
7
using Demo.Engine.Core.Interfaces.Platform;
8
using Demo.Engine.Core.Interfaces.Rendering;
9
using Microsoft.Extensions.DependencyInjection;
10
using Microsoft.Extensions.Hosting;
11
using Microsoft.Extensions.Logging;
12

13
namespace Demo.Engine.Core.Services;
14

15
internal abstract class EngineServiceBaseNew(
×
16
    ILogger<EngineServiceBaseNew> logger,
×
17
    IHostApplicationLifetime hostApplicationLifetime,
×
18
    IServiceScopeFactory scopeFactory)
×
19
    : IHostedService,
20
      IDisposable
21
{
22
    protected readonly ILogger<EngineServiceBaseNew> _logger = logger;
×
23
    private readonly IHostApplicationLifetime _hostApplicationLifetime = hostApplicationLifetime;
×
24
    private readonly IServiceScopeFactory _scopeFactory = scopeFactory;
×
25
    private readonly string _serviceName = "Engine";
×
26
    private Task? _executingTask;
27
    private bool _stopRequested;
28
    protected IServiceProvider? _sp;
29
    private bool _disposedValue;
30

31
    protected bool IsRunning { get; private set; }
×
32

33
    private readonly string _version = Assembly
×
34
        .GetEntryAssembly()
×
35
        ?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
×
36
        ?.InformationalVersion ?? "0.0.0";
×
37

38
    public Task StartAsync(CancellationToken cancellationToken)
39
    {
40
        _logger.LogInformation("{serviceName} starting! v{version}", _serviceName, _version);
×
41
        _executingTask = DoWorkAsync();
×
42

43
        return _executingTask.IsCompleted
×
44
            ? _executingTask
×
45
            : Task.CompletedTask;
×
46
    }
47

48
    public async Task StopAsync(CancellationToken cancellationToken)
49
    {
50
        _logger.LogInformation("{serviceName} stopping!", _serviceName);
×
51

52
        _stopRequested = true;
×
53
        if (_executingTask is null)
×
54
        {
55
            return;
×
56
        }
57

58
        _ = await Task.WhenAny(
×
59
            _executingTask,
×
60
            Task.Delay(Timeout.Infinite, cancellationToken));
×
61
    }
×
62

63
    private async Task DoWorkAsync()
64
    {
65
        try
66
        {
67
            using var scope = _scopeFactory.CreateScope();
×
68
            _sp = scope.ServiceProvider;
×
69
            var osMessageHandler = _sp.GetRequiredService<IOSMessageHandler>();
×
70
            var renderingEngine = _sp.GetRequiredService<IRenderingEngine>();
×
71

72
            IsRunning = true;
×
73

74
            var executeAsync = RunDoAsync(
×
75
                renderingEngine);
×
76

77
            var runStaThread = RunSTAThread(
×
78
                renderingEngine,
×
79
                osMessageHandler);
×
80

81
            await Task.WhenAll(
×
82
                [
×
83
                    executeAsync,
×
84
                    runStaThread
×
85
                ]);
×
86

87
            _sp = null;
×
88
        }
×
89
        catch (OperationCanceledException)
×
90
        {
91
            IsRunning = false;
×
92
        }
×
93
        catch (Exception ex)
×
94
        {
95
            _logger.LogCritical(ex, "{serviceName} failed with error! {errorMessage}", _serviceName, ex.Message);
×
96
        }
×
97
        finally
98
        {
99
            if (!_stopRequested)
×
100
            {
101
                _hostApplicationLifetime.StopApplication();
×
102
            }
103
        }
104
    }
×
105

106
    private Task RunSTAThread(
107
        IRenderingEngine renderingEngine,
108
        IOSMessageHandler osMessageHandler)
109
    {
110
        var tcs = new TaskCompletionSource();
×
111
        var thread = new Thread(()
×
112
            =>
×
113
        {
×
114
            try
×
115
            {
×
116
                SingleThreadedSynchronizationContext.Await(async ()
×
117
                    => await STAThread(
×
118
                        renderingEngine: renderingEngine,
×
119
                        osMessageHandler: osMessageHandler,
×
120
                        cancellationToken: _hostApplicationLifetime.ApplicationStopping));
×
121

×
122
                IsRunning = false;
×
123
                tcs.SetResult();
×
124
            }
×
125
            catch (OperationCanceledException)
×
126
            {
×
127
                IsRunning = false;
×
128
                tcs.SetResult();
×
129
            }
×
130
            catch (Exception ex)
×
131
            {
×
132
                tcs.SetException(ex);
×
133
            }
×
134
        });
×
135
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
×
136
        {
137
            //Can only by set on the Windows machine. Doesn't work on Linux/MacOS
138
            thread.SetApartmentState(ApartmentState.STA);
×
139
        }
140

141
        thread.Start();
×
142

143
        return tcs.Task;
×
144
    }
145

146
    private async Task RunDoAsync(
147
        IRenderingEngine renderingEngine)
148
    {
149
        await DoAsync(
×
150
            renderingEngine: renderingEngine,
×
151
            cancellationToken: _hostApplicationLifetime.ApplicationStopping);
×
152
        IsRunning = false;
×
153
    }
×
154

155
    protected abstract Task STAThread(
156
        IRenderingEngine renderingEngine,
157
        IOSMessageHandler osMessageHandler,
158
        CancellationToken cancellationToken);
159

160
    protected abstract Task DoAsync(
161
        IRenderingEngine renderingEngine,
162
        CancellationToken cancellationToken);
163

164
    protected virtual void Dispose(bool disposing)
165
    {
166
        if (!_disposedValue)
×
167
        {
168
            if (disposing)
×
169
            {
170
                _hostApplicationLifetime.StopApplication();
×
171
            }
172

173
            _disposedValue = true;
×
174
        }
175
    }
×
176

177
    public void Dispose()
178
    {
179
        // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
180
        Dispose(disposing: true);
×
181
        GC.SuppressFinalize(this);
×
182
    }
×
183

184
    internal sealed class SingleThreadedSynchronizationContext
185
            : SynchronizationContext
186
    {
187
        private readonly BlockingCollection<(SendOrPostCallback d, object? state)> _queue = [];
×
188

189
        public override void Post(SendOrPostCallback d, object? state) => _queue.Add((d, state));
×
190

191
        public static void Await(
192
            Func<Task> taskInvoker)
193
        {
194
            var originalContext = Current;
×
195
            try
196
            {
197
                var context = new SingleThreadedSynchronizationContext();
×
198
                SetSynchronizationContext(context);
×
199

200
                var task = taskInvoker.Invoke();
×
201
                _ = task.ContinueWith(_ => context._queue.CompleteAdding());
×
202

203
                while (context._queue.TryTake(out var work, Timeout.Infinite))
×
204
                {
205
                    work.d.Invoke(work.state);
×
206
                }
×
207

208
                task.GetAwaiter().GetResult();
×
209
            }
×
210
            finally
211
            {
212
                SetSynchronizationContext(originalContext);
×
213
            }
×
214
        }
×
215
    }
216
}
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