From 441488f4fa677a971838c1d730611e5684a50151 Mon Sep 17 00:00:00 2001 From: ron-macneil-youi Date: Fri, 17 Sep 2021 03:01:39 +1000 Subject: [PATCH] Support thread and process concurrency (#135) --- WebDriverManager/DriverManager.cs | 28 ++++- WebDriverManager/Services/IBinaryService.cs | 7 +- .../Services/Impl/BinaryService.cs | 107 +++++++++++++++--- 3 files changed, 120 insertions(+), 22 deletions(-) diff --git a/WebDriverManager/DriverManager.cs b/WebDriverManager/DriverManager.cs index 5462b4f..22fe754 100644 --- a/WebDriverManager/DriverManager.cs +++ b/WebDriverManager/DriverManager.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using WebDriverManager.DriverConfigs; using WebDriverManager.Helpers; @@ -31,12 +32,21 @@ public DriverManager WithProxy(IWebProxy proxy) return this; } + [Obsolete("binaryName parameter is redundant, use SetUpDriver(url, binaryPath)")] public string SetUpDriver(string url, string binaryPath, string binaryName) { - var zipPath = FileHelper.GetZipDestination(url); - binaryPath = _binaryService.SetupBinary(url, zipPath, binaryPath, binaryName); - _variableService.SetupVariable(binaryPath); - return binaryPath; + lock (Object) + { + return SetUpDriverImpl(url, binaryPath); + } + } + + public string SetUpDriver(string url, string binaryPath) + { + lock (Object) + { + return SetUpDriverImpl(url, binaryPath); + } } public string SetUpDriver(IDriverConfig config, string version = VersionResolveStrategy.Latest, @@ -52,10 +62,18 @@ public string SetUpDriver(IDriverConfig config, string version = VersionResolveS url = UrlHelper.BuildUrl(url, version); var binaryPath = FileHelper.GetBinDestination(config.GetName(), version, architecture, config.GetBinaryName()); - return SetUpDriver(url, binaryPath, config.GetBinaryName()); + return SetUpDriverImpl(url, binaryPath); } } + private string SetUpDriverImpl(string url, string binaryPath) + { + var zipPath = FileHelper.GetZipDestination(url); + binaryPath = _binaryService.SetupBinary(url, zipPath, binaryPath); + _variableService.SetupVariable(binaryPath); + return binaryPath; + } + private static string GetVersionToDownload(IDriverConfig config, string version) { switch (version) diff --git a/WebDriverManager/Services/IBinaryService.cs b/WebDriverManager/Services/IBinaryService.cs index 9ceaedf..65dc9fb 100644 --- a/WebDriverManager/Services/IBinaryService.cs +++ b/WebDriverManager/Services/IBinaryService.cs @@ -1,7 +1,12 @@ -namespace WebDriverManager.Services +using System; + +namespace WebDriverManager.Services { public interface IBinaryService { + [Obsolete("binaryName parameter is redundant, use SetupBinary(url, zipDestination, binDestination)")] string SetupBinary(string url, string zipDestination, string binDestination, string binaryName); + + string SetupBinary(string url, string zipDestination, string binDestination); } } diff --git a/WebDriverManager/Services/Impl/BinaryService.cs b/WebDriverManager/Services/Impl/BinaryService.cs index 261ea7f..e20b9a0 100644 --- a/WebDriverManager/Services/Impl/BinaryService.cs +++ b/WebDriverManager/Services/Impl/BinaryService.cs @@ -7,7 +7,6 @@ using System.Net; using System.Runtime.InteropServices; using System.Text; -using WebDriverManager.Helpers; namespace WebDriverManager.Services.Impl { @@ -15,37 +14,113 @@ public class BinaryService : IBinaryService { public IWebProxy Proxy { get; set; } + [Obsolete("binaryName parameter is redundant, use SetupBinary(url, zipDestination, binDestination)")] public string SetupBinary(string url, string zipDestination, string binDestination, string binaryName) { - if (File.Exists(binDestination)) return binDestination; - FileHelper.CreateDestinationDirectory(zipDestination); - zipDestination = DownloadZip(url, zipDestination); - FileHelper.CreateDestinationDirectory(binDestination); + return SetupBinary(url, zipDestination, binDestination); + } + + public string SetupBinary(string url, string zipPath, string binaryPath) + { + var zipDir = Path.GetDirectoryName(zipPath); + var binaryDir = Path.GetDirectoryName(binaryPath); + var binaryName = Path.GetFileName(binaryPath); + + // + // If the destination already exists, we don't have to do anything + // + if (Directory.Exists(binaryDir)) return binaryPath; + + // + // Download the driver + // + Directory.CreateDirectory(zipDir); + zipPath = DownloadZip(url, zipPath); + + // + // Copy or extract binary(s) into a staging directory + // + var stagingDir = Path.Combine(zipDir, "staging"); + var stagingPath = Path.Combine(stagingDir, binaryName); + + Directory.CreateDirectory(stagingDir); - if (zipDestination.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + if (zipPath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { - File.Copy(zipDestination, binDestination); + File.Copy(zipPath, stagingPath); } - else if (zipDestination.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) + else if (zipPath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) { - UnZip(zipDestination, binDestination, binaryName); + UnZip(zipPath, stagingPath, binaryName); } - else if (zipDestination.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) + else if (zipPath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) { - UnZipTgz(zipDestination, binDestination); + UnZipTgz(zipPath, stagingPath); } #if NETSTANDARD - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || - RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + var needsChmod = + RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || + RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + if (needsChmod) { - var chmod = Process.Start("chmod", $"+x {binDestination}"); + var chmod = Process.Start("chmod", $"+x {stagingPath}"); chmod?.WaitForExit(); } #endif - RemoveZip(zipDestination); - return binDestination; + // + // Create the destination parent directory if it doesn't exist + // + var binaryParentDir = Path.GetDirectoryName(binaryDir); + Directory.CreateDirectory(binaryParentDir); + + // + // Atomically rename the staging directory to the destination directory + // + // If a parallel thread or process races us and wins, the destination will already exist and this will fail + // with an IOException. + // + Exception renameException = null; + try + { + Directory.Move(stagingDir, binaryDir); + } + catch (Exception ex) + { + renameException = ex; + } + + // + // Regardless of what happens, do a best-effort clean up + // + try + { + if (Directory.Exists(stagingDir)) Directory.Delete(stagingDir, true); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.ToString()); + } + try + { + RemoveZip(zipPath); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.ToString()); + } + + // + // If the destination still doesn't exist, it means the rename failed in an unexpected way + // + if (!Directory.Exists(binaryDir)) + { + throw new Exception($"Error writing {binaryDir}", renameException); + } + + return binaryPath; } public string DownloadZip(string url, string destination)