/// <summary> /// Runs code within a new loader context before firing any pending /// <see cref="DataLoader{TKey,TReturn}">DataLoader</see> instances. /// </summary> public static Task <T> Run <T>(Func <DataLoaderContext, Task <T> > func) { if (func == null) { throw new ArgumentNullException(nameof(func)); } // TODO: Investigate this. // // For some reason, using `Task.Run` causes <see cref="TaskCompletionSource{T}"/> to run continuations // synchronously, which prevents the main loop from continuing on to the next loader before they're done. // // I presume this is because once we're inside the ThreadPool, continuations will be scheduled using the // local queues (in LIFO order) instead of the global queue (which executes in FIFO order). This is really // a hack I think - the same thing should be accomplished using a custom TaskScheduler or custom awaiter. return(Task.Run <T>(() => { using (var scope = new DataLoaderScope()) { var task = func(scope.Context); if (task == null) { throw new InvalidOperationException("No task provided."); } return task; } })); }
/// <summary> /// Runs code within a new loader context before firing any pending /// <see cref="DataLoader{TKey,TReturn}">DataLoader</see> instances. /// </summary> public static Task Run(Func <DataLoaderContext, Task> func) { if (func == null) { throw new ArgumentNullException(nameof(func)); } // TODO: see above return(Task.Run(() => { using (var scope = new DataLoaderScope()) { var task = func(scope.Context); if (task == null) { throw new InvalidOperationException("No task provided."); } return task; } })); }