예제 #1
0
        /// <summary>
        /// Called when exception happens in a GUI routine
        /// </summary>
        private void OnGuiUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            bool   userLevel;
            string message = ExceptionMessage.GetUserMessage(e.Exception, out userLevel);

            if (userLevel)
            {
                // TODO FIX NOW would really like to find the window with focus, and not always use the main window...
                MainWindow.Focus();
                MainWindow.StatusBar.LogError(message);
                e.Handled = true;
            }
            else
            {
                var feedbackSent = AppLog.SendFeedback("Unhandled Exception in GUI\r\n" + e.Exception.ToString(), true);
                var dialog       = new PerfView.Dialogs.UnhandledExceptionDialog(MainWindow, e.Exception, feedbackSent);
                var ret          = dialog.ShowDialog();
                // If it returns, it means that the user has opted to continue.
                e.Handled = true;
            }
        }
예제 #2
0
        /// <summary>
        /// This starts long running work.  It is called on the GUI thread.
        /// Only one piece of work can be running at a time (this is simply to keep the
        /// model the user sees simple).
        ///
        /// If finally_ is present, it will be run at EndWork time (But before the
        /// 'response' delegate passed to EndWork).   Logically it is a finally clause associated
        /// with the work (but not the 'response')  Unlike 'response' this action
        /// will occur under ALL conditions, including cancellation.   it is used for other
        /// GUID indications that work is in progress.
        ///
        /// If 'finally_' is present it will be executed on the GUI thread.
        /// </summary>
        public void StartWork(string message, Action work, Action finally_ = null)
        {
            // We only call this from the GUI thread
            if (Dispatcher.Thread != Thread.CurrentThread)
            {
                throw new InvalidOperationException("Work can only be started from the UI thread.");
            }

            // Because we are only called on the GUI thread, there is no race.
            if (m_work != null)
            {
                // PerfViewLogger.Log.DebugMessage("Must cancel " + m_workMessage + " before starting " + message);
                LogError("Must first cancel: " + m_workMessage + " before starting " + message);
                return;
            }
            // PerfViewLogger.Log.DebugMessage("Starting Working " + message);
            m_abortStarted      = false;
            m_abortDidInterrupt = false;
            m_endWorkStarted    = false;
            m_endWorkCompleted  = false;
            m_worker            = null;
            m_finally           = finally_;
            if (m_parentWindow == null)
            {
                m_parentWindow = Helpers.AncestorOfType <Window>(this);
            }
            if (m_parentWindow != null)
            {
                m_origCursor          = m_parentWindow.Cursor;
                m_parentWindow.Cursor = System.Windows.Input.Cursors.Wait;
            }

            // Update GUI state
            m_workTimeSec            = 0;
            m_startTime              = DateTime.Now;
            m_timer.IsEnabled        = true;
            m_CancelButton.IsEnabled = true;
            m_workMessage            = message;
            m_ProgressText.Text      = "Working";
            Background = m_blinkColor;
            var completeMessage = "Started: " + message;

            Status = completeMessage;
            LogWriter.WriteLine(completeMessage);
            m_loggedStatus = false;

            // This part may take a little bit of time, so we pass it off to another
            // thread (to keep the UI responsive, and then when it is done call
            // back (this.BeginInvoke), to finish it off.
            var currentCulture   = CultureInfo.CurrentCulture;
            var currentUICulture = CultureInfo.CurrentUICulture;
            var workSemaphore    = new SemaphoreSlim(1);

            workSemaphore.Wait();
            m_work = Task.Run(() =>
            {
                // Wait for the m_work variable to actually get assigned
                workSemaphore.Wait();

                var oldCulture = Tuple.Create(CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture);
                try
                {
                    Thread.CurrentThread.CurrentCulture   = currentCulture;
                    Thread.CurrentThread.CurrentUICulture = currentUICulture;

                    try
                    {
                        try
                        {
                            m_worker = Thread.CurrentThread;    // At this point we can be aborted.
                            // If abort was called before m_worker was initialized we need to kill this thread ourselves.
                            if (m_abortStarted)
                            {
                                throw new ThreadInterruptedException();
                            }
                            work();
                            Debug.Assert(m_endWorkStarted, "User did not call EndWork before returning from work body.");
                        }
                        catch (Exception ex)
                        {
                            EndWork(delegate()
                            {
                                if (!(ex is ThreadInterruptedException))
                                {
                                    bool userLevel;
                                    var errorMessage = ExceptionMessage.GetUserMessage(ex, out userLevel);
                                    if (userLevel)
                                    {
                                        LogError(errorMessage);
                                    }
                                    else
                                    {
                                        Log(errorMessage);
                                        LogError("An exceptional condition occurred, see log for details.");
                                    }
                                }
                            });
                        }
                        Debug.Assert(m_worker == null || m_abortStarted);     // EndWork should have been called and nulled this out.

                        // If we started an abort, then a thread-interrupt might happen at any time until the abort is completed.
                        // Thus we should wait around until the abort completes.
                        if (m_abortStarted)
                        {
                            while (!m_abortDidInterrupt)
                            {
                                Thread.Sleep(1);
                            }
                        }
                    }
                    catch (ThreadInterruptedException) { }      // we 'expect' ThreadInterruptedException so don't let them leak out.

                    // Cancellation completed, means that the thread is dead.   We don't allow another work item on this StatusBar
                    // The current thread is dead.

                    Debug.Assert(m_endWorkStarted);
                    if (m_abortStarted)
                    {
                        Log("Cancellation Complete on thread " + Thread.CurrentThread.ManagedThreadId + " : (Elapsed Time: " + Duration.TotalSeconds.ToString("f3") + " sec)");
                    }
                }
                finally
                {
                    Thread.CurrentThread.CurrentCulture   = oldCulture.Item1;
                    Thread.CurrentThread.CurrentUICulture = oldCulture.Item2;
                }
            });

            // Now that m_work is assigned, allow the operation to proceed
            workSemaphore.Release();

            SignalPropertyChange(nameof(IsWorking));
            SignalPropertyChange(nameof(IsNotWorking));
        }