10
10
using SixLabors . ImageSharp ;
11
11
using SixLabors . ImageSharp . PixelFormats ;
12
12
using System ;
13
+ using System . Collections . Concurrent ;
13
14
using System . Collections . Generic ;
14
15
using System . Collections . ObjectModel ;
15
16
using System . IO ;
20
21
21
22
namespace OpenLoco . Gui . Models
22
23
{
23
- public class ObjectEditorModel
24
+ public class ObjectEditorModel : IDisposable
24
25
{
25
26
public EditorSettings Settings { get ; private set ; }
26
27
@@ -58,11 +59,15 @@ public class ObjectEditorModel
58
59
59
60
public HttpClient ? WebClient { get ; }
60
61
62
+ readonly ConcurrentQueue < string > logQueue = new ( ) ;
63
+ readonly SemaphoreSlim logFileLock = new ( 1 , 1 ) ; // Allow only 1 concurrent write
64
+
61
65
public ObjectEditorModel ( )
62
66
{
63
67
Logger = new Logger ( ) ;
64
68
LoggerObservableLogs = [ ] ;
65
69
Logger . LogAdded += ( sender , laea ) => Dispatcher . UIThread . Post ( ( ) => LoggerObservableLogs . Insert ( 0 , laea . Log ) ) ;
70
+ Logger . LogAdded += ( sender , laea ) => LogAsync ( laea . Log . ToString ( ) ) . ConfigureAwait ( false ) ;
66
71
67
72
LoadSettings ( ) ;
68
73
InitialiseDownloadDirectory ( ) ;
@@ -80,6 +85,43 @@ public ObjectEditorModel()
80
85
}
81
86
}
82
87
88
+ public async Task LogAsync ( string message )
89
+ {
90
+ logQueue . Enqueue ( message ) ;
91
+ await WriteLogsToFileAsync ( ) ; // Start the async writing process
92
+ }
93
+
94
+ async Task WriteLogsToFileAsync ( )
95
+ {
96
+ if ( logQueue . IsEmpty )
97
+ {
98
+ return ; // Nothing to write
99
+ }
100
+
101
+ if ( await logFileLock . WaitAsync ( 0 ) ) // Non-blocking wait if available.
102
+ {
103
+ try
104
+ {
105
+ while ( logQueue . TryDequeue ( out var logMessage ) )
106
+ {
107
+ try
108
+ {
109
+ await File . AppendAllTextAsync ( LoggingFile , logMessage + Environment . NewLine ) ;
110
+ }
111
+ catch ( Exception ex )
112
+ {
113
+ Console . WriteLine ( $ "Error writing to log file: { ex . Message } ") ;
114
+ // Consider logging to a separate error file or using a more robust logging framework
115
+ }
116
+ }
117
+ }
118
+ finally
119
+ {
120
+ _ = logFileLock . Release ( ) ; // Release the semaphore
121
+ }
122
+ }
123
+ }
124
+
83
125
void LoadSettings ( )
84
126
{
85
127
Settings = EditorSettings . Load ( SettingsFile , Logger ) ;
@@ -430,5 +472,34 @@ public async Task UploadDatToServer(ObjectIndexEntry dat)
430
472
await Client . UploadDatFileAsync ( WebClient , dat . Filename , await File . ReadAllBytesAsync ( filename ) , lastModifiedTime , Logger ) ;
431
473
await Task . Delay ( 100 ) ; // wait 100ms, ie don't DoS the server
432
474
}
475
+
476
+ public async Task CloseAsync ( )
477
+ {
478
+ // Wait for any pending writes to complete.
479
+ await logFileLock . WaitAsync ( ) ; // Acquire the semaphore
480
+ _ = logFileLock . Release ( ) ; // Release it immediately after. This is just to wait.
481
+
482
+ // Process any remaining logs in the queue.
483
+ await WriteLogsToFileAsync ( ) ; // One last flush.
484
+
485
+ logFileLock . Dispose ( ) ; // Dispose of the semaphore
486
+ }
487
+
488
+ public void Dispose ( )
489
+ {
490
+ Dispose ( true ) ;
491
+ GC . SuppressFinalize ( this ) ; // Important for proper disposal
492
+ }
493
+
494
+ protected virtual void Dispose ( bool disposing )
495
+ {
496
+ if ( disposing )
497
+ {
498
+ // Wait for logging to complete synchronously.
499
+ Task . Run ( CloseAsync ) . Wait ( ) ; // <--- Key change
500
+ logFileLock ? . Dispose ( ) ; // Dispose of the semaphore
501
+ }
502
+ }
503
+
433
504
}
434
505
}
0 commit comments