VB.NET and DSharpPlus .. Awaiting/Async - vb.net

I am using DSHARPPLUS (for Discord) in my VB.NET application. I've wrapped the messages coming from discord with a handler (which is a async handler):
AddHandler DiscordGuild.MessageCreated, AddressOf MessageRecievedFromDiscord
.. I then use it like so:
Public Async Function MessageRecievedFromDiscord(sender As DiscordClient, e As MessageCreateEventArgs) As Task(Of Boolean)
Await Task.Delay(0)
If e.Author.IsBot Then Return True
If e.Channel.IsPrivate Then Return True
Dim channel As ULong = e.Message.ChannelId
If channel = mainGlobalChannel Then
If e.Message.Content.ToLower().StartsWith("!mute") Then
'Do Stuff here
Return True
End If
End If
End Function
My question is, does this run ASYNC properly? Is this even right? I want the messages to be handled off the main thread obviously. I am not sure if it really is. (Often lately my bot seems to be disconnecting and I wonder if it has to do with the way it's handling messages, it will just send the message using something like DiscordGuild.SendMessageAsync(queueOut.channel, queueOut.message)
which should work anyways.
Thanks!

Related

VB Await an asyc call that also awaits a call

I'm new to VB and not very familiar with async functions.
I need to fix a bug in existing code where some code is building a report before the data has finished loading.
The problem is that BuildReport() is getting called before the loadOption1(data) has been called. I need the application to wait for all of LoadAsync() to complete before running, but when it waits for GetData(), the application is returning to start() and running BuildReport() too early.
The code looks roughly like this:
Public Async Sub start()
await LoadAsync()
BuildReport() ' this must not run until everything in Load is complete
End Sub
Public Async Function LoadAsync() As System.Threading.Tasks.Task
'this is called from other locations, not just from Start()
dim data = await GetData() 'call to database
' at this point start() continues to run
' but we need it to keep waiting for these next calls to complete
'these calls are synchronous, builds objects needed for the report
loadOption1(data)
loadOption2(data)
loadOption3(data)
'now we want to return to start()
End Function
Public Async Function GetData(s As Scenario) As Task(Of DataResults)
...
Dim resp = Await Task.Run(Function() ConfigWebService.FetchIncentives(req)) ' soap call
End Function
(each function is in separate classes within the same project)
I have tried removing the await from the start function; the options load after BuildReport() is called.
if I remove the await from the GetData() call, passing data.result to the loadoptions functions, the whole application just hangs forever.
I'm stumped. Any tips would be greatly appreciated!
edit: updated the sample to correctly reflect the actual code
Update:
I have tried everything I can think of, from .ContinueWith(False), to attaching to the parent task, to using .ContinueWith(), but nothing has worked so far.
As soon as the code hits the await inside the LoadAsync(), the task in Start() is considered complete
the only thing that seemed to work was to making a isLoading flag that gets set to true at the start of LoadAsync(), then set to false after the LoadOptions3() finished. Then checking if the flag's value has changed in a loop with a delay
Public Async Sub start()
await LoadAsync()
For i As Integer = 1 To 50
If Not IsLoading Then Exit For
Await System.Threading.Tasks.Task.Delay(100)
Next
BuildReport()
End Sub

UWP (VB.Net) StreamSocketListener not firing connected event

I'm trying to setup a socket listener in a Universal Windows Platform App. The app will run on a Raspberry PI and listen for communications from an in-motion scale.
I've setup a StreamSocketListener in my mainVM file. I've got commands to start and stop it. I've followed the few guides online i can find, but it won't fire the event when the scale sends the data. I've tested with a listener app that I downloaded, so i can confirm that the messages are being sent and my computer is able to receive them (no firewall issues). (I also know that only one app can listen to a port, so this tester is off when testing my program.)
I'll get one hit on the handler function when it first starts the connection, but i never get anything again. No errors, just nothing happening.
Commands:
Public Property cmdStart As New Command(Async Sub() Await StartListener(), True)
Public Property cmdStop As New Command(Sub() StopListener(), True)
Command Subroutines:
Private Async Function StopListener() As Task
If Connected Then
Await _prePriceListener.CancelIOAsync()
RemoveHandler _prePriceListener.ConnectionReceived, Nothing
_prePriceListener.Dispose()
Connected = False
End If
End Function
Private Async Function StartListener() As Task
If ValidateInput() Then
Try
_prePriceListener = New StreamSocketListener()
_prePriceListener.Control.KeepAlive = False
AddHandler _prePriceListener.ConnectionReceived, AddressOf HandlerReceived
Await _prePriceListener.BindServiceNameAsync("6021")
Connected = True
Catch ex As Exception
Message = ex.Message
End Try
End If
End Function
Event Handler:
Private Async Function HandlerReceived(sender As StreamSocketListener, args As StreamSocketListenerConnectionReceivedEventArgs) As Task
Dim msgReceived As String = ""
Dim inStream As Stream
Dim reader As StreamReader
Try
inStream = args.Socket.InputStream.AsStreamForRead()
reader = New StreamReader(inStream)
Catch ex As Exception
Return
End Try
Try
If reader IsNot Nothing Then
msgReceived = Await reader.ReadLineAsync()
HandlePrepriceRead(msgReceived)
End If
Catch ex As Exception
Return
End Try
End Function
I've tried with _prePriceListener.Control.KeepAlive set to both true and false. When true, it hangs on msgReceived = Await reader.ReadLineAsync() in the handler. I suspect that is the issue, but every fix i've found made no difference. My only other suspicion is incorrect use of Async/Await but i can't find much information to either confirm or deny that. (I think i know what i'm doing with that, but i may be at that level of knowing just enough to be dangerous...)
Anyway, any input would be greatly appreciated. Thanks.
Are you sure that the scale is sending an EOL (\n or \r or \r\n) HEX 13 and or 10 ? I could see your code waiting for an something the scale is never sending. *FYI I'm not sure what kind of scale you are using or how quickly it talks or your application but if the scale is on continuous feed this probably isn't going to work very well for you as the scale and all the parts along the way will buffer things and the weight will be gone by the time you process this event (after a while atleast)

