Using LocalMessageSender synchronously - c#

We have an application that has a primary window, it can launch multiple other windows, in new browsers. We are using a silverlight application as a coordinating server in the primary window to close all windows that are part of the app, regardless of the way they are opened (we can't guarantee it was via window.open so don't always have a handle to the window in javascript).
On log out, we want to signal all the other windows to perform an auto-save, if necessary, then close down.
So all windows have a silverlight app, they coordinate using localmessagesenders. However, these are asynchronous:
private void ProcessAutosave()
{
foreach (string s in _windows)
{
SendMessage(s, "notify-logout");
}
// code here quoted later...
}
// sendasynch doesn't send until the method terminates, so have to do it in it's own function.
private void SendMessage(string to, string message)
{
var lms = new LocalMessageSender(to);
lms.SendCompleted += new EventHandler<SendCompletedEventArgs>(SenderSendCompleted);
lms.SendAsync(message);
}
Since the ProcessAutosave is called from a javascript onunload event which can't be cancelled, we need this to be synchronous and not complete before we have a response processed from each sub-window so the session state will still be valid etc.
In the SenderSendCompleted we remove items from _windows when they have said they're done.
So I added a loop on the end:
while(_windows.Count > 0) {
Thread.Sleep(1)
}
However, that never terminates, unless I put an iteration counter on it.
Am I the victim of a compiler optimisation meaning the changes in SenderSendCompleted do not affect that while loop, or, have I fundamentally misunderstood something? Or missed something obvious that's staring me in the face?

It sounds like a subtle verson of a race situation due to going sync/async. Couldn't the process in queston also receive notifications from the windows that they have received the message and are shutting down? Once all of the counter messages have been received, then the main app could shut down without the busy wait at the end(?).

I have found a way to work round. However, this does not really "solve" the problem generally, just in my case, which is also only supporting internet explorer.
function WindowCloseEventHandler()
{
var app = // get silverlight app handle...
app.doAutoSave();
var params = 'whatever you need';
var args = new Object();
args.hwnd = window;
window.showModalDialog('blocker.aspx',args,params);
}
function checkAutoSave()
{
var app = // get silverlight app handle...
return app.autosavecomplete();
}
Then in blocker.aspx we display a static "performing logout handlers" type message and do:
function timerTick()
{
if(window.dialogArguments.hwnd.checkAutoSave()) {
window.close();
} else {
setTimeout(timerTick, 500);
}
}
And start the timer on window load.
The child window's silverlight apps are notified to start an autosave, then they notify the parent when they are done. We then poll the parent's status from a modal dialog, which blocks the termination of the WindowCloseEventHandler() which we have wired up to the onclose event of the body.
It's hacky and horrible, but it means silverlight stays asynchronous and we're using a javascript timer so the javascript isn't loading the system.
Of course if the user closes the modal dialogue, there is a potential for issue.

Related

Win 10 IOT thread deadlocking in UWP

