How to wait for a callback without blocking main thread - c#

I need to use a WebView in a custom renderer for Android and need to get a Value with EvaluateJavascript:
class ValueCallback : Java.Lang.Object, IValueCallback
{
public Java.Lang.Object Value
{
get;
set;
}
public void OnReceiveValue(Java.Lang.Object value)
{
Value = value;
}
}
This is called from a Method in the Renderer, which in Turn gets called from a Property like Duration
public double Duration;
Get Duration from Webview:
var callback = new ValueCallback();
View.EvaluateJavascript(jsToExecute, callback);
//How to wait here without blocking the MainThread?
The Problem is that EvaluateJavascript can only be called from the MainThread
and the Property is also called from MainThread.
So when I use something like AutoResetEvent to wait for the Script to finish, the result is a Deadlock.

You're asking how to block the main thread without blocking the main thread. The answer is quite simple, you can't.
If you don't want to block the main thread you're going to need to not wait, and instead do whatever you want to have happen after the callback executes in the callback rather than waiting for it.

Related

The UI freezes until the method is finished. How can not freeze?

I'm coding 'Waiting for login' in the app.
public struct Member
{
public bool IsOpened, IsLogIn;
public string Title, Name;
}
private static void WaitForLogin(ref Member member)
{
while (member.IsOpened)
{
if (IsLoggIn() == true)
{
member.Title = "Welcome to App Centrel";
member.Name = "Omer";
member.IsLogIn = true;
break;
}
System.Threading.Thread.Sleep(1000);
}
}
Using the non-freezing Task, Thread, Async Etc... codes, Time problem occurs due to the ref, out in the parameter.
How does the method not freeze until finished using out, ref parameter?
External app is the not logged in. started freezing in my UI.
I wanted to change the 'mem' variable the time when logged in in the IsLoggIn() method
Once again, why you are using struct? Why not like this?
public class Member
{
public bool IsOpened { get; set; }
public bool IsLogIn{ get; set; }
public string Title{ get; set; }
public string Name{ get; set; }
}
private static async Task WaitForLogin(Member member)
{
while (member.IsOpened)
{
if (IsLoggIn() == true)
{
member.Title = "Welcome to App Centrel";
member.Name = "Omer";
member.IsLogIn = true;
break;
}
await Task.Delay(1000);
}
}
Then you should be able to use it like this
private async void button1_Click(object sender, EventArgs e)
{
Member asD = new Member();
asD.IsOpened = true;
await WaitForLogin(asD);
MessageBox.Show("Logged In. Starting Methods");
}
Thread.Sleep(1000) will suspend the current (UI) thread. This is rarely a good idea, especially on the UI thread. Also note that your example code is probably not safe. I would assume that you are modifying the IsOpened field from another thread, and this is not safe without at least marking the field as volatile. But just use a full lock if you are unsure about the level of synchronization needed.
I do not see that the ref has any real effect on the UI freezing. But I would in general recommend against mutable structs. Just use a class with properties instead.
The simplest possible workaround would be to replace the sleep with a Task.Delay and mark the method as async. This will internally be similar to starting a timer that checks for the login. But be careful with async void functions, since they can lose exception if you are not careful. Prefer to return a task for async functions, unless it has to be void, like an event-handler.
A better solution will be to let whatever component doing the login to send an event. This might be a regular event, or it might be thread-safe event, or a waithandle that may be triggered from a separate process if named. You can also use a task to signal the completion of some login process. But it is difficult to tell exactly how this should be done without more information about the internals of the system.
Regardless of the option you pick, you should probably show a modal UI dialog while waiting for login, to prevent any other interaction with the UI while waiting, while still allowing the UI to be responsive.
For example using a task to signal login and winforms for the UI
public static Task ShowDialogUntillLogin(Task loginTask){
var myForm = new MyForm();
loginTask.ContinueWith(t => myForm.Invoke(() => myForm.DialogResult = DialogResult.OK));
myForm.ShowDialog();
return loginTask;
}
This should show the form until the task is set as complete. When this happens the dialog will be closed and the method returns. Since it blocks inside the ShowDialog method UI messages will still be processed, and the UI remain responsive, but the user can only do stuff in MyForm.

