using PuppeteerSharp;
using System;
using System.Threading.Tasks;
namespace Video_Handler
{
public class VideoHost
{
private static bool Is_HTTPS_URL(string url)
{
Uri uriResult;
bool result = Uri.TryCreate(url, UriKind.Absolute, out uriResult) && (uriResult.Scheme == Uri.UriSchemeHttps);
return result;
}
#region BrowserCreationCode
private static async Task FectchBrowserIfItIsNotThere()
{
var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
}
internal static async Task<Page> GetPageAsync(string url, bool isPageInvisible = true, Action<Page> action)
{
if(!Is_HTTPS_URL(url))
{
throw new ArgumentException($"The url | {url} | is not an https url.");
}
await FectchBrowserIfItIsNotThere();
await using var browser = await Puppeteer.LaunchAsync(
new LaunchOptions { Headless = isPageInvisible });
await using var page = await browser.NewPageAsync();
await page.GoToAsync(url);
action(page);
return page;
}
}
}
//Boiler Plate code
async static void Main()
{
Action<Page> action = (page) => Foo(page);
await GetPageAsync("https://google.com", false , action);
}
public static async Task Foo(Page p){
await p.EvaluateExpression("//fill google search bar"); //first line
await p.EvaluateExpression("//click search button"); //second line
await p.WaitForNavigationAsync(); //third line
await p.EvaluateExpression("alert("hi")"); //fourth line
}
I am having an issue where when I send the page variable to the action which takes in a page variable it causes the page or browser variable to be disposed. From what I understand about using, this should only happen if I leave the function's parenthesis. I have read Microsoft's documentation on using and I can't find anything that tells me that the page variable disposes itself upon going to another function within the function the using variable is in.
Also, occasionally the code will be able to evaluate some of the function's expressions but it usually fails by the third. However, if I debug and run step by step, it always fails on the first line. This is what led me to believe it is a disposal issue.
To anyone wondering why I have not just removed the using from browser and page variables and dispose of them myself, I have two reasons. One, I don't have experience with disposing unmanaged code and when I tried to add the dispose to the finalizer/destructor it doesn't work and Secondly, I don't want to write code to do what the language already does for me.
The issue was using Action instead of Func<Page, Task>.
Action always returns a void. Since the function Action was calling was a async method but was not sending the method's output it causes the function that called Action to exit which released the unmanged resources. The Func<Page, Task> retains the output so it can be awaited so the main thread is blocked till completion preventing the unmanaged resources from being release early.
async static void Main()
{
Func<FileStream,Task> func = (FileStream fs) => Foo(fs); //Input to function is FileStream and Output of Function is Task
Action<FileStream> action = (FileStream fs) => Foo(fs); //Equivalent to Func<FileStream , void>
using var fs = new FileStream();
//action(fs); //This can fail if the main thread is finished before it
await func(fs); //This will succeed since the main thread is blocked till completion
}
async static Task Foo(FileStream fs) => await fs.SomeLongAsyncMethod();
Related
tldr: Is there a way to execute some code when an "await" causes a method call to return?
Suppose I log entry and exit of C# methods with an object whose Dispose() method logs the method's exit. For example
void DoWhatever()
{
using (LogMethodCall("DoWhatever")
{
// do whatever
}
}
That is, the method LogMethodCall() logs "DoWhatever entered" and then returns an object of type CallEnder whose Dispose() method logs "DoWhatever exiting". That works fine until await is used. For example...
async Task DoWhatever()
{
using (LogMethodCall("DoWhatever")
{
// do first part.
await Something();
// do second part.
}
}
The above code returns a Task to the caller when it hits the await, and the rest of the code (including the call to CallEnder.Dispose()) runs in that Task. My problem is that I want to log "DoWhatever exiting" when the await triggers the actual return, and not when CallEnder.Dispose() is finally called.
Is there a way to do that? Is there something like an event that's raised when await causes DoWhatever() to return? Maybe something to do with ExecutionContext or CallContext or TaskScheduler?
Note that I need to keep the "using (some_object)" pattern described in the above code. That pattern works well to log entry and exit of a block. I can change the implementation of some_object to detect when control returns from DoWhatever() to its caller, but I'd prefer not to change the implementation of DoWhatever(). Although I could if there's no other way.
ETA further clarification: I want to
Log when control exits from DoWhatever() and returns to its caller,
whether that's due to the await or due to the "natural" exit from
DoWhatever().
Do it in the same thread that called DoWhatever().
Preferably do it via the "using" clause shown above because that
pattern is already used in many places and works perfectly without
await.
Surprisingly it can be done, using AsyncLocal. AsyncLocal is like ThreadLocal except it flows through async code which might switch threads. It has a constructor which allows you to listen for value changes, and it even tells you the reason value has changed. It can be changed either because you explicitly set Value or if async context switch happens (in this case, Value changes to null/default when control leaves, and it changes back to original value when control returns). This allows us to detect when first await is reached, and not just first await but await that will introduce context switch (so, await Task.CompletedTask will not trigger context switch for example). So on first such switch Task will be returned back to caller.
Here is sample code:
public class Program {
public static void Main() {
var task = Test();
Console.WriteLine("Control flow got out of Test");
task.Wait();
}
static async Task Test() {
using (LogMethodCall()) {
await Task.Delay(1000);
Console.WriteLine("Finished using block");
}
}
static IDisposable LogMethodCall([CallerMemberName] string methodName = null) {
return new Logger(methodName);
}
private class Logger : IDisposable {
private readonly string _methodName;
private AsyncLocal<object> _alocal;
private bool _disposed;
public Logger(string methodName) {
Console.WriteLine($"{methodName} entered");
_methodName = methodName;
_alocal = new AsyncLocal<object>(OnChanged);
_alocal.Value = new object();
}
private void OnChanged(AsyncLocalValueChangedArgs<object> args) {
if (_disposed)
return;
// this property tells us that value changed because of context switch
if (args.ThreadContextChanged) {
Dispose();
}
}
public void Dispose() {
// prevent multiple disposal
if (_disposed)
return;
_disposed = true;
_alocal = null;
Console.WriteLine($"{_methodName} exited");
}
}
}
It outputs:
Test entered
Test exited
Control flow got out of Test
Finished using block
You can use the same code for regular functions too, because in them async local will never change and so dispose will happen as usual in the end of using block.
If you want to log when Something finished any synchronous actions and returns a Task, this is easy:
var task = Something();
/* log as you like */
await task;
Given the following method signature in a WCF service:
public string Query(string request)
{
using (Log.BeginTimedOperation("Persist request"))
{
var messageCorrelationId = Guid.NewGuid().ToString();
var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId);
PayloadHelper.PersistPayloadWithPath(request, payloadURI);
Log.Information("My service request {MessageCorrelationId} {RequestPayloadPath}", messageCorrelationId, payloadURI);
}
// DoWork here, code removed for brevity
return result;
}
and the corresponding extension methods:
public static string GetFullPath(string messageCorrelationId)
{
var folderDate = DateTime.Now.ToString("yyyyMMdd");
var folderHour = DateTime.Now.ToString("HH");
var logFolder = Path.Combine(ConfigurationManager.AppSettings["NetworkFiler"], "Payloads", folderDate, folderHour);
Directory.CreateDirectory(logFolder);
var fileName = $"{messageCorrelationId}-{"MyWCFService"}-{$"{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}-{Guid.NewGuid():N}"}.{"xml"}";
return Path.Combine(logFolder, fileName);
}
public static void PersistPayloadWithPath(string text, string path)
{
var task = WriteFileAsync(path, text);
task.GetAwaiter().GetResult();
}
private static async Task WriteFileAsync(string path, string text)
{
try
{
byte[] buffer = Encoding.Unicode.GetBytes(text);
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
}
}
catch (Exception ex)
{
Serilog.Log.Error(ex, "WriteFileAsync");
}
}
This code will block, if for instance the file is being interrogated by anti-virus (guess) or IO slowness to the filer.
So here is the great debate, calling a asynchronous method from a synchronous method in ASP.NET. To this day I still don't know if there is a reliable way to create a fire and forget mechanism. It's not that I don't care about the failure, I do, that 'should be' handled by the catch statement and the static Serilog instance.
While writing this version of the post, it dawned on me that maybe one of the problems might be in fact the logger and it's async wrapper around the File Sink.. will test that in a few.
Any help is appreciated with this bugger.
Thank you,
Stephen
UPDATE-ASYNC
public async Task<string> QueryAsync(string request)
{
using (Log.BeginTimedOperation("PersistAsync-Request"))
{
var messageCorrelationId = Guid.NewGuid().ToString();
var payloadURI = PayloadHelper.GetFullPath(messageCorrelationId);
await PayloadHelper.PersistPayloadWithPathAsync(request, payloadURI).ConfigureAwait(false);
Log.Information("My service request {MessageCorrelationId} {RequestPayloadPath}", messageCorrelationId, payloadURI);
}
// DoWork here, code removed for brevity
return result;
}
public static string GetFullPath(string messageCorrelationId)
{
var folderDate = DateTime.Now.ToString("yyyyMMdd");
var folderHour = DateTime.Now.ToString("HH");
var logFolder = Path.Combine(ConfigurationManager.AppSettings["NetworkFiler"], "Payloads", folderDate, folderHour);
Directory.CreateDirectory(logFolder);
var fileName = $"{messageCorrelationId}-MyWCFService-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}-{Guid.NewGuid():N}.xml";
return Path.Combine(logFolder, fileName);
}
public static async Task PersistPayloadWithPathAsync(string text, string path)
{
await WriteFileAsync(path, text).ConfigureAwait(false);
}
private static async Task WriteFileAsync(string path, string text)
{
try
{
byte[] buffer = Encoding.Unicode.GetBytes(text);
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
{
await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
}
}
catch
{
// ignored
}
}
Still blocking randomly, lick every 20-30 requests
So it's blocking on the new FileStream(). Looking at the source code, the constructor calls a method called Init(), which actually ends up opening the file. So it is doing I/O in the constructor, which really it shouldn't be since you can't await it.
Setting useAsync should make it run async, if it can. But maybe it can't open the file from a network drive asynchronously.
So your best bet is to just run it inside Task.Run() to guarantee it doesn't block.
The following only applies to .NET Core (unfortunately):
You would be better off using File.WriteAllTextAsync, which would actually make your life easier:
await File.WriteAllTextAsync(path, text);
The documentation of that doesn't really explain anything for some reason, but it works the same as File.WriteAllText (it's just async):
Creates a new file, write the contents to the file, and then closes the file. If the target file already exists, it is overwritten.
So, exactly what you're doing in your code, but this way you can await the whole operation (including opening the file).
I think it waits becuse you use
task.GetAwaiter().GetResult();
If you don't need the result, just remove this line. It should work fine.
If you need the result you should make PersistPayloadWithPath function also async.
The problem is your WriteFileAsync method. It actually runs synchronously until it hits the first await (that's how async/await works). I believe it hangs at new FileStream(...).
If you want to just fire-end-forget form synchronous code, this should be enough:
public static void PersistPayloadWithPath(string text, string path)
{
Task.Run(async () => await WriteFileAsync(path, text));
}
The code above should help you when you don't have async alternatives. However as Gabriel Luci suggested in his answer you should go with await File.WriteAllTextAsync(path, text); as it's probably optimized for async work.
You can still use Task.Run(...); with await File.WriteAllTextAsync(path, text); in fire-and-forget scenario.
Just beware that exceptions from inside the task (WriteFileAsync) won't propagate to the calling thread. This won't be the problem in your case since the whole WriteFileAsync method's body is inside try-catch block, where you log the exception.
EDIT
To illustrate how the threads behave in async/await methods, play around with the following example (try all 3 ways the Bar function is ran):
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Main thread: {Thread.CurrentThread.ManagedThreadId}");
Task.Run(async () => await Bar());
// Task.Run(() => Bar());
// Bar();
Console.ReadLine();
}
static async Task Bar()
{
Console.WriteLine($"Bar thread before await: {Thread.CurrentThread.ManagedThreadId}");
await Foo();
Console.WriteLine($"Bar thread after await: {Thread.CurrentThread.ManagedThreadId}");
}
static async Task Foo()
{
Console.WriteLine($"Foo thread before await: {Thread.CurrentThread.ManagedThreadId}");
var c = new WebClient();
var source = await c.DownloadStringTaskAsync("http://google.com");
Console.WriteLine($"Foo thread after await: {Thread.CurrentThread.ManagedThreadId}");
}
}
}
The root caused was determined to be an incorrectly configured StealthAudit, a tool that we use to audit our filers. Once the configuration was adjusted everything works as expected.
I've been reading examples for a long time now, but unfortunately I've been unable to apply the solutions to the code I'm working with. Some quick Facts/Assorted Info:
1) I'm new to C#
2) The code posted below is modified from Amazon Web Services (mostly stock)
3) Purpose of code is to compare server info to offline already downloaded info and create a list of need to download files. This snip is for the list made from the server side, only option with AWS is to call async, but I need this to finish before moving forward.
public void InitiateSearch()
{
UnityInitializer.AttachToGameObject(this.gameObject);
//these are the access key and secret access key for credentials
BasicAWSCredentials credentials = new BasicAWSCredentials("secret key", "very secret key");
AmazonS3Config S3Config = new AmazonS3Config()
{
ServiceURL = ("url"),
RegionEndpoint = RegionEndpoint.blahblah
};
//Setting the client to be used in the call below
AmazonS3Client Client = new AmazonS3Client(credentials, S3Config);
var request = new ListObjectsRequest()
{
BucketName = "thebucket"
};
Client.ListObjectsAsync(request, (responseObject) =>
{
if (responseObject.Exception == null)
{
responseObject.Response.S3Objects.ForEach((o) =>
{
int StartCut = o.Key.IndexOf(SearchType) - 11;
if (SearchType == o.Key.Substring(o.Key.IndexOf(SearchType), SearchType.Length))
{
if (ZipCode == o.Key.Substring(StartCut + 12 + SearchType.Length, 5))
{
AWSFileList.Add(o.Key + ", " + o.LastModified);
}
}
}
);
}
else
{
Debug.Log(responseObject.Exception);
}
});
}
I have no idea how to apply await to the Client.ListObjectsAsync line, I'm hoping you all can give me some guidance and let me keep my hair for a few more years.
You can either mark your method async and await it, or you can call .Wait() or .Result() on the Task you're given back.
I have no idea how to apply await to the Client.ListObjectsAsync line
You probably just put await in front of it:
await Client.ListObjectsAsync(request, (responseObject) => ...
As soon as you do this, Visual Studio will give you an error. Take a good look at the error message, because it tells you exactly what to do next (mark InitiateSearch with async and change its return type to Task):
public async Task InitiateSearchAsync()
(it's also a good idea to add an Async suffix to follow the common pattern).
Next, you'd add an await everywhere that InitiateSearchAsync is called, and so on.
I'm assuming Client.ListObjectsAsync returns a Task object, so a solution for your specific problem would be this:
public async void InitiateSearch()
{
//code
var collection = await Client.ListObjectsAsync(request, (responseObject) =>
{
//code
});
foreach (var item in collection)
{
//do stuff with item
}
}
the variable result will now be filled with the objects. You may want to set the return type of InitiateSearch() to Task, so you can await it too.
await InitiateSearch(); //like this
If this method is an event handler of some sort (like called by the click of a button), then you can keep using void as return type.
A simple introduction from an unpublished part of the documentation for async-await:
Three things are needed to use async-await:
The Task object: This object is returned by a method which is executed asynchronous. It allows you to control the execution of the method.
The await keyword: "Awaits" a Task. Put this keyword before the Task to asynchronously wait for it to finish
The async keyword: All methods which use the await keyword have to be marked as async
A small example which demonstrates the usage of this keywords
public async Task DoStuffAsync()
{
var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
var task = WriteTextAsync(#"temp.txt", result); //starts saving the string to a file, continues execution right await
Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
await task; //wait for WriteTextAsync to finish execution
}
private async Task<string> DownloadFromWebpageAsync()
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
}
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
Some thing to note:
You can specify a return value from an asynchronous operations with Task. The await keyword waits till the execution of the method finishes, and returns the string.
the Task object contains the status of the execution of the method, it can be used as any other variable.
if an exception is thrown (for example by the WebClient) it bubbles up at the first time the await keyword is used (in this example at the line string result (...))
It is recommended to name methods which return the Task object as MethodNameAsync
For more information about this take a look at http://blog.stephencleary.com/2012/02/async-and-await.html.
While on my local box the following code works:
public async Task<GameStatistic> LoadReport()
{
var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(FolderName, CreationCollisionOption.OpenIfExists);
var file = await folder.GetFileAsync(FileName);
GameStatistic returnValue;
using (var inStream = await file.OpenSequentialReadAsync())
{
var serializer = new DataContractJsonSerializer(typeof (GameStatistic));
returnValue = serializer.ReadObject(inStream.AsStreamForRead()) as GameStatistic;
}
return returnValue;
}
Code that calls the above method:
public GameStatistic GetReportData()
{
var repo = new GameRepository();
var gameStatTask = repo.LoadReport(); //(awaitable) Task<GameStatistic>
gameStatTask.Wait(); //this seems to make no difference
return gameStatTask.Result;
}
But When I move to code to my Surface Pro and run the application (no debugger), the folder.GetFileAsync(FileName) fails because the async call to get the folder hasn't returned yet.
When I debug the application on my Surface Pro (via Remote Machine) and slowly walk the debugger past the first line of code and wait a few seconds, and then step again, everything works.
I don't like the idea of trying to put a thread to sleep for an arbitrary length of time, but I am not sure what else I can do here.
Is there something I am doing wrong or something I should be doing that I am not doing at all?
Is there a common practice that would really wait until the CreateFolderAsync returns so that when I call folder.GetFileAsync that I could be sure the preceding line was complete?
Thanks for any help you may be able to provide.
As #J.B points out you need to use await instead of wait. Also, any function that calls an async method should itself be async (there is at least one exception to this). So almost your entire call stack up to the UI must be changed to be some variation of async Task<...>...:
async public Task<GameStatistic> GetReportData()
{
var repo = new GameRepository();
return await repo.LoadReport(); //(awaitable) Task<GameStatistic>
}
Caller of above (just an arbitrary method):
async public Task<MyResultClass> GetReportAndResult()
{
var gameStat = await GetReportData();
return ReportDataToMyResult(gameStat);
}
Top of call chain (event handler) must be async void:
async void GetReportData_ButtonClick(...)
{
var result = await GetReportAndResult();
// do something with result
// ...
}
Using latest CTP5 with async/await keywords, I wrote some code, which apparently cannot compile:
class Program
{
public class MyClass
{
async public Task<int> Test()
{
var result = await TaskEx.Run(() =>
{
Thread.Sleep(3000);
return 3;
});
return result;
}
}
static void Main(string[] args)
{
var myClass = new MyClass();
//The 'await' operator can only be used in a method or lambda marked with the 'async' modifier error ??!!
int result = await myClass.Test();
Console.ReadLine();
}
}
What is th reason of "The 'await' operator can only be used in a method or lambda marked with the 'async' modifier error?" (I've selected the line which Visual Studio point me to)
I don't know if you can mark Main as async, but you need to include the async keyword in the declaration of any method that uses await. For example:
public async void DoStuffAsync ()
{
var myClass = new MyClass ();
int result = await myClass.TestAsync ();
}
await is not the same as Wait(); doing an await is a significant re-writing of that method, and in particular affects the expectation of how that method exits to the caller. You are right in that it doesn't actually do much (caveat: return types) except tell the compiler to enable some things (as do switches like unsafe, checked and unchecked if you think about it) - but consider: this actually matters hugely in your example. If Main() exits (and we assume no other threads) - you exe is toast. Gone. No longer exists. Adding async makes you consider that just because the method exits doesn't mean it has finished. You really don't want Main() exiting before you are ready.
As a secondary effect, this switch also formalises that the method can only return things like Task; without the switch, you might be tempted to make it async later, which could be a significantly breaking change.
An async method can have a return type of void or Task. If the return type is not void the caller can still use the standard Wait mechanism introduced in .Net 4 inside the Main entry method (which can not be marked async). Here's a simple example:
static void Main(string[] args)
{
string address = "http://api.worldbank.org/countries?format=json";
Task t = LoadJsonAsync(address);
// do other work while loading
t.Wait();
Console.WriteLine("Hit ENTER to exit...");
Console.ReadLine();
}
private async static Task LoadJsonAsync(string address)
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(address);
// Check that response was successful or throw exception
response.EnsureSuccessStatusCode();
// Read response asynchronously as JsonValue and write out top facts for each country
JsonArray readTask = await response.Content.ReadAsAsync<JsonArray>();
Console.WriteLine("First 50 countries listed by The World Bank...");
foreach (var country in readTask[1])
{
Console.WriteLine(" {0}, Capital: {1}",
country.Value["name"],
country.Value["capitalCity"]);
}
}