What I want to do:
- synchronously (or even asynchronously) load settings from USB drive before first page loads
What I did:
- in OnLaunched method for App.xaml.cs I invoked this static function:
public static async void LoadSettings(string folderName = "Config", string fileName = "Settings.xml")
{
try
{
StorageFile configFile = null;
// scan through all devices
foreach (var device in await KnownFolders.RemovableDevices.GetFoldersAsync().AsTask().ConfigureAwait(false))
{
// folder that should have configuration
var configFolder = await device.GetFolderAsync(folderName).AsTask().ConfigureAwait(false);
if (configFile != null && configFolder != null && await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false) != null)
{
throw new Exception("More than one configuration file detected. First found configuration file will be used.");
}
else
configFile = await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false);
}
if (configFile == null)
throw new Exception("Configuration file was not found, please insert device with proper configuration path.");
string settingString = await FileIO.ReadTextAsync(configFile).AsTask().ConfigureAwait(false);
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
using (TextReader reader = new StringReader(settingString))
{
AppSettings = (Settings)serializer.Deserialize(reader); // store settings in some static variable
}
}
catch (Exception e)
{
//return await Task.FromResult<string>(e.Message);
}
//return await Task.FromResult<string>(null);
}
As you can see right now it's async void method, so I don't even want to synchronize it in any way with UI thread. It should just fire and do something. With ConfigureAwait(false) I want to be sure that it will never try to return to context. These returns at the end are remnants of other things I tried (I wanted to do this better way, this is the most primitive solution and it still doesn't work).
Anyway, because that's where the fun begins: everything works well when I debug application on local machine with Win 10. And I get deadlocked thread on Win 10 IOT installed on Raspberry Pi 3 (I installed it from the scratch today, last version).
But deadlock is not the weirdest thing. Weirdest thing is when it appears.
Like I said, invocation of this method looks like that:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Configuration.Settings.LoadSettings();
After that everything in this method goes normally, so I navigate to my first page somewhere below:
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(LogScreen), e.Arguments);
}
Window.Current.Activate();
}
Everything still works. User needs to write his code, I check if this code is available in settings and after that user can press "OK" to move to next page. Somewhere in LogScreenViewModel this method is responsible for that:
private void GoForward(bool isValid)
{
try
{
_navigationService.NavigateTo("MainPage"); // it's SimpleIoc navigation from MVVMLight
}
catch (Exception e)
{
Debug.WriteLine($"ERROR: {e.Message}");
}
}
And deadlock happens when _navigationService.NavigateTo("MainPage") is reached. Basically right now UI thread freezes. If I wait for long enough I will see catched exception in Output saying that messenger seemed occupied (I can't show the screen because I don't have access to that Raspberry right now) and after some timeout this thread was killed (like 30 seconds or something) - after that UI thread unlocks and application proceeds to MainPage. It doesn't happen on PC - MainPage appears immediately, no exceptions, no deadlocks.
I tried waiting on first page for like 1 minute to check if some deadlock exception would fire on it's own - but it doesn't. It will fire ONLY after I try to proceed to next page.
What else I tried instead of this fire-and-forget approach:
Making OnLaunched async and await LoadSettings returning Task - same thing happens in the same place, and no problem on PC.
Using:
Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => await Configuration.Settings.LoadSettings(); ).AsTask().Wait(); If I remember correctly it deadlocked immediately on Wait(), even with ConfigureAwait(false) everywhere, but it also happened on PC.
Allowing LogScreen to load, make it's OnNavigatedTo method async and await LoadSettings - same deadlock in same place
Allowing LogScreen to load and use Dispatcher from there like in point 2. It deadlocked the same way after reaching Wait(), on PC too.
Trying to force LoadSettings to be fully synchronous by replacing every await with AsTask().GetAwaiter().GetResults(). It worked well on PC... and of course deadlock on Raspberry.
What am I missing? What else can I try? Because to be honest right now it looks to me that Win 10 IOT .NET runtime is bugged or something.
I think I resolved the issue. This code was generally speaking not mine and after some digging I noticed that someone before me tried to list some other external devices while navigating to MainPage. It was not really async-safe code (someone probably wasn't aware of synchronization context) and it worked on Win 10 only because on desktop it was looking for COM0 device and I only have COM2, so method causing trouble was not even invoked at all.
I still have no idea how related it was to my configuration (because it somehow was working without it), but after I fixed issues with this old not-async-safe code it started to behave as expected.

C# form is blocked after creation

I am creating a Form when a certain event occurs. I put this created Form into a static member of the class where it is created. I debugged the code and everything works fine but the Form stays blocked and the user can't do anything in this window. It just appears with a loading animation (see picture). So nothing in the opened window is clickable, you can't even close it.
class CallManagementObserver : CallObserver
{
private static FrmIncomingCall frmCurrentCall;
public CallManagementObserver()
{
}
public void callChangedEvent(CallEv[] events)
{
foreach (CallEv currentEvent in events)
{
switch (currentEvent.getID())
{
case TermConnRingingEv.ID:
// Incoming call
frmCurrentCall = new FrmIncomingCall(currentEvent);
frmCurrentCall.Show();
frmCurrentCall.Update();
break;
case CiscoCallInfoChangedEv.ID:
// User accepted external call on terminal
frmCurrentCall.Close();
break;
case TermConnActiveEv.ID:
// User is in call
frmCurrentCall.Close();
break;
case ConnDisconnectedEv.ID:
// Caller has hung up
frmCurrentCall.Close();
break;
default:
break;
}
}
}
}
}
As you can see above I wrote my own Form class whose code is here:
public partial class FrmIncomingCall : Form
{
Call incomingCall;
CallEv currentEvent;
public FrmIncomingCall(CallEv currentEvent)
{
InitializeComponent();
this.currentEvent = currentEvent;
this.incomingCall = currentEvent.getCall();
}
private void initGui()
{
Connection[] callConnections = incomingCall.getConnections();
Address caller = callConnections[1].getAddress();
lblIncomingCallSource.Text = caller.getName();
}
private void btnAcceptCall_Click(object sender, System.EventArgs e)
{
TermConnEv termConnEv = (TermConnEv)currentEvent;
TerminalConnection termConn = termConnEv.getTerminalConnection();
termConn.answer();
}
private void frmIncomingCall_Load(object sender, System.EventArgs e)
{
initGui();
}
}
When I show the Form via ShowDialog() it is usable but the program stops (since this is what dialogs are made for I guess).
Any ideas what I'm doing wrong? Nothing freezes, the program is running correctly.
Well, your application is poorly designed... It seems that you have no idea of what multithreading is and why you should use it.
If the application hangs forever, then either there is a deadlock (something like the dialog wait on the calling system and the calling system wait on the dialog).
As I have no idea what CallEv is and how it is intended to be used.
Well, if the calling system works and the UI is never updated, then obviously, you never let the UI have time to be updated because your UI thread is 100% of the time using the calling system or waiting on it.
That means that the calling system should probably be used from another thread and that you should have some communication between both threads...
It might also be possible that the calling system might be used in many different ways (as it would be the case for serial port and TCP communication) where one could use what fit most with his application.
Another problem with your code is that when you close a dialog, as far as I know it cannot be used anymore without recreating the dialog as the dialog would be disposed... So you would need to set the formCurrentCall to null and update any affected code. Alternatively, you might hide the form instead and show it again when required.
In any case, it is hard to help you because we don't have any idea of what is CallEv and other classes or events in your code. Also, we have no idea which code is executing when the UI is not responding (or updated). So the question do not have enough informations. In fact, such problem are way easier to debug using a debugger as it is far easier to see what code is run and which line of code take time to execute or even to see which code is not executed.