Is c# event in Unity MonoBehaviour single threaded?

Is it true that event in Unity's Monobehavior is single threaded? When I trigger an event, if one of the listener raises an exception, the catch block will be executed. I assume once class A fires an event, the thread will go over each subscriber. When each subscriber finishes, does class A continue in the same thread?
public class EventExample : MonoBehaviour
{
public delegate void ExampleEventHandler();
public static event ExampleEventHandler OneDayPassed;
public void Start NextTurn()
{
try {
if (OneDayPassed != null)
{
OneDayPassed();
}
}
catch(Exception e)
{
// will catch error here
}
}
}
public class EntityWatcher : MonoBehaviour
{
void Start()
{
EventExample.OneDayPassed += this.PrepareNextDay;
}
public void PrepareNextDay()
{
int test = int.parse("error");
}
}
Monobehavior is mostly single Threaded but some few callback functions are not. Most of the Unity API callback functions will be made on the main Thread.
These are the fnctions that will not be called on the main Thread which means that you can't call/use the Unity API inside these functions:
Application.RegisterLogCallbackThreaded
Application.logMessageReceivedThreaded event
OnAudioFilterRead Monobehavior callback function.
As for your own custom event like:
public delegate void ExampleEventHandler();
public static event ExampleEventHandler OneDayPassed;
The event is called on the Thread you invoked it from. If you call it from the main/MonoBehaviour thread, the callback will happen on the main thread. If you create a new Thread and call it from there or use any of the 3 functions listed above then expect it to be called on another Thread other than the main Thread.
If you need to use Unity's API from another Thread other than the main Thread then see this post.
Unity's event API is single-threaded. And also, is not designed to support multi-threading (not thread-safe).
Of course, you can explicitly delegate some work with multi-threading if you want, but don't try to call the Unity API with these.
You can have some more information here : https://answers.unity.com/questions/180243/threading-in-unity.html
If you need to rely on some execution order, you can refer to the manual page here

Prevent Task from starting

Class TaskHolder has a property of type Task. I pass Task as a parameter into constructor and it starts immediately. Is there a way to prevent it from start?
public class Worker
{
public class TaskHolder
{
public TaskHolder(Task objective)
{
Objective = objective;
}
public Task Objective { get; set; }
}
public async Task DoSomething()
{
await Task.Delay(5000);
Debugger.Break(); // Task starts, so debugger stops here!
// Is there a way to prevent it from start?
}
[Test]
public async Task TempTest()
{
// programm starts here:
var t1 = new TaskHolder(DoSomething());
await Task.Delay(10000);
}
}
A Task represents the now-or-future result of an awaitable operation. If that task has already started, that's interesting - but it has nothing to do with the code that is receiving the task.
If you don't want to start something yet, you should probably be passing an Action. You can create a Task from an Action via Task.Run. Or you can simply invoke the action via action() or action.Invoke()`.
If what you want to run is explicitly asynchronous: you can pass Func<Task>. Again, you can start such a delegate with Task.Run if you want a Task that represents the final state. Or you can just invoke it.
(whether to Invoke() it or pass it to Task.Run() depends on whether you want it to use your thread for any of it)
Ultimately, when you called DoSomething(), you started the code running. A timer was scheduled by the Task.Delay, and then the code continued into TaskHolder.

Awaiting a method that is deeply nested on stack

