protected virtual void FreeResources(ICheckoutTaskContext context, CheckoutTask task) { task.ProxyPool.Release(task.Proxy); task.Proxy = null; context?.Dispose(); TierControl.Release(); }
private StepResult WaitQuota(ICheckoutTaskContext context, CheckoutTask task, CancellationToken cancelToken) { StepResult res = StepResult.Ok; bool isQuotaAvailable = task.Profile.Wait(cancelToken); if (!isQuotaAvailable) { res = StepResult.Failed; } return(res); }
public StepResult Run(ICheckoutTaskContext context, CheckoutTask task, TimeSpan delayBetweenRetires, out TimeSpan executionTime, CancellationToken cancelToken) { StepResult res = StepResult.Error; Stopwatch stopWatch = new Stopwatch(); task.State = State != CheckoutTaskState.Undefined ? State : task.State; Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{Name}..."); while (res == StepResult.Error) { stopWatch.Reset(); stopWatch.Start(); try { res = m_action(context, task, cancelToken); } catch (Exception e) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Exception has occurred."); NotificationService.SendStatistic($"Excetpion: {e.ToString()}\nStack Trace:{e.StackTrace}", "ERROR"); res = StepResult.Failed; } stopWatch.Stop(); if (res == StepResult.Error) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{Name} got error... retry"); if (cancelToken.WaitHandle.WaitOne(delayBetweenRetires)) { res = StepResult.Canceled; break; } } } executionTime = stopWatch.Elapsed; if (res == StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{Name}... done ({(int)executionTime.TotalMilliseconds} ms)"); } return(res); }
private void CheckoutTaskFinishedHandler(object sender, EventArgs args) { CheckoutTask task = sender as CheckoutTask; task.Finished -= CheckoutTaskFinishedHandler; lock (m_lock) { m_tasks.Remove(task); if (m_tasks.Count == 0) { m_finishedEvent.Set(); } } }
private bool IsCanceled(CancellationToken cancelToken, CheckoutTask task, ICheckoutTaskContext context = null, bool isTierAcquired = true) { bool ret = false; if (cancelToken.IsCancellationRequested) { task.State = CheckoutTaskState.Canceled; context?.Dispose(); if (isTierAcquired) { TierControl.Release(); } Logger.LogEvent(task.Log, $"TASK {task.Id}", "Task is canceled!"); ret = true; } return(ret); }
private StepResult WaitProxy(ICheckoutTaskContext context, CheckoutTask task, CancellationToken cancelToken) { StepResult res = StepResult.Ok; task.Proxy = task.ProxyPool.WaitOne(cancelToken); if (task.Proxy == null) { if (cancelToken.IsCancellationRequested) { res = StepResult.Canceled; } else { res = StepResult.Failed; } } return(res); }
protected override StepResult Billing(ICheckoutTaskContext context, CheckoutTask task, CancellationToken cancelToken) { StepResult res = StepResult.Ok; SupremeUSABotTaskContext cxt = context as SupremeUSABotTaskContext; string html = null; HttpStatusCode?statusCode = null; cxt.ResetCheckoutInfo(); using (HttpRequestMessage checkoutPageRequest = new HttpRequestMessage() { RequestUri = new Uri($"https://{task.Footsite.Domain}{m_checkoutPagePath}"), Method = HttpMethod.Get }) { checkoutPageRequest.Headers.TryAddWithoutValidation("User-Agent", BrowserAgent); checkoutPageRequest.Headers.TryAddWithoutValidation("Accept", "*/*"); checkoutPageRequest.Headers.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate"); html = HttpHelper.GetStringSync(checkoutPageRequest, cxt.Client, out statusCode, cancelToken); } if (cancelToken.IsCancellationRequested) { return(StepResult.Canceled); } if ((res = CheckResult(html, statusCode)) != StepResult.Ok) { return(res); } cxt.CheckoutTimestamp = TimeHelper.GetUnixTimeStamp(); HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(html); HtmlNode checkoutFormNode = doc.GetElementbyId("checkout_form"); if (checkoutFormNode != null) { HtmlNode creditCardDetailsNode = doc.GetElementbyId("card_details"); if (creditCardDetailsNode != null) { try { HtmlNodeCollection divs = creditCardDetailsNode.SelectNodes("div"); cxt.CreditCardNumberFieldName = divs[0] .SelectSingleNode("input") .GetAttributeValue("name", cxt.CreditCardNumberFieldName); cxt.CreditCardMonthFieldName = divs[1].SelectNodes("select")[0] .GetAttributeValue("name", cxt.CreditCardMonthFieldName); cxt.CreditCardYearFieldName = divs[1].SelectNodes("select")[1] .GetAttributeValue("name", cxt.CreditCardYearFieldName); cxt.CreditCardCVVFieldName = divs[2] .SelectSingleNode("input") .GetAttributeValue("name", cxt.CreditCardCVVFieldName); cxt.CsrfToken = doc.DocumentNode.SelectSingleNode("//meta[@name='csrf-token']") .GetAttributeValue("content", ""); //cxt.StoreId = checkoutFormNode.SelectSingleNode("//input[@name='store_credit_id']") // .GetAttributeValue("value", ""); cxt.Utf8Symbol = checkoutFormNode.SelectSingleNode("//input[@name='utf8']") .GetAttributeValue("value", ""); cxt.AuthenticityToken = checkoutFormNode.SelectSingleNode("//input[@name='authenticity_token']") .GetAttributeValue("value", ""); } catch (Exception ex) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Could not parse credit card form. Default field names will be used."); } } else { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Could not find credit card form. Default field names will be used."); } try { HtmlNode extraFieldNode = doc.GetElementbyId("cart-cc").SelectSingleNode("fieldset").SelectNodes("input").Last(); cxt.ExtraField = new KeyValuePair <string, string>(extraFieldNode.GetAttributeValue("name", ""), extraFieldNode.GetAttributeValue("value", "")); } catch (Exception e) { } } else { Logger.LogEvent(task.Log, $"TASK {task.Id}", "Could not get checkout form. Contact with support."); res = StepResult.Failed; } return(res); }
protected override StepResult Paying(ICheckoutTaskContext context, CheckoutTask task, CancellationToken cancelToken) { StepResult res = StepResult.Ok; SupremeUSABotTaskContext cxt = context as SupremeUSABotTaskContext; string html = null; HttpStatusCode?statusCode = null; int timeElapsed = TimeHelper.GetUnixTimeStamp() - cxt.CheckoutTimestamp; if (timeElapsed * 1000 < task.Footsite.Settings.DelayInCheckout) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Delay in checkout {task.Footsite.Settings.DelayInCheckout - timeElapsed * 1000} ms"); cancelToken.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(task.Footsite.Settings.DelayInCheckout - timeElapsed * 1000)); if (cancelToken.IsCancellationRequested) { return(StepResult.Canceled); } } task.State = CheckoutTaskState.WaitingCaptcha; Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Waiting captcha..."); string token = null; while (true) { ICaptchaHarvesterTask harvesterTask = new CaptchaHarvesterTaskBase(null); Release.CaptchaHarvester.GetSolution(harvesterTask); WaitHandle.WaitAny(new[] { harvesterTask.SolutionReadyEvent, cancelToken.WaitHandle }); if (cancelToken.IsCancellationRequested) { return(StepResult.Canceled); } RecaptchaSolution solution = harvesterTask.Solution as RecaptchaSolution; if (solution.TimeStamp > cxt.CheckoutTimestamp) { token = solution.Value; break; } harvesterTask.SolutionReadyEvent.Dispose(); } Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Waiting captcha... done"); task.State = CheckoutTaskState.Paying; using (HttpRequestMessage tohruCommitMessage = new HttpRequestMessage() { RequestUri = new Uri(m_tohruCommitPath), Method = HttpMethod.Post }) { tohruCommitMessage.Headers.TryAddWithoutValidation("User-Agent", BrowserAgent); tohruCommitMessage.Headers.TryAddWithoutValidation("Accept", "*/*"); tohruCommitMessage.Headers.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate"); tohruCommitMessage.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream"); tohruCommitMessage.Headers.TryAddWithoutValidation("Refer", "https://www.supremenewyork.com/checkout"); tohruCommitMessage.Content = new StringContent(m_tohruTrackInfo); HttpHelper.GetStringSync(tohruCommitMessage, cxt.Client, out statusCode, cancelToken); } string billPhoneNumber = task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.PhoneNumber.Insert(3, "-").Insert(7, "-"); using (HttpRequestMessage checkoutRequest = new HttpRequestMessage() { RequestUri = new Uri($"https://{task.Footsite.Domain}{m_checkoutPath}"), Method = HttpMethod.Post }) { checkoutRequest.Headers.TryAddWithoutValidation("User-Agent", BrowserAgent); checkoutRequest.Headers.TryAddWithoutValidation("Accept", "*/*"); checkoutRequest.Headers.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate"); checkoutRequest.Headers.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); checkoutRequest.Headers.TryAddWithoutValidation("Refer", "https://www.supremenewyork.com/checkout"); checkoutRequest.Headers.TryAddWithoutValidation("X-Requested-With", "XMLHttpRequest"); checkoutRequest.Headers.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.8,en-GB;q=0.6"); checkoutRequest.Headers.Add("X-CSRF-Token", cxt.CsrfToken); checkoutRequest.Content = new FormUrlEncodedContent(new KeyValuePair <string, string>[] { new KeyValuePair <string, string>("store_credit_id", cxt.StoreId), new KeyValuePair <string, string>("same_as_billing_address", "1"), new KeyValuePair <string, string>("credit_card[last_name]", task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.SecondName), new KeyValuePair <string, string>("credit_card[first_name]", task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.FirstName), new KeyValuePair <string, string>("order[email]", task.Profile.ReleaseCheckoutProfile.CheckoutProfile.Email), new KeyValuePair <string, string>("order[tel]", billPhoneNumber), new KeyValuePair <string, string>("order[billing_address]", $"{task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.StreetAddress1}, {task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.StreetAddressLine2}"), new KeyValuePair <string, string>("order[billing_city]", task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.City), new KeyValuePair <string, string>("order[billing_state]", task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.State?.Abbreviation), new KeyValuePair <string, string>("order[billing_zip]", task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.PostalCode), new KeyValuePair <string, string>("order[billing_country]", ConvertCommonCountryNameToSupremeName(task.Profile.ReleaseCheckoutProfile.CheckoutProfile.BillingAddress.Country.Code)), new KeyValuePair <string, string>("credit_card[type]", ConvertCommonCreditCardTypeToSupremeType(task.Profile.ReleaseCheckoutProfile.CheckoutProfile.PayCard.Type.Code) ?? ""), new KeyValuePair <string, string>(cxt.CreditCardNumberFieldName, task.Profile.ReleaseCheckoutProfile.CheckoutProfile.PayCard.Number), new KeyValuePair <string, string>(cxt.CreditCardMonthFieldName, task.Profile.ReleaseCheckoutProfile.CheckoutProfile.PayCard.ExpirationDate.Value.ToString("MM")), new KeyValuePair <string, string>(cxt.CreditCardYearFieldName, task.Profile.ReleaseCheckoutProfile.CheckoutProfile.PayCard.ExpirationDate.Value.ToString("yyyy")), new KeyValuePair <string, string>(cxt.CreditCardCVVFieldName, task.Profile.ReleaseCheckoutProfile.CheckoutProfile.PayCard.CVS), new KeyValuePair <string, string>("order[terms]", "0"), new KeyValuePair <string, string>("order[terms]", "1"), new KeyValuePair <string, string>("g-recaptcha-response", token), new KeyValuePair <string, string>("utf8", cxt.Utf8Symbol), new KeyValuePair <string, string>("authenticity_token", cxt.AuthenticityToken), cxt.ExtraField }); html = HttpHelper.GetStringSync(checkoutRequest, cxt.Client, out statusCode, cancelToken); } if (cancelToken.IsCancellationRequested) { return(StepResult.Canceled); } if ((res = CheckResult(html, statusCode)) != StepResult.Ok) { return(res); } JObject checkoutReponseJObject = null; string checkoutStatus = null; string slug = null; try { checkoutReponseJObject = JObject.Parse(html); checkoutStatus = checkoutReponseJObject["status"].Value <string>(); } catch (Exception e) { Logger.LogEvent(task.Log, $"TASK {task.Id}", "Could not get checkout response. Contact with support."); } if (checkoutStatus != null) { if (checkoutStatus == "queued") { try { slug = checkoutReponseJObject["slug"].Value <string>(); } catch (Exception e) { Logger.LogEvent(task.Log, $"TASK {task.Id}", "Could not get queue id. Contact with support."); } if (slug != null) { cxt.Slug = slug; CheckoutStep queuedCheckout = new CheckoutStep(QueuedCheckout, "Waiting in Queue", CheckoutTaskState.Undefined); CheckoutStep retryCheckout = new CheckoutStep(RetryCheckout, "Checking paying status", CheckoutTaskState.Undefined); TimeSpan executionTime; res = queuedCheckout.Run(cxt, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (res == StepResult.Ok) { checkoutStatus = cxt.QueuedCheckoutResponse["status"].Value <string>(); int attempts = 1; while (attempts < 5 && res != StepResult.Canceled && checkoutStatus == "failed") { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Attempt {attempts} is failed... retry"); attempts++; cancelToken.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod)); if (cancelToken.IsCancellationRequested) { return(StepResult.Canceled); } res = retryCheckout.Run(cxt, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (res == StepResult.Ok) { checkoutStatus = cxt.QueuedCheckoutResponse["status"].Value <string>(); } } } } } if (res == StepResult.Ok) { if (checkoutStatus == "failed") { string errorMessage = ""; try { foreach (JToken errorJToken in checkoutReponseJObject["errors"]) { errorMessage += $"{errorJToken.First().Value<string>()};"; } } catch (Exception e) { ; } if (errorMessage != "") { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Has got errors: {errorMessage}"); } res = StepResult.Failed; } else if (checkoutStatus == "dup") { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Has got duplication"); res = StepResult.Failed; } else if (checkoutReponseJObject.ToString().Contains("Your order has been submitted") || cxt.QueuedCheckoutResponse != null && cxt.QueuedCheckoutResponse.ToString().Contains("Your order has been submitted")) { res = StepResult.Ok; } else { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Could not resolve checkout status '{checkoutStatus}'. Contact with support."); res = StepResult.Failed; } } } else { res = StepResult.Failed; } return(res); }
protected virtual StepResult Paying(ICheckoutTaskContext context, CheckoutTask task, CancellationToken cancelToken) { return(StepResult.Ok); }
protected virtual StepResult WaitProduct(ICheckoutTaskContext context, CheckoutTask task, CancellationToken cancelToken) { task.ProductAvailableEvent.WaitOne(cancelToken); return(StepResult.Ok); }
protected virtual ICheckoutTaskContext CreateContext(CheckoutTask task) { return(null); }
private void Checkout(CancellationToken cancelToken, CheckoutTask task) { ICheckoutTaskContext context = null; CheckoutStep waitProxy = new CheckoutStep(WaitProxy, "Waiting proxy", CheckoutTaskState.WaitingProxy); CheckoutStep waitProduct = new CheckoutStep(WaitProduct, "Waiting product", CheckoutTaskState.WaitingProduct); CheckoutStep addToCart = new CheckoutStep(AddToCart, "Carting", CheckoutTaskState.Carting); CheckoutStep checkCart = new CheckoutStep(CheckCart, "Checking cart", CheckoutTaskState.Checking); CheckoutStep billing = new CheckoutStep(Billing, "Billing", CheckoutTaskState.Billing); CheckoutStep waitQuota = new CheckoutStep(WaitQuota, "Waiting quota", CheckoutTaskState.WaitingQuota); CheckoutStep paying = new CheckoutStep(Paying, "Paying", CheckoutTaskState.Paying); StepResult res = StepResult.Ok; TimeSpan executionTime; if (!TierControl.Wait(cancelToken)) { if (IsCanceled(cancelToken, task, null, false)) { return; } throw new Exception(); } Logger.LogEvent(task.Log, $"TASK {task.Id}", "Initializing... done"); //res = waitProxy.Run(null, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); task.Proxy = task.ProxyPool.GetOne(); if (IsCanceled(cancelToken, task)) { return; } if (res != StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", "ATC can not be continued without proxy"); return; } while (true) { if (res != StepResult.Ok) { cancelToken.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod)); if (IsCanceled(cancelToken, task, context)) { return; } } context?.Dispose(); context = CreateContext(task); res = waitProduct.Run(context, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (IsCanceled(cancelToken, task, context)) { return; } if (res != StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{waitProduct.Name} is failed... retry"); continue; } if (task.Size == ClothesSizeSystemCollection.RandomSize) { task.Size = GetRandomSize(); Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Size {task.Size} is selected"); } res = addToCart.Run(context, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (IsCanceled(cancelToken, task, context)) { return; } if (res == StepResult.OutOfStock) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{addToCart.Name} is failed... Out of Stock. Retry ATC"); continue; } if (res != StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{addToCart.Name} is failed... retry ATC"); continue; } while (true) { if (res != StepResult.Ok) { cancelToken.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod)); if (IsCanceled(cancelToken, task, context)) { return; } } res = checkCart.Run(context, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (IsCanceled(cancelToken, task, context)) { return; } if (res != StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{checkCart.Name} is failed... retry ATC"); break; } res = billing.Run(context, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (IsCanceled(cancelToken, task, context)) { return; } if (res != StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{billing.Name} is failed... retrying checkout"); } res = waitQuota.Run(context, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (IsCanceled(cancelToken, task, context)) { return; } if (res == StepResult.Failed) { Logger.LogEvent(task.Log, $"TASK {task.Id}", "Checkout quota has ended"); } res = paying.Run(context, task, TimeSpan.FromMilliseconds(task.Footsite.Settings.RetryPeriod), out executionTime, cancelToken); if (res == StepResult.Ok) { task.Profile.Release(true); OnSuccessfulCheckout(new CheckoutInfo() { ReleaseName = Release.Name, ProductName = ProductName, ReleaseCheckoutProfile = task.Profile.ReleaseCheckoutProfile, Size = task.Size, TimeStamp = DateTime.Now }); } else { task.Profile.Release(false); } if (IsCanceled(cancelToken, task, context)) { return; } if (res != StepResult.Ok) { Logger.LogEvent(task.Log, $"TASK {task.Id}", $"{paying.Name} is failed... retrying checkout"); continue; } break; } if (res != StepResult.Ok) { continue; } break; } Logger.LogEvent(task.Log, $"TASK {task.Id}", $"Finished!"); task.State = CheckoutTaskState.Finished; FreeResources(context, task); }
public async Task <bool> Start(CancellationToken cancelToken) { bool ret = true; lock (m_lock) { if (m_state == ReleaseTaskState.Working) { ret = false; } m_state = ReleaseTaskState.Working; } if (ret) { OnPropertyChanged("State"); await Task.Run(() => { Log.Clear(); ReleaseProductInfo productInfo = new ReleaseProductInfo() { ProductLink = Release.ProductLink, KeyWords = Release.KeyWords.ToList() }; ProductMonitorTask productMonitorTask = new ProductMonitorTask() { Period = Release.Footsite.Settings.ProductMonitorPeriod, ProductInfo = productInfo, Proxy = Release.GlobalProxy }; Thread monitorThread = new Thread(() => MonitorProduct(cancelToken, Log, productMonitorTask)); monitorThread.Start(); List <CheckoutTask> checkoutTasks = new List <CheckoutTask>(); int taskId = 0; ProxyPool proxyPool = new ProxyPool(Release.Proxies.ToList(), 1); foreach (ReleaseCheckoutProfile profile in Release.Profiles) { CheckoutTaskCcProfile checkoutTaskCcProfile = new CheckoutTaskCcProfile(new ReleaseCheckoutProfile(profile) { CheckoutProfile = new CheckoutProfile(profile.CheckoutProfile) }, Release.Footsite.Settings.PurchaseLimitPerProfile); Profiles.Add(checkoutTaskCcProfile); for (int i = 0; i < profile.TasksCount; i++) { CheckoutTask checkoutTask = new CheckoutTask() { Id = taskId++, Size = profile.Size, VariantId = profile.VariantId, Profile = checkoutTaskCcProfile, ProxyPool = proxyPool, ProductAvailableEvent = productMonitorTask.ProductAvailableEvent, Footsite = Release.Footsite, ProductInfo = productInfo }; Logger.LogEvent(checkoutTask.Log, $"TASK {checkoutTask.Id}", "Initializing..."); checkoutTaskCcProfile.CheckoutTasks.Add(checkoutTask); checkoutTasks.Add(checkoutTask); } } RestockMonitorTask restockMonitorTask = new RestockMonitorTask() { Period = TimeSpan.FromMilliseconds(Release.Footsite.Settings.ProductMonitorPeriod), CheckoutTasks = checkoutTasks, ProductInfo = productInfo, }; Thread restockMonitorThread = new Thread(() => RestockMonitor(Log, restockMonitorTask, cancelToken)); restockMonitorThread.Start(); CheckoutTasksWatcher checkoutTasksWatcher = new CheckoutTasksWatcher(checkoutTasks); Task.Run(() => { foreach (CheckoutTask checkoutTask in checkoutTasks) { if (cancelToken.IsCancellationRequested) { break; } Thread checkoutThread = new Thread(() => Checkout(cancelToken, checkoutTask)); checkoutThread.Start(); } }); checkoutTasksWatcher.FinishedEvent.WaitOne(cancelToken); productMonitorTask.CompleteEvent.Set(); restockMonitorTask.CompleteEvent.Set(); }); TierControl.ReleaseQuota(Release.TasksCount, Release.Footsite.Type); State = ReleaseTaskState.Idle; } return(ret); }