Index: stable/HeuristicLab.Scripting/3.3/ExecutableScript.cs
===================================================================
--- stable/HeuristicLab.Scripting/3.3/ExecutableScript.cs	(revision 13277)
+++ stable/HeuristicLab.Scripting/3.3/ExecutableScript.cs	(revision 13277)
@@ -0,0 +1,139 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Threading;
+using HeuristicLab.Common;
+using HeuristicLab.Core;
+using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
+
+namespace HeuristicLab.Scripting {
+  [Item("Executable Script", "An executable script.")]
+  [StorableClass]
+  public abstract class ExecutableScript : Script {
+    private Thread scriptThread;
+    private DateTime lastUpdateTime;
+
+    [Storable]
+    private TimeSpan executionTime;
+    public TimeSpan ExecutionTime {
+      get { return executionTime; }
+      protected set {
+        executionTime = value;
+        OnExecutionTimeChanged();
+      }
+    }
+
+    public bool Running { get; protected set; }
+
+    #region Construction & Cloning
+    [StorableConstructor]
+    protected ExecutableScript(bool deserializing) : base(deserializing) { }
+    protected ExecutableScript(ExecutableScript original, Cloner cloner)
+      : base(original, cloner) {
+      executionTime = original.executionTime;
+    }
+    protected ExecutableScript()
+      : base() {
+      executionTime = TimeSpan.Zero;
+    }
+    protected ExecutableScript(string code)
+      : base(code) {
+      executionTime = TimeSpan.Zero;
+    }
+    #endregion
+
+    #region Execution
+    protected abstract void ExecuteCode();
+
+    public virtual void Execute() {
+      if (Running)
+        throw new InvalidOperationException("Script is already running");
+
+      ExecutionTime = TimeSpan.Zero;
+      Exception ex = null;
+      var timer = new System.Timers.Timer(250) { AutoReset = true };
+      timer.Elapsed += timer_Elapsed;
+      try {
+        Running = true;
+        OnScriptExecutionStarted();
+        lastUpdateTime = DateTime.UtcNow;
+        timer.Start();
+        ExecuteCode();
+      } catch (Exception e) {
+        ex = e;
+      } finally {
+        timer.Elapsed -= timer_Elapsed;
+        timer.Stop();
+        timer.Dispose();
+        ExecutionTime += DateTime.UtcNow - lastUpdateTime;
+        Running = false;
+        OnScriptExecutionFinished(ex);
+      }
+    }
+
+    public virtual void ExecuteAsync() {
+      scriptThread = new Thread(() => {
+        try {
+          Execute();
+        } finally {
+          scriptThread = null;
+        }
+      });
+      scriptThread.SetApartmentState(ApartmentState.STA);
+      scriptThread.Start();
+    }
+
+    public virtual void Kill() {
+      if (!Running) return;
+
+      scriptThread.Abort();
+    }
+    #endregion
+
+    private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
+      var timer = (System.Timers.Timer)sender;
+      timer.Enabled = false;
+      DateTime now = DateTime.UtcNow;
+      ExecutionTime += now - lastUpdateTime;
+      lastUpdateTime = now;
+      timer.Enabled = true;
+    }
+
+    public event EventHandler ScriptExecutionStarted;
+    protected virtual void OnScriptExecutionStarted() {
+      var handler = ScriptExecutionStarted;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+
+    public event EventHandler<EventArgs<Exception>> ScriptExecutionFinished;
+    protected virtual void OnScriptExecutionFinished(Exception e) {
+      var handler = ScriptExecutionFinished;
+      if (handler != null) handler(this, new EventArgs<Exception>(e));
+    }
+
+    public event EventHandler ExecutionTimeChanged;
+    protected virtual void OnExecutionTimeChanged() {
+      var handler = ExecutionTimeChanged;
+      if (handler != null) handler(this, EventArgs.Empty);
+    }
+  }
+}
Index: stable/HeuristicLab.Scripting/3.3/HeuristicLab.Scripting-3.3.csproj
===================================================================
--- stable/HeuristicLab.Scripting/3.3/HeuristicLab.Scripting-3.3.csproj	(revision 13207)
+++ stable/HeuristicLab.Scripting/3.3/HeuristicLab.Scripting-3.3.csproj	(revision 13277)
@@ -92,4 +92,5 @@
   <ItemGroup>
     <Compile Include="CompilationException.cs" />
+    <Compile Include="ExecutableScript.cs" />
     <Compile Include="Scripts\CSharp\CSharpScript.cs" />
     <Compile Include="Scripts\Templates\CSharpScriptTemplate.cs" />