Lets say at some point at least 10 methods are available at stack as not finished. Many of these methods are dealing with actions that make impact on UI. At this point, I would like to issue a Save command. Save command can finish successfully, or can fail. Based on the result, I would like to make different actions, and only then return execution to those methods that are left on stack.
Now, if I run Save command synchronously, there is no problem. I would like to execute Save command asynchronously, return the execution to message pump (UI), while all the code (methods) on stack should wait for SaveCommand to finish.
Now, as I have understood await, there is no guarantee that a call will be made on same thread (in my case UI thread). SO, I cannot just await the first method that was called (the parent of all other methods in stack), since if a different thread gets started, it will raise a UI exception (accessing UI elements from different thread).
So, how to handle this situation? Example code:
public bool PropertyName {get; set { MethodA(); // some code after };}
public void MethodB() { MethodC(); // some code after }
public void MethodC() { MethodD(); // some code after }
public void MethodD() { MethodE(); // some code after }
// etc
void MEthodK()
{
Save();
}
If you want to (asynchronously) wait for a method, just await the Task returned from that method:
public async Task MethodCAsync() { await MethodDAsync(); // some code after }
public async Task MethodDAsync() { await MethodEAsync(); // some code after }
async Task MethodKAsync()
{
await Save();
}
This will cause a problem with your property setter, which now must be an asynchronous method:
public bool PropertyName { get; private set; }
public async Task SetPropertyNameAsync() { await MethodAAsync(); // some code after }
Unless you call ConfigureAwait(), awaiting a Task from a UI thread will always resume running your code on the UI thread.
You don't have to worry about it.

dispatching wcf model - adding method

Sorry I still don't understand how UI works and what is Dispatcher
I have such DispatchingWcfModel:
public interface IWcfModel
{
List<ConsoleData> DataList { get; set; }
event Action<List<ConsoleData>> DataArrived;
}
class DispatchingWcfModel : IWcfModel
{
private readonly IWcfModel _underlying;
private readonly Dispatcher _currentDispatcher;
public DispatchingWcfModel(IWcfModel model)
{
_currentDispatcher = Dispatcher.CurrentDispatcher;
_underlying = model;
_underlying.DataArrived += _underlying_DataArrived;
}
private void _underlying_DataArrived(List<ConsoleData> obj)
{
Action dispatchAction = () =>
{
if (DataArrived != null)
{
DataArrived(obj);
}
};
_currentDispatcher.BeginInvoke(DispatcherPriority.DataBind, dispatchAction);
}
public List<ConsoleData> DataList
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public event Action<List<ConsoleData>> DataArrived;
}
Now I want to add int[] ConnectionStats { get; set; }. Should I introduce separate event for it? What should I write in DispatchingWcfModel? I want to have interface like that:
public interface IWcfModel
{
List<ConsoleData> DataList { get; set; }
int[] ConnectionStats { get; set; }
event Action<List<ConsoleData>> DataArrived;
}
The Dispatcher is WPF's internal message queue for the main UI Thread. It can be used from other threads to run commands on the main UI thread of an application.
This is important because WPF doesn't let you access objects which were created on other threads. For example, if a Button is created on the main UI thread, then you cannot modify this button from another thread, but you can use the Dispatcher from another thread to send a command to the main UI thread to update the button.
This applies to all objects, not just UI Elements. If something like an ObservableCollection is created on one thread, another thread cannot modify it. Because of this, all objects are usually created on the main UI thread.
Dispatcher messages can get processed synchronously or asynchronously, and they can have different priorities . In your code, you're using _currentDispatcher.BeginInvoke(DispatcherPriority.DataBind, dispatchAction);, which means it will begin an asynchronous operation with the main UI thread, and run it at the same priority as DataBinding. You can view more information on DispatcherPriorities here.
If your ConnectionStats collection gets its data asynchronously, then you will want to add some kind of DataArrived method that will take data obtained from a non-UI thread and add it to the collection. You can use the existing DataArrived method if the data is obtained and packaged together, or create your own if the data is obtained separately. If it gets it's data synchronously, you don't need to do anything special.
From the looks of things, _underlying_DataArrived is meant to run on a background thread (meaning it can't alter your collections, which should be created on the main UI thread), while DataArrived is meant to run on the main UI thread.

Categories