Skip to content

Commit 011391b

Browse files
authored
Merge pull request #14243 from tamasvajk/parallelize-restore
C#: Parallelize restore logic of missing packages
2 parents 455cde2 + c78cd73 commit 011391b

File tree

14 files changed

+110
-98
lines changed

14 files changed

+110
-98
lines changed

csharp/autobuilder/Semmle.Autobuild.Shared/Autobuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ protected string RequireEnvironmentVariable(string name)
267267

268268
protected DiagnosticClassifier DiagnosticClassifier { get; }
269269

270-
private readonly ILogger logger = new ConsoleLogger(Verbosity.Info);
270+
private readonly ILogger logger = new ConsoleLogger(Verbosity.Info, logThreadId: false);
271271

272272
private readonly IDiagnosticsWriter diagnostics;
273273

csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,12 @@ public class SystemBuildActions : IBuildActions
187187

188188
bool IBuildActions.FileExists(string file) => File.Exists(file);
189189

190-
private static ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? workingDirectory, IDictionary<string, string>? environment, bool redirectStandardOutput)
190+
private static ProcessStartInfo GetProcessStartInfo(string exe, string arguments, string? workingDirectory, IDictionary<string, string>? environment)
191191
{
192192
var pi = new ProcessStartInfo(exe, arguments)
193193
{
194194
UseShellExecute = false,
195-
RedirectStandardOutput = redirectStandardOutput
195+
RedirectStandardOutput = true
196196
};
197197
if (workingDirectory is not null)
198198
pi.WorkingDirectory = workingDirectory;
@@ -204,40 +204,22 @@ private static ProcessStartInfo GetProcessStartInfo(string exe, string arguments
204204

205205
int IBuildActions.RunProcess(string exe, string args, string? workingDirectory, System.Collections.Generic.IDictionary<string, string>? env, BuildOutputHandler onOutput, BuildOutputHandler onError)
206206
{
207-
var pi = GetProcessStartInfo(exe, args, workingDirectory, env, true);
208-
using var p = new Process
209-
{
210-
StartInfo = pi
211-
};
212-
p.StartInfo.RedirectStandardError = true;
213-
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) => onOutput(e.Data));
214-
p.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => onError(e.Data));
215-
216-
p.Start();
207+
var pi = GetProcessStartInfo(exe, args, workingDirectory, env);
208+
pi.RedirectStandardError = true;
217209

218-
p.BeginErrorReadLine();
219-
p.BeginOutputReadLine();
220-
221-
p.WaitForExit();
222-
return p.ExitCode;
210+
return pi.ReadOutput(out _, onOut: s => onOutput(s), onError: s => onError(s));
223211
}
224212

225213
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? environment)
226214
{
227-
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, false);
228-
using var p = Process.Start(pi);
229-
if (p is null)
230-
{
231-
return -1;
232-
}
233-
p.WaitForExit();
234-
return p.ExitCode;
215+
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment);
216+
return pi.ReadOutput(out _, onOut: Console.WriteLine, onError: null);
235217
}
236218

237219
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? environment, out IList<string> stdOut)
238220
{
239-
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, true);
240-
return pi.ReadOutput(out stdOut);
221+
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment);
222+
return pi.ReadOutput(out stdOut, onOut: null, onError: null);
241223
}
242224

243225
void IBuildActions.DirectoryDelete(string dir, bool recursive) => Directory.Delete(dir, recursive);

csharp/extractor/Semmle.Extraction.CIL.Driver/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public static void Main(string[] args)
3838
}
3939

4040
var options = new ExtractorOptions(args);
41-
using var logger = new ConsoleLogger(options.Verbosity);
41+
using var logger = new ConsoleLogger(options.Verbosity, logThreadId: false);
4242

4343
var actions = options.AssembliesToExtract
4444
.Select(asm => asm.Filename)

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ private void GenerateSourceFilesFromWebViews(List<FileInfo> allFiles)
219219
}
220220
}
221221

222-
public DependencyManager(string srcDir) : this(srcDir, DependencyOptions.Default, new ConsoleLogger(Verbosity.Info)) { }
222+
public DependencyManager(string srcDir) : this(srcDir, DependencyOptions.Default, new ConsoleLogger(Verbosity.Info, logThreadId: true)) { }
223223

224224
private IEnumerable<FileInfo> GetAllFiles()
225225
{
@@ -430,8 +430,8 @@ private void AnalyseProject(FileInfo project)
430430

431431
}
432432

433-
private bool RestoreProject(string project, out string stdout, string? pathToNugetConfig = null) =>
434-
dotnet.RestoreProjectToDirectory(project, packageDirectory.DirInfo.FullName, out stdout, pathToNugetConfig);
433+
private bool RestoreProject(string project, string? pathToNugetConfig = null) =>
434+
dotnet.RestoreProjectToDirectory(project, packageDirectory.DirInfo.FullName, pathToNugetConfig);
435435

