private void AssertUpgradeableLockUpgradeContended(AsyncUpgradeableReaderWriterLock rwLock) { using (CancellationTokenSource timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(200))) using (rwLock.EnterUpgradeableReadLockAsync(timeout.Token).ConfigureAwait(false).GetAwaiter().GetResult()) { using (CancellationTokenSource upgradeTimeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(200))) Assert.Throws <TaskCanceledException>(() => rwLock.UpgradeToWriteLock(timeout.Token).ConfigureAwait(false).GetAwaiter().GetResult()); } }
private Task HoldUpgradedLockAsync(AsyncUpgradeableReaderWriterLock rwLock, ManualResetEventSlim setWhenLockHeld, CancellationToken cancellationToken) { return(Task.Run(() => { using (rwLock.EnterUpgradeableReadLockAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()) { using (rwLock.UpgradeToWriteLock(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult()) { setWhenLockHeld.Set(); cancellationToken.WaitHandle.WaitOne(); } } })); }
private async Task ReloadTrainingDataHighAvailabilityAsync(bool finalize, CancellationToken cancellationToken) { using (var trainingDataUpgradeableLock = await m_trainingDataLockAsync.EnterUpgradeableReadLockAsync(cancellationToken).ConfigureAwait(false)) { Logging.Log.Info("Reloading training data and retraining rec sources. Rec sources will remain available."); Logging.Log.InfoFormat("Memory use: {0} bytes", GC.GetTotalMemory(forceFullCollection: false)); Stopwatch totalTimer = Stopwatch.StartNew(); // Load new training data MalTrainingData newData; IDictionary <int, string> newUsernames; IDictionary <int, IList <int> > newPrereqs; using (IMalTrainingDataLoader malTrainingDataLoader = m_trainingDataLoaderFactory.GetTrainingDataLoader()) using (CancellationTokenSource faultCanceler = new CancellationTokenSource()) using (CancellationTokenSource faultOrUserCancel = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, faultCanceler.Token)) { Stopwatch trainingDataTimer = Stopwatch.StartNew(); CancellableTask <MalTrainingData> trainingDataTask = new CancellableTask <MalTrainingData>( malTrainingDataLoader.LoadMalTrainingDataAsync(faultOrUserCancel.Token), faultCanceler); Task trainingDataTimerTask = trainingDataTask.Task.ContinueWith(task => { trainingDataTimer.Stop(); Logging.Log.InfoFormat("Training data loaded. {0} users, {1} animes, {2} entries. Took {3}.", task.Result.Users.Count, task.Result.Animes.Count, task.Result.Users.Keys.Sum(userId => task.Result.Users[userId].Entries.Count), trainingDataTimer.Elapsed); }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted, TaskScheduler.Current); Stopwatch prereqsTimer = Stopwatch.StartNew(); CancellableTask <IDictionary <int, IList <int> > > prereqsTask = new CancellableTask <IDictionary <int, IList <int> > >( malTrainingDataLoader.LoadPrerequisitesAsync(faultOrUserCancel.Token), faultCanceler); Task prereqsTimerTask = prereqsTask.Task.ContinueWith(task => { prereqsTimer.Stop(); int numPrereqs = task.Result.Values.Sum(prereqList => prereqList.Count); Logging.Log.InfoFormat("Prerequisites loaded. {0} prerequisites for {1} animes. Took {2}.", numPrereqs, task.Result.Count, prereqsTimer.Elapsed); }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted, TaskScheduler.Current); await AsyncUtils.WhenAllCancelOnFirstExceptionDontWaitForCancellations(trainingDataTask, prereqsTask); newData = trainingDataTask.Task.Result; newUsernames = GetUsernamesFromTrainingData(newData); newPrereqs = prereqsTask.Task.Result; await trainingDataTimerTask.ConfigureAwait(false); await prereqsTimerTask.ConfigureAwait(false); } GC.Collect(); Logging.Log.InfoFormat("Memory use: {0} bytes", GC.GetTotalMemory(forceFullCollection: false)); using (var recSourcesUpgradeableLock = await m_recSourcesLockAsync.EnterUpgradeableReadLockAsync(cancellationToken).ConfigureAwait(false)) { // clone the json rec sources without the training state and train each one with the new data. Dictionary <string, ITrainableJsonRecSource> newRecSources = new Dictionary <string, ITrainableJsonRecSource>(StringComparer.OrdinalIgnoreCase); Dictionary <string, Func <ITrainableJsonRecSource> > newRecSourceFactories = new Dictionary <string, Func <ITrainableJsonRecSource> >(m_recSourceFactories, StringComparer.OrdinalIgnoreCase); if (m_recSourceFactories.Count == 0) { Logging.Log.Info("No rec sources to retrain."); } else { Logging.Log.Info("Retraining rec sources."); object newRecSourcesLockAndMemFence = new object(); List <Task> recSourceTrainTasksList = new List <Task>(); // ToList() so we can unload a rec source as we iterate if it errors while training. foreach (string recSourceNameLoopVar in m_recSourceFactories.Keys.ToList()) { string recSourceName = recSourceNameLoopVar; // avoid capturing the loop var ITrainableJsonRecSource recSource = newRecSourceFactories[recSourceName](); Task recSourceTrainTask = Task.Run(() => { Logging.Log.InfoFormat("Retraining rec source {0} ({1}).", recSourceName, recSource); Stopwatch trainTimer = Stopwatch.StartNew(); try { recSource.Train(newData, newUsernames, cancellationToken); trainTimer.Stop(); Logging.Log.InfoFormat("Trained rec source {0} ({1}). Took {2}.", recSourceName, recSource, trainTimer.Elapsed); lock (newRecSourcesLockAndMemFence) { newRecSources[recSourceName] = recSource; } } catch (OperationCanceledException) { Logging.Log.InfoFormat("Canceled while retraining rec source {0} ({1}).", recSourceName, recSource); throw; } catch (Exception ex) { Logging.Log.ErrorFormat("Error retraining rec source {0} ({1}): {2} Unloading it.", ex, recSourceName, recSource, ex.Message); lock (newRecSourcesLockAndMemFence) { newRecSourceFactories.Remove(recSourceName); } } }, cancellationToken); recSourceTrainTasksList.Add(recSourceTrainTask); } // Wait for all to complete or cancellation. There should not be any exceptions other than OperationCanceledException. await Task.WhenAll(recSourceTrainTasksList); lock (newRecSourcesLockAndMemFence) { ; // just for the fence } } // Swap in the newly trained rec sources. using (var trainingDataWriteLock = await m_trainingDataLockAsync.UpgradeToWriteLock(cancellationToken).ConfigureAwait(false)) using (var recSourcesWriteLock = await m_recSourcesLockAsync.UpgradeToWriteLock(cancellationToken).ConfigureAwait(false)) { m_recSources = newRecSources; m_recSourceFactories = newRecSourceFactories; m_animes = newData.Animes; m_prereqs = newPrereqs; if (finalize) { m_trainingData = null; m_usernames = null; m_finalized = true; Logging.Log.Info("Finalized rec sources."); } else { m_trainingData = newData; m_usernames = newUsernames; m_finalized = false; } } } totalTimer.Stop(); Logging.Log.InfoFormat("All rec sources retrained with the latest data. Total time: {0}", totalTimer.Elapsed); } GC.Collect(); Logging.Log.InfoFormat("Memory use: {0} bytes", GC.GetTotalMemory(forceFullCollection: false)); }