ASync Dataportal issue

ASync Dataportal issue

Old forum URL: forums.lhotka.net/forums/t/7590.aspx


rfcdejong posted on Wednesday, September 09, 2009

I need some help on this, i've a WPF application and while saving the application shouldn't be able to close so i though the ManualResetEvent would block the main thread in this case until the save operation is done. Ok, not very nice because this will lock the UI, but effective since it must block the close operation.

_model as an csla business object.
ManualResetEvent waitHandle = new ManualResetEvent(false);

_model.BeginSave((s1, e1) =>
{
      if (e1.Error != null) 
            
throw e1.error;
      
waitHandle.Set();
});

waitHandle.WaitOne();

The problem is that the backgroundworker within the DataPortal does his DoWork method but never does his RunWorkerCompleted.

I know a solution for this problem, but that isn't realisable.

Setting the waitHandle in the backgroundworker at the end of DoWork.

void Update_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
   var request = e.Argument as DataPortalAsyncRequest;
   SetThreadContext(request);
   T result =
default(T);
   try
   {
         
object state = request.Argument;
         result = (T)Csla.
DataPortal.Update<T>((T)state);
         e.Result =
new DataPortalAsyncResult(result, Csla.ApplicationContext.GlobalContext, null,    request.UserState);

         waitHandle.Set();
   }
   catch (Exception ex)
   {
         e.Result =
new DataPortalAsyncResult(result, Csla.ApplicationContext.GlobalContext, ex, request.UserState);
   }
}

RockfordLhotka replied on Wednesday, September 09, 2009

The problem is that RunWorkerCompleted delegates back to the UI thread - which you have blocked.

You really can't block the UI thread like that, as doing so is outside the design parameters for the async data portal.

I haven't looked in WPF, but in Windows Forms there's an event you can handle when the primary form is closing, and you can cancel the close operation. I suspect there's a similar concept in WPF as well.

rfcdejong replied on Wednesday, September 09, 2009

it's more difficult, but not impossible thru.
anyway canceling is something i tried, there are 2 ways of closing an application or actually 3.

1) When the user request the application to close, something like mainwindow.closing

2) When the user log's off windows
application session_ending with the reason

3) When the user shuts down windows
application session_ending with the reason

All 3 can be cancelled, but that isn't the problem. The real problem is that the application should wait until the save operation is completed. BeginSave() as Event-Based Async method and whenever it comes back then continue.

The only real solution i currenly see is passing the handle along so that the background worker can signal it at the end of the DoWork method.

Another solution is having a while busy do eat processor loop

I tryed to cancel the close operation and setting a private boolean to closing and in the callback method i closed the application again, but then it just throws an exception that it's already closing.

ajj3085 replied on Wednesday, September 09, 2009

If you cancel the user logoff or shutdown, wouldn't that make users upset?

Which brings me to another question... if you have an async BeginSave, can you cancel the async operation.. and will that rollback changes?

Oh, I wouldn't do that while busy eat processor... that will cause problems for users on laptops as your program wastes cycles (and thus battery power, not to mention cpu time).

rfcdejong replied on Thursday, September 10, 2009

Canceling is correct when showing a dialog that there is a ongoing mutation and u want to ask the user what to do, if he is sure to close without saving. In my situation there is a problem when there are more then one controllers open and they are one by one closing with user interaction.

It's kinda frustrating that something so simple isn't as simple as it should be, i could modify csla code but that isn't what i want.

I'm almost going to introduce something like a (while busy do) Dispatcher.KeepMyMainThreadAlive(); or something.

ajj3085 replied on Friday, September 11, 2009

Even if you manage to get the dialog to display, I think at some point Windows will say "this program is preventing your logoff, wait or end it" so the user can still force it to exit.

rfcdejong replied on Friday, September 11, 2009

Well forget the windows logoff and windows shut down, just when the user closes the application manually and choose to save his changes. That save will be async and some code will run on the server, at this point the client running the wpf application shouldn't close yet because something could go wrong saving.

Any suggestion on how to join the threads?

RockfordLhotka replied on Friday, September 11, 2009

What you need is a thread sync mechanism that doesn't block the UI thread.
That's the core issue here - blocking the UI thread is never a good idea.

You can implement a basic polling scheme that uses a busy loop with a sleep
timer so you don't consume all the processor.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var busy = new BusyWait();

var bw = new System.ComponentModel.BackgroundWorker();
bw.RunWorkerCompleted += (o, e) =>
{
busy.End();
};
bw.DoWork += (o, e) =>
{
// sleep for 5 seconds
Thread.Sleep(5000);
};

Console.WriteLine("starting");
busy.Start();
bw.RunWorkerAsync();

Console.WriteLine("waiting");
busy.Wait();
Console.WriteLine("done");
Console.ReadLine();
}

}

public class BusyWait
{
private long _count;

public void Start()
{
Interlocked.Increment(ref _count);
}

public void End()
{
Interlocked.Decrement(ref _count);
}

public void Wait()
{
while (Interlocked.Read(ref _count) > 0)
Thread.Sleep(5);
}
}
}

rfcdejong replied on Saturday, September 12, 2009

I'm going to it with a Sleep(500) around the factory method, when it returns in the event-based async lambda part i end the wait. Maybe even a "busy" popup with a busy animation on it.

I was hoping there was a standard solution, since it can't be that i'm the first with this problem.

Copyright (c) Marimer LLC