436436
private bool RestoreSolution(string solution, out IEnumerable<string> projects) =>
437437
dotnet.RestoreSolutionToDirectory(solution, packageDirectory.DirInfo.FullName, out projects);
@@ -454,25 +454,14 @@ private IEnumerable<string> RestoreSolutions(IEnumerable<string> solutions) =>
454454
/// <summary>
455455
/// Executes `dotnet restore` on all projects in projects.
456456
/// This is done in parallel for performance reasons.
457-
/// To ensure that output is not interleaved, the output of each
458-
/// restore is collected and printed.
459457
/// </summary>
460458
/// <param name="projects">A list of paths to project files.</param>
461459
private void RestoreProjects(IEnumerable<string> projects)
462460
{
463-
var stdoutLines = projects
464-
.AsParallel()
465-
.WithDegreeOfParallelism(options.Threads)
466-
.Select(project =>
467-
{
468-
RestoreProject(project, out var stdout);
469-
return stdout;
470-
})
471-
.ToList();
472-
foreach (var line in stdoutLines)
461+
Parallel.ForEach(projects, new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, project =>
473462
{
474-
Console.WriteLine(line);
475-
}
463+
RestoreProject(project);
464+
});
476465
}
477466

478467
private void DownloadMissingPackages(List<FileInfo> allFiles)
@@ -499,30 +488,30 @@ private void DownloadMissingPackages(List<FileInfo> allFiles)
499488
var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName)
500489
.Select(d => Path.GetFileName(d).ToLowerInvariant());
501490
var notYetDownloadedPackages = fileContent.AllPackages.Except(alreadyDownloadedPackages);
502-
foreach (var package in notYetDownloadedPackages)
491+
492+
Parallel.ForEach(notYetDownloadedPackages, new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, package =>
503493
{
504494
progressMonitor.NugetInstall(package);
505-
using var tempDir = new TemporaryDirectory(GetTemporaryWorkingDirectory(package));
495+
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package));
506496
var success = dotnet.New(tempDir.DirInfo.FullName);
507497
if (!success)
508498
{
509-
continue;
499+
return;
510500
}
501+
511502
success = dotnet.AddPackage(tempDir.DirInfo.FullName, package);
512503
if (!success)
513504
{
514-
continue;
505+
return;
515506
}
516507

517-
success = RestoreProject(tempDir.DirInfo.FullName, out var stdout, nugetConfig);
518-
Console.WriteLine(stdout);
519-
508+
success = RestoreProject(tempDir.DirInfo.FullName, nugetConfig);
520509
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
521510
if (!success)
522511
{
523512
progressMonitor.FailedToRestoreNugetPackage(package);
524513
}
525-
}
514+
});
526515
}
527516

528517
private void AnalyseSolutions(IEnumerable<string> solutions)

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,15 @@ private void Info()
4040
private static string GetRestoreArgs(string projectOrSolutionFile, string packageDirectory) =>
4141
$"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true";
4242

43-
public bool RestoreProjectToDirectory(string projectFile, string packageDirectory, out string stdout, string? pathToNugetConfig = null)
43+
public bool RestoreProjectToDirectory(string projectFile, string packageDirectory, string? pathToNugetConfig = null)
4444
{
4545
var args = GetRestoreArgs(projectFile, packageDirectory);
4646
if (pathToNugetConfig != null)
4747
{
4848
args += $" --configfile \"{pathToNugetConfig}\"";
4949
}
50-
var success = dotnetCliInvoker.RunCommand(args, out var output);
51-
stdout = string.Join("\n", output);
52-
return success;
50+
51+
return dotnetCliInvoker.RunCommand(args);
5352
}
5453

5554
public bool RestoreSolutionToDirectory(string solutionFile, string packageDirectory, out IEnumerable<string> projects)
@@ -90,7 +89,6 @@ private IList<string> GetListed(string args, string artifact)
9089
{
9190
if (dotnetCliInvoker.RunCommand(args, out var artifacts))
9291
{
93-
progressMonitor.LogInfo($"Found {artifact}s: {string.Join("\n", artifacts)}");
9492
return artifacts;
9593
}
9694
return new List<string>();

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Diagnostics;
34
using Semmle.Util;
@@ -19,23 +20,33 @@ public DotNetCliInvoker(ProgressMonitor progressMonitor, string exec)
1920
this.Exec = exec;
2021
}
2122

