Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed BackgroundWorkers to async await #728

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
8 changes: 8 additions & 0 deletions Glyssen/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ private void SetProject(Project project)
}

private void FinishSetProjectIfReady(object sender, EventArgs e)
{
if (InvokeRequired)
Invoke(new Action(FinishSetProjectIfReady));
else
FinishSetProjectIfReady();
}

private void FinishSetProjectIfReady()
{
if (m_project != null && (m_project.ProjectState & ProjectState.ReadyForUserInteraction) > 0)
FinishSetProject();
Expand Down
167 changes: 71 additions & 96 deletions GlyssenEngine/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public Project(GlyssenBundle bundle, string recordingProjectName = null, Project
PopulateAndParseBooks(bundle);
}

public Project(ParatextScrTextWrapper paratextProject) :
public Project(ParatextScrTextWrapper paratextProject, Action<Exception> exceptionHandler = null) :
this(paratextProject.GlyssenDblTextMetadata, null, false, paratextProject.WritingSystem)
{
Writer.SetUpProjectPersistence(this);
Expand All @@ -204,7 +204,7 @@ public Project(ParatextScrTextWrapper paratextProject) :
SetWsQuotationMarksUsingFullySpecifiedContinuers(paratextProject.QuotationMarks);
}

ParseAndSetBooks(paratextProject.UsxDocumentsForIncludedBooks, paratextProject.Stylesheet);
ParseAndSetBooks(paratextProject.UsxDocumentsForIncludedBooks, paratextProject.Stylesheet, exceptionHandler);
}

/// <summary>
Expand Down Expand Up @@ -328,7 +328,7 @@ public void SetQuoteSystem(QuoteSystemStatus status, QuoteSystem system)
if (IsQuoteSystemReadyForParse && ProjectState == ProjectState.NeedsQuoteSystemConfirmation)
{
m_quoteSystem = system;
DoQuoteParse();
DoQuoteParseAsync();
}
else if ((quoteSystemChanged && !quoteSystemBeingSetForFirstTime) ||
(QuoteSystemStatus == QuoteSystemStatus.Reviewed &&
Expand Down Expand Up @@ -1344,7 +1344,7 @@ private void InitializeLoadedProject()
m_usxPercentComplete = 100;
if (QuoteSystem == null)
{
GuessAtQuoteSystem();
GuessAtQuoteSystemAsync();
UpdateControlFileVersion();
return;
}
Expand Down Expand Up @@ -1440,7 +1440,7 @@ public void IncludeExistingBook(BookScript book)
m_books.Insert(i, book);
}

public void IncludeBooksFromParatext(ParatextScrTextWrapper wrapper, ISet<int> bookNumbers, Action<BookScript> postParseAction)
public async Task IncludeBooksFromParatext(ParatextScrTextWrapper wrapper, ISet<int> bookNumbers, Action<BookScript> postParseAction)
{
wrapper.IncludeBooks(bookNumbers.Select(BCVRef.NumberToBookCode));
var usxBookInfoList = wrapper.GetUsxDocumentsForIncludedParatextBooks(bookNumbers);
Expand All @@ -1453,65 +1453,72 @@ void EnhancedPostParseAction(BookScript book)
postParseAction?.Invoke(book);
}

ParseAndIncludeBooks(usxBookInfoList, wrapper.Stylesheet, EnhancedPostParseAction);
await ParseAndIncludeBooks(usxBookInfoList, wrapper.Stylesheet, null, EnhancedPostParseAction);
}

private void ParseAndSetBooks(IEnumerable<UsxDocument> books, IStylesheet stylesheet)
private async Task ParseAndSetBooks(IEnumerable<UsxDocument> books, IStylesheet stylesheet, Action<Exception> exceptionHandler = null)
{
if (m_books.Any())
throw new InvalidOperationException("Project already contains books. If the intention is to replace the existing ones, let's clear the list first. Otherwise, call ParseAndIncludeBooks.");
ParseAndIncludeBooks(books, stylesheet);
await ParseAndIncludeBooks(books, stylesheet, exceptionHandler);
}

