ConsoleRestartManager.cs :  » Script » IronRuby » Microsoft » Scripting » Hosting » Shell » Remote » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Script » IronRuby 
IronRuby » Microsoft » Scripting » Hosting » Shell » Remote » ConsoleRestartManager.cs
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

#if !SILVERLIGHT // Remoting

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace Microsoft.Scripting.Hosting.Shell.Remote{
    /// <summary>
    /// Supports detecting the remote runtime being killed, and starting up a new one.
    /// 
    /// Threading model:
    /// 
    /// ConsoleRestartManager creates a separate thread on which to create and execute the consoles. 
    /// There are usually atleast three threads involved:
    /// 
    /// 1. Main app thread: Instantiates ConsoleRestartManager and accesses its APIs. This thread has to stay 
    ///    responsive to user input and so the ConsoleRestartManager APIs cannot be long-running or blocking.
    ///    Since the remote runtime process can terminate asynchronously, the current RemoteConsoleHost can 
    ///    change at any time (if auto-restart is enabled). The app should typically not care which instance of 
    ///    RemoteConsoleHost is currently being used. The flowchart of this thread is:
    ///        Create ConsoleRestartManager
    ///        ConsoleRestartManager.Start
    ///        Loop:
    ///            Respond to user input | Send user input to console for execution | BreakExecution | RestartConsole | GetMemberNames
    ///        ConsoleRestartManager.Terminate
    ///    TODO: Currently, BreakExecution and GetMemberNames are called by the main thread synchronously.
    ///    Since they execute code in the remote runtime, they could take arbitrarily long. We should change
    ///    this so that the main app thread can never be blocked indefinitely.
    ///
    /// 2. Console thread: Dedicated thread for creating RemoteConsoleHosts and executing code (which could
    ///    take a long time or block indefinitely).
    ///        Wait for ConsoleRestartManager.Start to be called
    ///        Loop:
    ///            Create RemoteConsoleHost
    ///            Wait for signal for:
    ///                 Execute code | RestartConsole | Process.Exited
    ///
    /// 3. CompletionPort async callbacks:
    ///        Process.Exited | Process.OutputDataReceived | Process.ErrorDataReceived
    /// 
    /// 4. Finalizer thred
    ///    Some objects may have a Finalize method (which possibly calls Dispose). Not many (if any) types
    ///    should have a Finalize method.
    /// 
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors")] // TODO: This is public only because the test (RemoteConsole.py) needs it to be so. The test should be rewritten
    public abstract class ConsoleRestartManager {
        private RemoteConsoleHost _remoteConsoleHost;
        private Thread _consoleThread;
        private bool _exitOnNormalExit;
        private bool _terminating;

        /// <summary>
        /// Accessing _remoteConsoleHost from a thread other than console thread can result in race.
        /// If _remoteConsoleHost is accessed while holding _accessLock, it is guaranteed to be
        /// null or non-disposed.
        /// </summary>
        private object _accessLock = new object();

        /// <summary>
        /// This is created on the "creating thread", and goes on standby. Start needs to be called for activation.
        /// </summary>
        /// <param name="exitOnNormalExit">A host might want one of two behaviors:
        /// 1. Keep the REPL loop alive indefinitely, even when a specific instance of the RemoteConsoleHost terminates normally
        /// 2. Close the REPL loop when an instance of the RemoteConsoleHost terminates normally, and restart the loop
        ///    only if the instance terminates abnormally.</param>
        public ConsoleRestartManager(bool exitOnNormalExit) {
            _exitOnNormalExit = exitOnNormalExit;
            _consoleThread = new Thread(Run);
            _consoleThread.Name = "Console thread";
        }

        protected object AccessLock { get { return _accessLock; } }

        public Thread ConsoleThread { get { return _consoleThread; } }

        protected RemoteConsoleHost CurrentConsoleHost { get { return _remoteConsoleHost; } }

        public abstract RemoteConsoleHost CreateRemoteConsoleHost();

        /// <summary>
        /// Needs to be called for activation.
        /// </summary>
        public void Start() {
            Debug.Assert(Thread.CurrentThread != _consoleThread);

            if (_consoleThread.IsAlive) {
                throw new InvalidOperationException("Console thread is already running.");
            }
            _consoleThread.Start();
        }

        private void Run() {
#if DEBUG
            try {
                RunWorker();
            } catch (Exception e) {
                Debug.Assert(false, "Unhandled exception on console thread:\n\n" + e.ToString());
            }
#else
            RunWorker();
#endif
        }

        private void RunWorker() {
            Debug.Assert(Thread.CurrentThread == _consoleThread);

            while (true) {
                RemoteConsoleHost remoteConsoleHost = CreateRemoteConsoleHost();

                // Reading _terminating and setting of _remoteConsoleHost should be done atomically. 
                // Terminate() does the reverse operation (setting _terminating reading _remoteConsoleHost) atomically
                lock (_accessLock) {
                    if (_terminating) {
                        return;
                    }

                    _remoteConsoleHost = remoteConsoleHost;
                }

                try {
                    try {
                        int exitCode = remoteConsoleHost.Run(new string[0]);

                        if (_exitOnNormalExit && exitCode == 0) {
                            return;
                        }
                    } catch (RemoteRuntimeStartupException) {
                    }
                } finally {
                    lock (_accessLock) {
                        remoteConsoleHost.Dispose();
                        _remoteConsoleHost = null;
                    }
                }
            }
        }

        // TODO: We have to catch all exceptions as we are executing user code in the remote runtime, and we cannot control what 
        // exception it may throw. This could be fixed if we built our own remoting channel which returned an error code
        // instead of propagating exceptions back from the remote runtime.
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public IList<string> GetMemberNames(string expression) {
            Debug.Assert(Thread.CurrentThread != _consoleThread);

            lock (_accessLock) {
                if (_remoteConsoleHost == null) {
                    return null;
                }

                ScriptEngine engine = _remoteConsoleHost.Engine;
                try {
                    ScriptScope scope = _remoteConsoleHost.ScriptScope;
                    ObjectOperations operations = engine.CreateOperations(scope);
                    ScriptSource src = engine.CreateScriptSourceFromString(expression, SourceCodeKind.Expression);
                    return operations.GetMemberNames(src.ExecuteAndWrap(scope));
                } catch {
                    return null;
                }
            }
        }

        public void BreakExecution() {
            Debug.Assert(Thread.CurrentThread != _consoleThread);

            lock (_accessLock) {
                if (_remoteConsoleHost == null) {
                    return;
                }

                try {
                    _remoteConsoleHost.AbortCommand();
                } catch (System.Runtime.Remoting.RemotingException) {
                    // The remote runtime may be terminated or non-responsive
                }
            }
        }

        public void RestartConsole() {
            Debug.Assert(Thread.CurrentThread != _consoleThread);

            lock (_accessLock) {
                if (_remoteConsoleHost == null) {
                    return;
                }

                _remoteConsoleHost.Terminate(0);
            }
        }

        /// <summary>
        /// Request (from another thread) the console REPL loop to terminate
        /// </summary>
        public void Terminate() {
            Debug.Assert(Thread.CurrentThread != _consoleThread);

            lock (_accessLock) {
                _terminating = true;
                _remoteConsoleHost.Terminate(0);
            }
            
            _consoleThread.Join();
        }
    }
}

#endif
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.