source: trunk/CrypPlugins/WorkspaceManager/Execution/ExecutionEngine.cs @ 1637

Last change on this file since 1637 was 1637, checked in by kopal, 11 years ago
  • added a benchmarking function to the ExecutionEngine
File size: 15.6 KB
Line 
1/*                             
2   Copyright 2010 Nils Kopal, Viktor M.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections.Generic;
19using System.Linq;
20using System.Text;
21
22using WorkspaceManager.Model;
23using System.Threading;
24using System.Collections;
25using Cryptool.PluginBase;
26using System.Reflection;
27using Gears4Net;
28using System.Windows.Threading;
29
30namespace WorkspaceManager.Execution
31{
32    /// <summary>
33    /// Engine to execute a model of the WorkspaceManager
34    /// This class needs a WorkspaceManager to be instantiated
35    /// To run an execution process it also needs a WorkspaceModel
36    ///
37    /// This class uses Gears4Net to execute the plugins
38    /// </summary>
39    public class ExecutionEngine
40    {
41        private WorkspaceManager WorkspaceManagerEditor;
42        private Scheduler[] schedulers;
43        private WorkspaceModel workspaceModel;
44
45        public long ExecutedPluginsCounter { get; set; }
46        public bool BenchmarkPlugins { get; set; }
47        public long CheckInterval { get; set; }
48        public long GuiUpdateInterval { get; set; }
49
50        /// <summary>
51        /// Creates a new ExecutionEngine
52        /// </summary>
53        /// <param name="workspaceManagerEditor"></param>
54        public ExecutionEngine(WorkspaceManager workspaceManagerEditor)
55        {
56            WorkspaceManagerEditor = workspaceManagerEditor;
57        }
58
59        /// <summary>
60        /// Is this ExecutionEngine running?
61        /// </summary>
62        public bool IsRunning
63        {
64            get;
65            private set;
66        }
67
68        /// <summary>
69        /// Execute the given Model
70        /// </summary>
71        /// <param name="workspaceModel"></param>
72        public void Execute(WorkspaceModel workspaceModel)
73        {
74            this.workspaceModel = workspaceModel;
75
76            if (!IsRunning)
77            {
78                IsRunning = true;
79
80                //Here we create n = "ProcessorsCount * 2" Gears4Net schedulers
81                //We do this, because measurements showed that we get the best performance if we
82                //use this amount of schedulers
83                schedulers = new Scheduler[System.Environment.ProcessorCount*2];
84                for(int i=0;i<System.Environment.ProcessorCount*2;i++){
85                    schedulers[i] = new STAScheduler("Scheduler" + i);
86                }
87
88                //We have to reset all states of PluginModels, ConnectorModels and ConnectionModels:
89                workspaceModel.resetStates();
90
91                //The UpdateGuiProtocol is a kind of "daemon" which will update the view elements if necessary
92                UpdateGuiProtocol updateGuiProtocol = new UpdateGuiProtocol(schedulers[0], workspaceModel, this);
93                schedulers[0].AddProtocol(updateGuiProtocol);
94                updateGuiProtocol.Start();
95
96                //The CheckExecutableProtocl is also a kind of "daemon" which will check from time to time if a
97                //plugin can be executed again
98                CheckExecutableProtocol checkExecutableProtocol = new CheckExecutableProtocol(schedulers[0], workspaceModel, this);
99                schedulers[0].AddProtocol(checkExecutableProtocol);
100                checkExecutableProtocol.Start();
101
102                //The BenchmarkProtocl counts the amount of executed plugins per seconds and writes this to debug
103                if (this.BenchmarkPlugins)
104                {
105                    BenchmarkProtocol benchmarkProtocol = new BenchmarkProtocol(schedulers[0], this.workspaceModel, this);
106                    schedulers[0].AddProtocol(benchmarkProtocol);
107                    benchmarkProtocol.Start();
108                }
109
110                //Here we create for each PluginModel an own PluginProtocol
111                //By using round-robin we give each protocol to another scheduler to gain
112                //a good average load balancing of the schedulers
113                int counter=0;
114                foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
115                {
116                    PluginProtocol pluginProtocol = new PluginProtocol(schedulers[counter], pluginModel,this);
117                    pluginModel.PluginProtocol = pluginProtocol;
118                    schedulers[counter].AddProtocol(pluginProtocol);
119                    pluginModel.checkExecutable(pluginProtocol);
120                    pluginProtocol.Start();
121                    counter = (counter + 1) % (System.Environment.ProcessorCount*2);
122                }
123            }
124        }     
125     
126        /// <summary>
127        /// Stop the execution process:
128        /// calls shutdown on all schedulers + calls stop() on each plugin
129        /// </summary>
130        public void Stop()
131        {
132            IsRunning = false;
133            //First stop all Gears4Net Schedulers
134            foreach (Scheduler scheduler in schedulers)
135            {
136                scheduler.Shutdown();
137            }
138            //Secondly stop alle plugins
139            foreach(PluginModel pluginModel in workspaceModel.AllPluginModels)
140            {
141                pluginModel.Plugin.Stop();
142            }           
143        }
144
145        /// <summary>
146        /// Pause the execution
147        /// </summary>
148        public void Pause()
149        {
150            //not implemented yet
151        }
152
153        /// <summary>
154        /// Use the logger of the WorkspaceManagerEditor
155        /// </summary>
156        /// <param name="message"></param>
157        /// <param name="level"></param>
158        public void GuiLogMessage(string message, NotificationLevel level)
159        {           
160            WorkspaceManagerEditor.GuiLogMessage(message, level);
161        }           
162    }
163 
164    /// <summary>
165    /// Message send to scheduler for a Plugin to trigger the Execution
166    /// </summary>
167    public class MessageExecution : MessageBase
168    {
169        public PluginModel PluginModel;
170    }
171
172    /// <summary>
173    /// A Protocol for updating the GUI in time intervals
174    /// </summary>
175    public class UpdateGuiProtocol : ProtocolBase
176    {
177        private WorkspaceModel workspaceModel;
178        private ExecutionEngine executionEngine;     
179
180        /// <summary>
181        /// Create a new protocol. Each protocol requires a scheduler which provides
182        /// a thread for execution.
183        /// </summary>
184        /// <param name="scheduler"></param>
185        public UpdateGuiProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
186            : base(scheduler)
187        {
188            this.workspaceModel = workspaceModel;
189            this.executionEngine = executionEngine;           
190        }
191
192        /// <summary>
193        /// The main function of the protocol
194        /// </summary>
195        /// <param name="stateMachine"></param>
196        /// <returns></returns>
197        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
198        {
199            while (this.executionEngine.IsRunning)
200            {
201                yield return Timeout(this.executionEngine.GuiUpdateInterval, HandleUpdateGui);
202            }
203        }
204
205        /// <summary>
206        /// Handler function for a message.
207        /// This handler must not block, because it executes inside the thread of the scheduler.
208        /// </summary>
209        /// <param name="msg"></param>
210        private void HandleUpdateGui()
211        {
212            //Get the gui Thread
213            this.workspaceModel.WorkspaceManagerEditor.Presentation.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
214            {
215                foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
216                {
217                    if (pluginModel.GuiNeedsUpdate)
218                    {
219                        //executionEngine.GuiLogMessage("UpdateGui for \"" + pluginModel.Name + "\"", NotificationLevel.Debug);
220                        pluginModel.GuiNeedsUpdate = false;
221                        pluginModel.paint();
222                        if (pluginModel.UpdateableView != null)
223                        {
224                            pluginModel.UpdateableView.update();
225                        }
226                    }
227                }
228                foreach (ConnectionModel connectionModel in workspaceModel.AllConnectionModels)
229                {
230                    if (connectionModel.GuiNeedsUpdate)
231                    {
232                        if (connectionModel.UpdateableView != null)
233                        {
234                            connectionModel.UpdateableView.update();
235                        }
236                    }
237                }
238            }
239            , null);
240        }
241    }
242
243    /// <summary>
244    /// A Protocol for checking if plugins are executable in time intervals
245    /// </summary>
246    public class CheckExecutableProtocol : ProtocolBase
247    {
248        private WorkspaceModel workspaceModel;
249        private ExecutionEngine executionEngine;
250     
251        /// <summary>
252        /// Create a new protocol. Each protocol requires a scheduler which provides
253        /// a thread for execution.
254        /// </summary>
255        /// <param name="scheduler"></param>
256        public CheckExecutableProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
257            : base(scheduler)
258        {
259            this.workspaceModel = workspaceModel;
260            this.executionEngine = executionEngine;
261        }
262
263        /// <summary>
264        /// The main function of the protocol
265        /// </summary>
266        /// <param name="stateMachine"></param>
267        /// <returns></returns>
268        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
269        {
270            while (this.executionEngine.IsRunning)
271            {
272                yield return Timeout(this.executionEngine.CheckInterval, HandleCheckExecutable);
273            }
274        }
275
276        /// <summary>
277        /// Handler function for a message.
278        /// This handler must not block, because it executes inside the thread of the scheduler.
279        /// </summary>
280        /// <param name="msg"></param>
281        private void HandleCheckExecutable()
282        {
283            foreach (PluginModel pluginModel in workspaceModel.AllPluginModels)
284            {
285                pluginModel.checkExecutable(pluginModel.PluginProtocol);
286            }
287        }
288       
289    }
290
291    /// <summary>
292    /// A Protocol for checking if plugins are executable in time intervals
293    /// </summary>
294    public class BenchmarkProtocol : ProtocolBase
295    {
296        private WorkspaceModel workspaceModel;
297        private ExecutionEngine executionEngine;
298
299        /// <summary>
300        /// Create a new protocol. Each protocol requires a scheduler which provides
301        /// a thread for execution.
302        /// </summary>
303        /// <param name="scheduler"></param>
304        public BenchmarkProtocol(Scheduler scheduler, WorkspaceModel workspaceModel, ExecutionEngine executionEngine)
305            : base(scheduler)
306        {
307            this.workspaceModel = workspaceModel;
308            this.executionEngine = executionEngine;
309        }
310
311        /// <summary>
312        /// The main function of the protocol
313        /// </summary>
314        /// <param name="stateMachine"></param>
315        /// <returns></returns>
316        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
317        {
318            while (this.executionEngine.IsRunning)
319            {
320                yield return Timeout(1000, HandleBenchmark);
321            }
322        }
323
324        /// <summary>
325        /// Handler function for a message.
326        /// This handler must not block, because it executes inside the thread of the scheduler.
327        /// </summary>
328        /// <param name="msg"></param>
329        private void HandleBenchmark()
330        {
331            this.workspaceModel.WorkspaceManagerEditor.GuiLogMessage("Executing at about " + this.executionEngine.ExecutedPluginsCounter + " Plugins/s", NotificationLevel.Debug);
332            this.executionEngine.ExecutedPluginsCounter = 0;
333        }
334
335    }
336
337    /// <summary>
338    /// A Protocol for a PluginModel
339    /// </summary>
340    public class PluginProtocol : ProtocolBase
341    {
342        private PluginModel pluginModel;
343        private ExecutionEngine executionEngine;
344
345        /// <summary>
346        /// Create a new protocol. Each protocol requires a scheduler which provides
347        /// a thread for execution.
348        /// </summary>
349        /// <param name="scheduler"></param>
350        public PluginProtocol(Scheduler scheduler, PluginModel pluginModel,ExecutionEngine executionEngine)
351            : base(scheduler)
352        {
353            this.pluginModel = pluginModel;
354            this.executionEngine = executionEngine;
355        }
356
357        /// <summary>
358        /// The main function of the protocol
359        ///
360        /// states are here:
361        ///
362        ///     PreExecution -> Execution -> PostExecution
363        ///        /\                           |
364        ///         |---------------------------|
365        ///         
366        /// </summary>
367        /// <param name="stateMachine"></param>
368        /// <returns></returns>
369        public override System.Collections.Generic.IEnumerator<ReceiverBase> Execute(AbstractStateMachine stateMachine)
370        {
371            while (this.executionEngine.IsRunning)
372            {
373                yield return Receive<MessageExecution>(null, this.HandleExecute);             
374            }
375        }
376
377        /// <summary>
378        /// Call the execution function of the wrapped IPlugin
379        /// </summary>
380        /// <param name="msg"></param>
381        private void HandleExecute(MessageExecution msg)
382        {
383            msg.PluginModel.Plugin.PreExecution();
384
385            //executionEngine.GuiLogMessage("HandleExecute for \"" + msg.PluginModel.Name + "\"", NotificationLevel.Debug);
386            //Fill the plugins Inputs with data
387            foreach (ConnectorModel connectorModel in pluginModel.InputConnectors)
388            {
389                if (connectorModel.HasData)
390                {
391                    PropertyInfo propertyInfo = pluginModel.Plugin.GetType().GetProperty(connectorModel.PropertyName);
392                    propertyInfo.SetValue(pluginModel.Plugin, connectorModel.Data, null);
393                    connectorModel.HasLastData = true;
394                    connectorModel.LastData = connectorModel.Data;
395                    connectorModel.Data = null;
396                    connectorModel.HasData = false;
397                    connectorModel.InputConnection.Active = false;                   
398                }
399            }
400           
401            msg.PluginModel.Plugin.Execute();
402
403            msg.PluginModel.Plugin.PostExecution();
404
405            if (this.executionEngine.BenchmarkPlugins)
406            {
407                this.executionEngine.ExecutedPluginsCounter++;                               
408            }
409        }
410     
411    }
412}
Note: See TracBrowser for help on using the repository browser.