UI freezes when using Async-await

I have got this function which freezes the UI:
Public Sub ValidateUrlContentAsync(externalApplyURL As String)
AsyncManager.OutstandingOperations.Increment()
Dim result = Threading.Tasks.Task.
Run( _
Async Function()
Return Await ValidateLinks.ValidateUrlContentAsync(externalApplyURL)
End Function).
ContinueWith(
Sub(t)
Try
If t.IsFaulted Then
Throw t.Exception
End If
If t.Result.Error IsNot Nothing Then
ErrorHandler.Log.Warn(
String.Format("ValidationError: {0}, Exception: {1}", externalApplyURL, t.Result.Error))
End If
AsyncManager.Parameters("validationResult") = t.Result.ResultValue.ToString()
Finally
AsyncManager.OutstandingOperations.Decrement()
End Try
End Sub)
End Sub
Public Function ValidateUrlContentCompleted(validationResult As String) As ActionResult
Return Json(validationResult, JsonRequestBehavior.AllowGet)
End Function
I thought that task.run solves this issue because it creates a new thread separated from the UI thread, What's wrong with this code?
One thing that stands out to me is the use of three separate asynchronous code patterns (Await, ContinueWith, and AsyncManager) instead of just using Await.
The other major thing is that you're returning an ActionResult - indicating this is an ASP.NET application - and yet talking about a "UI thread". There is no UI thread on ASP.NET.
Thus, I reinterpret "freezes the UI" as "does not return a result until the handler is complete", which is exactly how ASP.NET is supposed to work.
So, first, remove all the unnecessary AsyncManager, ContinueWith, and Task.Run code, which really simplifies the method:
Public Function ValidateUrlContent(externalApplyURL As String) As Task(Of ActionResult)
Dim result = Await ValidateLinks.ValidateUrlContentAsync(externalApplyURL)
If result.Error IsNot Nothing Then
ErrorHandler.Log.Warn(String.Format("ValidationError: {0}, Exception: {1}", externalApplyURL, result.Error))
End If
Return Json(result.ResultValue.ToString(), JsonRequestBehavior.AllowGet)
End Function
Next, solve the "freezing the UI" problem. The proper place to solve this problem is in the UI, not on the server (ASP.NET) side. The way to prevent the UI from freezing is to call the server in an asynchronous way. If you UI is a .NET application, you can use Await with HttpClient to call it asynchronously.
You must Await the call to ValidateUrlContentAsnc. Turn it into a Function and return Task.