.NET stop waiting for a database event which does not arrive

I'm working on a really big project developed by two teams, one (mainly) for the database, and one (where I am) mainly for the GUI and helper classes as an interface between GUI and DB.
Obviously, there are errors in communication, and - of course - we can't assume 100Mbit bandwidth & super-fast server computer.
Language is C# .NET, target "framework" is WPF and Silverlight.
When a user clicks a button, the GUI asks the DB (through helper classes) for information. Let's say... pizza types. The server should answer "{Funghi,Frutti di mare,Prosciutto}". When DB sends his answer, we receive a "database.Ready" event and fill our datagrid.
BUT if the user clicks the button while we haven't received the answer yet, the GUI sends an another request to the database. And the whole system tries to serve the user.
But it can't, because when the second request is sent, the first is disposed when we want to read it. So NullReferenceExceptions occur.
I've solved this by implementing kind of a semaphore which closes when user input occurs and opens when the Ready event (the functions it calls) finishes working.
Problem:
If I don't receive the Ready event, no user input is allowed, but this is wrong.
Question:
Is there a common (or at least, working) solution to stop waiting for the Ready event and...
1) re-sending the request a few times, hoping we receive our pizza types?
AND/OR
2) Dropping the request, tell the user that database failed to send the answer, and re-open the semaphore?
I can't post code here as this code is the property of a corporation, I'd rather like to have theoretical solutions, which are okay for professionals too.
Sorry for the long post, and thank you for your answers!
I assume that you are already using a background thread to dispatch the query to the database and wait for it's response. You can use the Task API that was introduced in .NET 4.0 to cancel such a request. For that, you pass in a CancellationToken that signals the status to the executing task. You can obtain a CancellationToken via a CancellationTokenSource as shown in the following code:
public partial class MainWindow : Window
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
public MainWindow()
{
InitializeComponent();
}
private void Button_CallDatabase(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(CallDatabase, _cancellationTokenSource.Token);
}
private void Button_OnNavigate(object sender, RoutedEventArgs e)
{
// If you navigate, you can cancel the background task and thus
// it will not execute any further
_cancellationTokenSource.Cancel();
}
private void CallDatabase()
{
// This simulates a DB call
for (var i = 0; i < 50; i++)
{
Thread.Sleep(100);
}
// Check if cancellation was requested
if (_cancellationTokenSource.Token.IsCancellationRequested)
{
Debug.WriteLine("Request cancelled");
return;
}
Debug.WriteLine("Update Controls with DB infos.");
}
}
Note that this example is simplified, you can and should use this in another component (e.g. view model).
If you still want to use the Ready event, you could also just unregister from it when you navigate away, so that no further actions will be performed when it is raised.

BackgroundWorker in MVC3 application freezes UI