Index: stable/HeuristicLab.Scripting/3.3/Script.cs
===================================================================
--- stable/HeuristicLab.Scripting/3.3/Script.cs	(revision 13207)
+++ stable/HeuristicLab.Scripting/3.3/Script.cs	(revision 13277)
@@ -37,9 +37,5 @@
 namespace HeuristicLab.Scripting {
   [StorableClass]
-  public class Script : NamedItem, IProgrammableItem {
-    protected virtual string CodeTemplate {
-      get { return string.Empty; }
-    }
-
+  public abstract class Script : NamedItem, IProgrammableItem {
     #region Fields & Properties
     public static new Image StaticItemImage {
@@ -77,28 +73,14 @@
         compileErrors = new CompilerErrorCollection(original.compileErrors);
     }
-    public Script()
+    protected Script()
       : base("Script", "An empty script.") {
-      code = CodeTemplate;
     }
-    public Script(string code)
+    protected Script(string code)
       : this() {
       this.code = code;
-    }
-
-    public override IDeepCloneable Clone(Cloner cloner) {
-      return new Script(this, cloner);
     }
     #endregion
 
     #region Compilation
-    protected virtual CSharpCodeProvider CodeProvider {
-      get {
-        return new CSharpCodeProvider(
-          new Dictionary<string, string> {
-                {"CompilerVersion", "v4.0"}, // support C# 4.0 syntax
-              });
-      }
-    }
-
     protected virtual CompilerResults DoCompile() {
       var parameters = new CompilerParameters {
@@ -114,5 +96,10 @@
         .ToArray());
 
-      return CodeProvider.CompileAssemblyFromSource(parameters, code);
+      var codeProvider = new CSharpCodeProvider(
+        new Dictionary<string, string> {
+          { "CompilerVersion", "v4.0"} // support C# 4.0 syntax
+        });
+
+      return codeProvider.CompileAssemblyFromSource(parameters, code);
     }
 
Index: stable/HeuristicLab.Scripting/3.3/Scripts/CSharp/CSharpScript.cs
===================================================================
--- stable/HeuristicLab.Scripting/3.3/Scripts/CSharp/CSharpScript.cs	(revision 13207)
+++ stable/HeuristicLab.Scripting/3.3/Scripts/CSharp/CSharpScript.cs	(revision 13277)
@@ -23,5 +23,4 @@
 using System.Linq;
 using System.Reflection;
-using System.Threading;
 using HeuristicLab.Common;
 using HeuristicLab.Core;
@@ -32,10 +31,5 @@
   [Creatable(CreatableAttribute.Categories.Scripts, Priority = 100)]
   [StorableClass]
-  public class CSharpScript : Script, IStorableContent {
-    #region Constants
-    protected const string ExecuteMethodName = "Execute";
-    protected override string CodeTemplate { get { return ScriptTemplates.CSharpScriptTemplate; } }
-    #endregion
-
+  public class CSharpScript : ExecutableScript, IStorableContent {
     #region Fields & Properties
     private CSharpScriptBase compiledScript;
@@ -50,5 +44,5 @@
     #endregion
 
-    #region Construction & Initialization
+    #region Construction & Cloning
     [StorableConstructor]
     protected CSharpScript(bool deserializing) : base(deserializing) { }
@@ -57,7 +51,7 @@
       variableStore = cloner.Clone(original.variableStore);
     }
-    public CSharpScript() {
+    public CSharpScript()
+      : base(ScriptTemplates.CSharpScriptTemplate) {
       variableStore = new VariableStore();
-      Code = CodeTemplate;
     }
     public CSharpScript(string code)
@@ -82,5 +76,4 @@
 
     #region Compilation
-
     public override Assembly Compile() {
       DeregisterScriptEvents();
@@ -94,44 +87,12 @@
     #endregion
 
-    private Thread scriptThread;
-    public virtual void ExecuteAsync() {
+    protected override void ExecuteCode() {
       if (compiledScript == null) return;
-      scriptThread = new Thread(() => {
-        Exception ex = null;
-        try {
-          OnScriptExecutionStarted();
-          compiledScript.Execute(VariableStore);
-        }
-        catch (Exception e) {
-          ex = e;
-        }
-        finally {
-          scriptThread = null;
-          OnScriptExecutionFinished(ex);
-        }
-      });
-      scriptThread.SetApartmentState(ApartmentState.STA);
-      scriptThread.Start();
-    }
 
-    public virtual void Kill() {
-      if (scriptThread == null) return;
-      scriptThread.Abort();
+      compiledScript.Execute(VariableStore);
     }
 
     protected virtual void CompiledScriptOnConsoleOutputChanged(object sender, EventArgs<string> e) {
       OnConsoleOutputChanged(e.Value);
-    }
-
-    public event EventHandler ScriptExecutionStarted;
-    protected virtual void OnScriptExecutionStarted() {
-      var handler = ScriptExecutionStarted;
-      if (handler != null) handler(this, EventArgs.Empty);
-    }
-
-    public event EventHandler<EventArgs<Exception>> ScriptExecutionFinished;
-    protected virtual void OnScriptExecutionFinished(Exception e) {
-      var handler = ScriptExecutionFinished;
-      if (handler != null) handler(this, new EventArgs<Exception>(e));
     }
 