22-
private ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput)
23+
private ProcessStartInfo MakeDotnetStartInfo(string args)
2324
{
2425
var startInfo = new ProcessStartInfo(Exec, args)
2526
{
2627
UseShellExecute = false,
27-
RedirectStandardOutput = redirectStandardOutput
28+
RedirectStandardOutput = true,
29+
RedirectStandardError = true
2830
};
2931
// Set the .NET CLI language to English to avoid localized output.
3032
startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en";
3133
return startInfo;
3234
}
3335

34-
private bool RunCommandAux(string args, bool redirectStandardOutput, out IList<string> output)
36+
private bool RunCommandAux(string args, out IList<string> output)
3537
{
3638
progressMonitor.RunningProcess($"{Exec} {args}");
37-
var pi = MakeDotnetStartInfo(args, redirectStandardOutput);
38-
var exitCode = pi.ReadOutput(out output);
39+
var pi = MakeDotnetStartInfo(args);
40+
var threadId = $"[{Environment.CurrentManagedThreadId:D3}]";
41+
void onOut(string s)
42+
{
43+
Console.Out.WriteLine($"{threadId} {s}");
44+
}
45+
void onError(string s)
46+
{
47+
Console.Error.WriteLine($"{threadId} {s}");
48+
}
49+
var exitCode = pi.ReadOutput(out output, onOut, onError);
3950
if (exitCode != 0)
4051
{
4152
progressMonitor.CommandFailed(Exec, args, exitCode);
@@ -45,9 +56,9 @@ private bool RunCommandAux(string args, bool redirectStandardOutput, out IList<s
4556
}
4657

4758
public bool RunCommand(string args) =>
48-
RunCommandAux(args, redirectStandardOutput: false, out _);
59+
RunCommandAux(args, out _);
4960

5061
public bool RunCommand(string args, out IList<string> output) =>
51-
RunCommandAux(args, redirectStandardOutput: true, out output);
62+
RunCommandAux(args, out output);
5263
}
5364
}

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNet.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
44
{
55
internal interface IDotNet
66
{
7-
bool RestoreProjectToDirectory(string project, string directory, out string stdout, string? pathToNugetConfig = null);
7+
bool RestoreProjectToDirectory(string project, string directory, string? pathToNugetConfig = null);
88
bool RestoreSolutionToDirectory(string solutionFile, string packageDirectory, out IEnumerable<string> projects);
99
bool New(string folder);
1010
bool AddPackage(string folder, string package);

csharp/extractor/Semmle.Extraction.CSharp.Standalone/Extractor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static ExitCode Run(Options options)
119119
var stopwatch = new Stopwatch();
120120
stopwatch.Start();
121121

122-
using var logger = new ConsoleLogger(options.Verbosity);
122+
using var logger = new ConsoleLogger(options.Verbosity, logThreadId: true);
123123
logger.Log(Severity.Info, "Running C# standalone extractor");
124124
using var a = new Analysis(logger, options);
125125
var sourceFileCount = a.Extraction.Sources.Count;

csharp/extractor/Semmle.Extraction.CSharp.Standalone/Options.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Semmle.Util;
33
using Semmle.Util.Logging;
44
using Semmle.Extraction.CSharp.DependencyFetching;
5+
using System;
56

67
namespace Semmle.Extraction.CSharp.Standalone
78
{
@@ -64,15 +65,15 @@ public override bool HandleArgument(string arg)
6465
var fi = new FileInfo(dependencies.SolutionFile);
6566
if (!fi.Exists)
6667
{
67-
System.Console.WriteLine("Error: The solution {0} does not exist", fi.FullName);
68+
System.Console.WriteLine($"[{Environment.CurrentManagedThreadId:D3}] Error: The solution {fi.FullName} does not exist");
6869
Errors = true;
6970
}
7071
return true;
7172
}
7273

7374
public override void InvalidArgument(string argument)
7475
{
75-
System.Console.WriteLine($"Error: Invalid argument {argument}");
76+
System.Console.WriteLine($"[{Environment.CurrentManagedThreadId:D3}] Error: Invalid argument {argument}");
7677
Errors = true;
7778
}
7879

csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ public static void SetInvariantCulture()
7171

7272
public static ILogger MakeLogger(Verbosity verbosity, bool includeConsole)
7373
{
74-
var fileLogger = new FileLogger(verbosity, GetCSharpLogPath());
74+
var fileLogger = new FileLogger(verbosity, GetCSharpLogPath(), logThreadId: true);
7575
return includeConsole
76-
? new CombinedLogger(new ConsoleLogger(verbosity), fileLogger)
76+
? new CombinedLogger(new ConsoleLogger(verbosity, logThreadId: true), fileLogger)
7777
: (ILogger)fileLogger;
7878
}
7979

0 commit comments

Comments
 (0)