5
5
using System . Threading ;
6
6
using UnityEngine ;
7
7
using System . Collections . Specialized ;
8
-
8
+ using System . Threading . Tasks ;
9
+ using System . Collections . Concurrent ;
10
+ using System . IO ;
9
11
10
12
namespace UXF
11
13
{
@@ -62,6 +64,11 @@ public bool saveData
62
64
/// </summary>
63
65
public ResultsDictionary result ;
64
66
67
+ // Used by the worker task
68
+ private static BlockingQueue < System . Action > blockingQueue = new BlockingQueue < System . Action > ( ) ;
69
+ private static Task workerTask ;
70
+ private static bool quitting = false ;
71
+
65
72
/// <summary>
66
73
/// Manually create a trial. When doing this you need to add this trial to a block with block.trials.Add(trial)
67
74
/// </summary>
@@ -277,7 +284,12 @@ private void SaveData()
277
284
tracker . StopRecording ( ) ;
278
285
if ( tracker . Data . CountRows ( ) > 0 )
279
286
{
280
- SaveDataTable ( tracker . Data , tracker . DataName , dataType : UXFDataType . Trackers ) ;
287
+ UXFDataTable table = tracker . Data ;
288
+ string name = tracker . DataName ;
289
+ ManageInWorker ( ( ) =>
290
+ {
291
+ SaveDataTable ( table , name , dataType : UXFDataType . Trackers ) ;
292
+ } ) ;
281
293
}
282
294
}
283
295
catch ( NullReferenceException )
@@ -292,9 +304,68 @@ private void SaveData()
292
304
result [ s ] = settings . GetObject ( s , string . Empty ) ;
293
305
}
294
306
}
295
- }
296
307
297
-
308
+ /// <summary>
309
+ /// Adds a new command to a queue which is executed in a separate worker thread when it is available.
310
+ /// Warning: The Unity Engine API is not thread safe, so do not attempt to put any Unity commands here.
311
+ /// </summary>
312
+ /// <param name="action"></param>
313
+ public static void ManageInWorker ( System . Action action )
314
+ {
315
+ if ( workerTask == null )
316
+ {
317
+ workerTask = Task . Run ( Worker ) ;
318
+ quitting = false ;
319
+ }
320
+
321
+ blockingQueue . Enqueue ( action ) ;
322
+ }
323
+
324
+ /// <summary>
325
+ /// The worker thread used when <see cref="ManageInWorker"/> is called.
326
+ /// </summary>
327
+ private static void Worker ( )
328
+ {
329
+ // performs FileIO tasks in seperate thread
330
+ foreach ( var action in blockingQueue )
331
+ {
332
+ try
333
+ {
334
+ action . Invoke ( ) ;
335
+ }
336
+ catch ( ThreadAbortException )
337
+ {
338
+ break ;
339
+ }
340
+ catch ( IOException e )
341
+ {
342
+ Utilities . UXFDebugLogError ( string . Format ( "Error, file may be in use! Exception: {0}" , e ) ) ;
343
+ }
344
+ catch ( System . Exception e )
345
+ {
346
+ // stops thread aborting upon an exception
347
+ Debug . LogException ( e ) ;
348
+ }
349
+
350
+ if ( quitting && blockingQueue . NumItems ( ) == 0 )
351
+ {
352
+ break ;
353
+ }
354
+ }
355
+ }
356
+
357
+ /// <summary>
358
+ /// Wait for all tasks scheduled through <see cref="ManageInWorker"/>
359
+ /// </summary>
360
+ public static void WaitForTasks ( )
361
+ {
362
+ Utilities . UXFDebugLog ( "Waiting for tasks to finish" ) ;
363
+ quitting = true ;
364
+ blockingQueue . Enqueue ( ( ) => { } ) ; // ensures bq breaks from foreach loop
365
+ workerTask ? . Wait ( ) ;
366
+ Utilities . UXFDebugLog ( "Tasks finished" ) ;
367
+ }
368
+ }
298
369
299
370
/// <summary>
300
371
/// Status of a trial
@@ -307,4 +378,4 @@ public enum TrialStatus
307
378
}
308
379
309
380
310
- }
381
+ }
0 commit comments