parallel event update UI

I have a class written in C#. In it I want to run a certain function in parallel on a list. After it completes on each item I would like to update a progress bar. However, I get very odd behavior from my program. It executes the event and reaches my sub but never proceeds to actually execute any code. Instead it just freezes. (I've mixed vb.net and c#. It will be rewritten at some point)
so in my windows form I call
progressBar.Visible = True
progressBar.Value = 0
progressBar.Maximum = dataGrid.SelectedRows.Count
AddHandler quoteManager.refreshStarted, AddressOf progressBarCounter
quoteManager.refreshAllAsync(list)
and the event is simply
Private Sub progressBarCounter()
Me.Invoke(Sub()
If progressBar.Value = progressBar.Maximum Then
progressBar.Visible = False
Else
progressBar.Value += 1
End If
End Sub)
End Sub
and in the quote manager class I have this defined.
public event Action refreshStarted;
public void refreshAllAsync(List<BindableQuote> bindableQuotes)
{
bindableQuotes.AsParallel()
.WithDegreeOfParallelism(10)
.ForAll((quote) => {
quote.refreshAll();
if (refreshStarted != null) { refreshStarted(); }
});
}
So for some reason I get it to enter progressBarCounter on each item in the list but it never exists. Instead it just keeps the form frozen.
I am not exactly sure this is what is happening, but it looks like progressBarCounter is blocking because you are calling Invoke. Should you be using BeginInvoke instead? Using BeginInvoke might solve the deadlock issue. See this post: What's the difference between Invoke() and BeginInvoke()
What appears to be happening here is that you access UI objects from multiple threads.
That's not supported. You'll have to run this code on a worker thread, and let it somehow accumulate progress, and send messages back to the UI thread. The BackgroundWorker class can help you implement the marshalling back to the UI thread.

.NET End Invoke / Post Operation Completed

My question involves async operations in VB .NET.
Given the following:
Delegate WorkerDelegate(Byval asyncOp As AsyncOperation)
Public Sub StartWork()
Dim worker as new WorkerDelegate(AddressOf DoWork)
Dim asyncOp as AsyncOperation = AsyncOperationManager.CreateOperation(New Object)
// begin work on different thread
worker.BeginInvoke(asyncOp, Nothing, Nothing)
End Sub
Private Sub DoWork(Byval asyncOp as AsyncOperation)
// do stuff
// work finished, post
asyncOp.PostOperationCompleted(AddressOf OnDownloadFinished, Nothing)
End Sub
Private Sub OnDownloadFinished()
// Back on the main thread now
End Sub
Most resources I've read say that if you use BeginInvoke on a delegate you must call EndInvoke. In my example above I am using the PostOperationCompleted method to switch threads back and report the operation has finished.
Do I still need to get an IAsyncResult when I call worker.BeginInvoke and add worker.EndInvoke in the OnDownloadFinished method?
It's best practice to call EndInvoke, because that's when resources assigned by the AsyncResult are cleaned.
However, AFAIK the async result used by the asynchronous delegate does not use any resource if you don't access the WaitHandle property, so not calling the EndInvoke may have no impact.
In your scenario, you should consider using ThreadPool.QueueUserWorkItem.
In the example on MSDN, the equivalent of your OnDownloadFinished method looks like this:
// This method is invoked via the AsyncOperation object,
// so it is guaranteed to be executed on the correct thread.
private void CalculateCompleted(object operationState)
{
CalculatePrimeCompletedEventArgs e =
operationState as CalculatePrimeCompletedEventArgs;
OnCalculatePrimeCompleted(e);
}
It does not call EndInvoke(). So it's safe to assume that not calling EndInvoke() in the PostOperationCompleted handler is alright.