private void ParseAndIncludeBooks(IEnumerable<UsxDocument> books, IStylesheet stylesheet, Action<BookScript> postParseAction = null)
private async Task ParseAndIncludeBooks(IEnumerable<UsxDocument> books, IStylesheet stylesheet,
Action<Exception> exceptionHandler/* = null*/, Action<BookScript> postParseAction = null)
{
if (Versification == null)
throw new NullReferenceException("What!!!");
ProjectState = ProjectState.Initial | (ProjectState & ProjectState.WritingSystemRecoveryInProcess);
var usxWorker = new BackgroundWorker {WorkerReportsProgress = true};
usxWorker.DoWork += UsxWorker_DoWork;
usxWorker.RunWorkerCompleted += UsxWorker_RunWorkerCompleted;
usxWorker.ProgressChanged += UsxWorker_ProgressChanged;
List<BookScript> parsedBooks = null;
try
{
await Task.Run(() =>
{
parsedBooks = UsxParse(books, stylesheet, postParseAction);
});

object[] parameters = {books, stylesheet, postParseAction};
usxWorker.RunWorkerAsync(parameters);
await Task.Run(() =>
{
if (QuoteSystem == null)
GuessAtQuoteSystem();
else if (IsQuoteSystemReadyForParse)
DoQuoteParse(parsedBooks.Select(b => b.BookId));
});
}
catch (Exception e)
{
Console.WriteLine(e);
if (exceptionHandler == null)
ErrorReport.ReportFatalException(e);
else
exceptionHandler(e);
}
}

private void UsxWorker_DoWork(object sender, DoWorkEventArgs e)
private List<BookScript> UsxParse(IEnumerable<UsxDocument> books, IStylesheet stylesheet, Action<BookScript> postParseAction = null)
{
var parameters = (object[])e.Argument;
var books = (IEnumerable<UsxDocument>)parameters[0];
var stylesheet = (IStylesheet)parameters[1];
var postParseAction = parameters.Length > 2 ? (Action<BookScript>)parameters[2] : null;

var backgroundWorker = (BackgroundWorker)sender;

var parsedBooks = UsxParser.ParseBooks(books, stylesheet, i => backgroundWorker.ReportProgress(i));
var parsedBooks = UsxParser.ParseBooks(books, stylesheet, i =>
{
m_usxPercentComplete = i;
var pe = new ProgressChangedEventArgs(PercentInitialized, null);
OnReport(pe);
});

if (postParseAction != null)
{
foreach (var book in parsedBooks)
postParseAction(book);
}
e.Result = parsedBooks;
}

private void UsxWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
throw e.Error;

var bookScripts = (List<BookScript>)e.Result;

foreach (var bookScript in bookScripts)
foreach (var bookScript in parsedBooks)
{
// This code is an attempt to figure out how we are getting null reference exceptions when using the objects in the list (See PG-275 & PG-287)
if (bookScript?.BookId == null)
{
var nonNullBookScripts = bookScripts.Where(b => b != null).Select(b => b.BookId);
var nonNullBookScripts = parsedBooks.Where(b => b != null).Select(b => b.BookId);
var nonNullBookScriptsStr = Join(";", nonNullBookScripts);
var initialMessage = bookScript == null ? "BookScript is null." : "BookScript has null BookId.";
throw new ApplicationException($"{initialMessage} Number of BookScripts: {bookScripts.Count}. " +
throw new ApplicationException($"{initialMessage} Number of BookScripts: {parsedBooks.Count}. " +
$"BookScripts which are NOT null: {nonNullBookScriptsStr}");
}

Expand All @@ -1520,12 +1527,12 @@ private void UsxWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEvent

if (m_books.Any())
{
foreach (var book in bookScripts)
foreach (var book in parsedBooks)
IncludeExistingBook(book);
}
else
{
m_books.AddRange(bookScripts);
m_books.AddRange(parsedBooks);
m_projectMetadata.ParserVersion = kParserVersion;
if (m_books.All(b => IsNullOrEmpty(b.PageHeader)))
ChapterAnnouncementStyle = ChapterAnnouncement.ChapterLabel;
Expand All @@ -1534,80 +1541,55 @@ private void UsxWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEvent
AddMissingAvailableBooks();
}

if (QuoteSystem == null)
GuessAtQuoteSystem();
else if (IsQuoteSystemReadyForParse)
DoQuoteParse(bookScripts.Select(b => b.BookId));
return parsedBooks;
}

private void UsxWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
private async Task GuessAtQuoteSystemAsync()
{
m_usxPercentComplete = e.ProgressPercentage;
var pe = new ProgressChangedEventArgs(PercentInitialized, null);
OnReport(pe);
await Task.Run(GuessAtQuoteSystem);
}

private void GuessAtQuoteSystem()
{
ProjectState = ProjectState.UsxComplete | (ProjectState & ProjectState.WritingSystemRecoveryInProcess);
var guessWorker = new BackgroundWorker {WorkerReportsProgress = true};
guessWorker.DoWork += GuessWorker_DoWork;
guessWorker.RunWorkerCompleted += GuessWorker_RunWorkerCompleted;
guessWorker.ProgressChanged += GuessWorker_ProgressChanged;
guessWorker.RunWorkerAsync();
}

private void GuessWorker_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = QuoteSystemGuesser.Guess(ControlCharacterVerseData.Singleton, m_books, Versification, out _,
sender as BackgroundWorker);
}

