bin/projects/dbatools/dbatools/Runspace/RunspaceContainer.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;

namespace Sqlcollaborative.Dbatools.Runspace
{
    /// <summary>
    /// Class that contains the logic necessary to manage a unique runspace
    /// </summary>
    public class RunspaceContainer
    {
        private ScriptBlock Script;

        private PowerShell Runspace;

        /// <summary>
        /// The name of the runspace.
        /// </summary>
        public readonly string Name;

        /// <summary>
        /// The Guid of the running Runspace
        /// </summary>
        public Guid RunspaceGuid
        {
            get { return Runspace.Runspace.InstanceId; }
        }

        /// <summary>
        /// Sets the script to execute in the runspace. Will NOT take immediate effect. Only after restarting the runspace will it be used.
        /// </summary>
        /// <param name="Script">The scriptblock to execute</param>
        public void SetScript(ScriptBlock Script)
        {
            this.Script = Script;
        }

        /// <summary>
        /// The state the runspace currently is in.
        /// </summary>
        public DbaRunspaceState State
        {
            get { return _State; }
        }
        private DbaRunspaceState _State = DbaRunspaceState.Stopped;

        /// <summary>
        /// Starts the Runspace.
        /// </summary>
        public void Start()
        {
            if ((Runspace != null) && (State == DbaRunspaceState.Stopped))
            {
                Kill();
            }

            if (Runspace == null)
            {
                Runspace = PowerShell.Create();
                try { SetName(Runspace.Runspace); }
                catch { }
                Runspace.AddScript(Script.ToString());
                _State = DbaRunspaceState.Running;
                try { Runspace.BeginInvoke(); }
                catch { _State = DbaRunspaceState.Stopped; }
            }
        }

        /// <summary>
        /// Sets the name on a runspace. This WILL FAIL for PowerShell v3!
        /// </summary>
        /// <param name="Runspace">The runspace to be named</param>
        private void SetName(System.Management.Automation.Runspaces.Runspace Runspace)
        {
            #if (NORUNSPACENAME)

            #else
            Runspace.Name = Name;
            #endif
        }

        /// <summary>
        /// Gracefully stops the Runspace
        /// </summary>
        public void Stop()
        {
            _State = DbaRunspaceState.Stopping;

            int i = 0;

            // Wait up to the limit for the running script to notice and kill itself
            if ((Runspace != null) && (Runspace.Runspace != null))
            {
                while ((Runspace.Runspace.RunspaceAvailability != RunspaceAvailability.Available) && (i < (10 * RunspaceHost.StopTimeoutSeconds)))
                {
                    i++;
                    Thread.Sleep(100);
                }
            }

            Kill();
        }

        /// <summary>
        /// Very ungracefully kills the runspace. Use only in the most dire emergency.
        /// </summary>
        public void Kill()
        {
            if (Runspace != null)
            {
                try { Runspace.Runspace.Close(); }
                catch { }
                Runspace.Dispose();
                Runspace = null;
            }

            _State = DbaRunspaceState.Stopped;
        }

        /// <summary>
        /// Signals the registered runspace has stopped execution
        /// </summary>
        public void SignalStopped()
        {
            _State = DbaRunspaceState.Stopped;
        }

        /// <summary>
        /// Creates a new runspace container with the basic information needed
        /// </summary>
        /// <param name="Name">The name of the Runspace</param>
        /// <param name="Script">The code using the runspace logic</param>
        public RunspaceContainer(string Name, ScriptBlock Script)
        {
            this.Name = Name.ToLower();
            this.Script = Script;
        }
    }
}