I'm developing MVC3 based web application, which at one point needs to cache large amount of data from some external database. Process takes about 10-30 min (depending on a traffic) so I put it in BackgroundWorker. When you click particular button on the webpage, using ajax it just access other method in controller, depending on the returned value proper information is displayed on user interface.
Controller:
if (IsDbLocked())
{
return this.Json(new
{
success = false,
message = "There is already an update requested by other user on the way."
});
}
this.model.DataUpdateBegin();
return this.Json(new { success = true });
Model:
public void DataUpdateBegin()
{
var backgroundWorker = new BackgroundWorker
{
WorkerSupportsCancellation = false,
WorkerReportsProgress = true
};
backgroundWorker.DoWork += this.DataUpdateWorkerDoWork;
backgroundWorker.ProgressChanged += this.DataUpdaterWorkerProgressChanged;
backgroundWorker.RunWorkerCompleted += this.DataUpdaterWorkerRunWorkerCompleted;
if (this.DataUpdateLockDb(true))
{
backgroundWorker.RunWorkerAsync();
}
}
Now when I do update, UI still freezes. While debuging controller I can see, that it starts BackgroundWorker and instantly continues to return statement (with success = true), but then it just finishes, and nothing else happens (returned message never reaches webpage).
I can see page from other browser/user and everything works ok, but this particular thread is locked for several minutes (not entire 10-30 min, as it's get unlocked after about 5 min - timeout?)
My question is, what I did wrong and how to fix it. I expect backgroundWorker to just run in the background, and free user to move around page wherever he wish. Also making an entry in database and making some external application just fetch it and do all the work (in real background) is not an option for me.
Do not use Background worker like this. Yes, the work will be done within another thread, but still in scope of that web request. Web requests are not ment to be alive for 30 minutes, there are plenty of things that can go wrong (timeouts, app pool restart, other IIS behaviour..)
If you have this type of long-running task, you should do it in some worker - windows service, maybe console application, etc. and from web you just start it (console) or set it to be done (message queue, azure queue)
Also, i hope you are not locking database (you method IsDbLocked()) for 30 minutes? Just do your import in transaction and use proper isolation level (read commited) so DB work normally all the time and the changes are there instantly when import finishes.
I'm developing MVC3 based web application
... so I put it in BackgroundWorker
BackgroundWorker is designed to work with Windows (forms) application; to achieve similar in web application, use Task.Factory.StartNew or Thread (more primitive)

Displaying a form locks up mono

I've got a mono app written in c# and executed on a Mac using "mono myapp.exe"
The app itself is a "Windows Application" when viewed from the project properties, but it doesn't always show a window. In program.cs, there is a static Main:
static void Main(string[] args) {
UserClient client = new UserClient();
client.Start(args);
}
public class UserClient {
public void Start(string[] args) {
// Talk to server, listen for instructions, etc.
....
// Launch the "Stay Alive" thread
// Just a thread that Sleeps/Loops watching for an exit command; mainly used to keep the process alive
}
}
Inside the UserClient's Start method, there is a piece of code that continuously monitors a server which gives it instructions to do things. One of the things it does is optionally displays a message using a windows form.
When the server instructs the process to display a message, it instantiates a form, displays it using frm.ShowDialog() and then after 30 seconds, a timer on the form runs Close() and the frm then gets disposed. However, when this happens, on my Mac I see an application title bar saying "mono" and a new icon on my dock bar for the mono app. After about 2 minutes the mono process in Activity Monitor shows "Not Responding." This eventually will prevent the user from logging out, shutting down, etc. (because Mac OS can't kill mono gracefully).
ON THE OTHER HAND... if the server never tells the process to display that form, everything runs fine and dandy: a dock icon never shows up (which is good!), mono title bar never shows up and the mono process continues to run happily, not preventing the system from shutting down or rebooting.
Anyone experienced this or have ideas on what's causing it? My guess is that it's a new GUI thread being created by the form which isn't ever being shutdown and is somehow causing a lockup, though I'm unsure of how to handle it.
Thanks for any suggestions.
Update:
Here's some code to easily reproduce and see this happening. I realize that this seems kind of "non-standard." Having said that, the below works perfectly in a Windows environment and provides the desired result of not showing an icon in the task area except when showing a message. Currently, using Application.Run and simply doing frm.ShowDialog() produce exactly the same result.
In the end what we need is to be able to display the form, then destroy the form and any associated icon from the dock. I suspect the GUI is starting a thread which isn't ever being disposed, which is why the dock icon remains. Is there a way to make sure the GUI thread is taken care of?
static class Program {
static void Main() {
StartupClass s = new StartupClass();
s.start();
}
}
public class StartupClass {
Thread stayAliveThread;
public void start() {
// Stay alive thread
stayAliveThread = new Thread(stayAliveLoop);
stayAliveThread.Start();
// This shows a form and would normally be used to display temporary and brief messages to the user. Close the message and you'll see the undesired functionality.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
Application.Exit();
Application.ExitThread();
}
/// <summary>
/// Keep the app alive.
/// </summary>
private void stayAliveLoop() {
while (true) {
Thread.Sleep(10000);
// In the real project this method monitors the server and performs other tasks, only sometimes displaying a message.
}
}
}
I feel I'm missing several things. Most notably
[STAThread]
static void Main(string[] args) { //....
Also see this answer: Windows Forms and ShowDialog problem
I can't see anything like initializing message loop for windowed application. I.e. in windows forms case something like Application.Run(). If you do not have it, no wonder application freezes. In any case, posting more code could be helpful, as stated in comment.
In the end, I couldn't resolve this. I created a process that launched another app which displayed the message form. Not really a true answer, but the solution I had to go with.

Categories