private void GuessWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
throw e.Error;

SetQuoteSystem(QuoteSystemStatus.Guessed, (QuoteSystem)e.Result);
var quoteSystem = QuoteSystemGuesser.Guess(ControlCharacterVerseData.Singleton, m_books, Versification, out _,
i =>
{
m_guessPercentComplete = i;
var pe = new ProgressChangedEventArgs(PercentInitialized, null);
OnReport(pe);
});

SetQuoteSystem(QuoteSystemStatus.Guessed, quoteSystem);
Save();
}

private void GuessWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
private async Task DoQuoteParseAsync(IEnumerable<string> booksToParse = null)
{
m_guessPercentComplete = e.ProgressPercentage;
var pe = new ProgressChangedEventArgs(PercentInitialized, null);
OnReport(pe);
await Task.Run(() => { DoQuoteParse(booksToParse);});
}

private void DoQuoteParse(IEnumerable<string> booksToParse = null)
private void DoQuoteParse(IEnumerable<string> bookIds)
{
m_projectMetadata.ParserVersion = kParserVersion;
ProjectState = ProjectState.Parsing;
var quoteWorker = new BackgroundWorker {WorkerReportsProgress = true};
quoteWorker.DoWork += QuoteWorker_DoWork;
quoteWorker.RunWorkerCompleted += QuoteWorker_RunWorkerCompleted;
quoteWorker.ProgressChanged += QuoteWorker_ProgressChanged;
object[] parameters = {booksToParse};
quoteWorker.RunWorkerAsync(parameters);
}

private void QuoteWorker_DoWork(object sender, DoWorkEventArgs e)
{
var bookIds = (IEnumerable<string>)((object[])e.Argument)[0];
QuoteParser.ParseProject(this, sender as BackgroundWorker, bookIds);
}

private void QuoteWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
try
{
QuoteParser.ParseProject(this, i =>
{
m_quotePercentComplete = i;
var pe = new ProgressChangedEventArgs(PercentInitialized, null);
OnReport(pe);
}, bookIds);
}
catch (Exception e)
{
#if DEBUG
Exception innerException;
if ((innerException = e.Error?.InnerException) != null)
if ((innerException = e.InnerException) != null)
Debug.WriteLine(innerException.Message + innerException.StackTrace);
#endif
throw e.Error;
throw;
}

ProjectState = ProjectState.QuoteParseComplete;
Expand All @@ -1628,13 +1610,6 @@ private void QuoteWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEve
QuoteParseCompleted?.Invoke(this, new EventArgs());
}

private void QuoteWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
m_quotePercentComplete = e.ProgressPercentage;
var pe = new ProgressChangedEventArgs(PercentInitialized, null);
OnReport(pe);
}

public int MaxProjectNameLength => Writer.GetMaxProjectNameLength(this);

public BookScript LoadExistingBookIfPossible(string bookId)
Expand Down
7 changes: 3 additions & 4 deletions GlyssenEngine/Quote/QuoteParser.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
Expand All @@ -21,7 +20,7 @@ namespace GlyssenEngine.Quote
{
public class QuoteParser
{
public static void ParseProject(Project project, BackgroundWorker projectWorker, IEnumerable<string> bookIdsToParse)
public static void ParseProject(Project project, Action<int> reportProgress, IEnumerable<string> bookIdsToParse)
{
var cvInfo = new ParserCharacterRepository(new CombinedCharacterVerseData(project), project.ReferenceText);

Expand All @@ -43,10 +42,10 @@ public static void ParseProject(Project project, BackgroundWorker projectWorker,
{
book.Blocks = new QuoteParser(cvInfo, book.BookId, blocksInBook[book], project.Versification).Parse().ToList();
completedProjectBlocks += numBlocksPerBook[book.BookId];
projectWorker.ReportProgress(MathUtilities.Percent(completedProjectBlocks, allProjectBlocks, 99));
reportProgress(MathUtilities.Percent(completedProjectBlocks, allProjectBlocks, 99));
});

projectWorker.ReportProgress(100);
reportProgress(100);
}

public static void SetQuoteSystem(QuoteSystem quoteSystem)
Expand Down
Loading