private Type CompileScenarioRecursively(CompilationContext ctx) { #if FORCE_SINGLE_THREADED Application.DoEvents(); #endif // vault modifications guard if (!ctx.StillInTouch()) throw new CompilerOutOfTouchException(); // type var t_parent = ctx.PrevSuccessfulType("scenario") ?? typeof(CompiledScenario); (t_parent == typeof(CompiledScenario) ^ ctx.CumulativeCompilation).AssertTrue(); var t = _mod.DefineType(ctx.RequestName(Vault.GetClassName()), TA.Public, t_parent); #if TRACE try { #endif // constructors var ctorDesignMode = t.DefineConstructor(MA.PublicCtor, CallingConventions.Standard, new[] { typeof(IVault) }); ctorDesignMode.DefineParameter(1, ParmA.None, "scenario"); ctorDesignMode.il().ld_args(2).basector(typeof(CompiledScenario), typeof(IVault)).ret(); var ctorRuntimeMode = t.DefineConstructor(MA.PublicCtor, CallingConventions.Standard, new[] { typeof(IVault), typeof(IVault) }); ctorRuntimeMode.DefineParameter(1, ParmA.None, "scenario"); ctorRuntimeMode.DefineParameter(2, ParmA.None, "repository"); ctorRuntimeMode.il().ld_args(3).basector(typeof(CompiledScenario), typeof(IVault), typeof(IVault)).ret(); // the [Version(...)] attribute for reflection-based analysis t.SetCustomAttribute(new CustomAttributeBuilder( typeof(VersionAttribute).GetConstructor(new[] { typeof(String), typeof(ulong) }), new object[] { Vault.Id.ToString(), Vault.Revision })); // the [Seq(...)] attribute for synchronization and namespacing purposes t.SetCustomAttribute(new CustomAttributeBuilder( typeof(SeqAttribute).GetConstructor(new[] { typeof(int) }), new object[] { ctx.WorkerSeq })); // trivial overrides t.DefineOverrideReadonly(typeof(CompiledScenario).GetProperty("Version")) .il() .ldstr(Vault.Id.ToString()) .newobj(typeof(Guid), typeof(String)) .ldc_i8(Vault.Revision) .newobj(typeof(Version), typeof(Guid), typeof(ulong)) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Name")) .il() .ldstr(Vault.GetClassName() + "_seq" + ctx.WorkerSeq) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("VPath")) .il() .call(typeof(VPath).GetProperty("Empty").GetGetMethod()) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Revision")) .il() .ldc_i8(Vault.Revision) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Seq")) .il() .ldc_i4(ctx.WorkerSeq) .ret(); // factory initialization var parentFactory = ctx.PrevSuccessfulType("factory") ?? typeof(NodeFactory); ctx.FactoryType = _mod.DefineType(ctx.RequestName(t.Name + "_factory"), TA.Public, parentFactory); ctx.FactoryType.SetCustomAttribute(new CustomAttributeBuilder( typeof(SeqAttribute).GetConstructor(new[] { typeof(int) }), new object[] { ctx.WorkerSeq })); // setting up the factory method ctx.Factory = ctx.FactoryType.DefineConstructor(MA.PublicCtor, CallingConventions.Standard, new Type[0]); ctx.Factory.il().ldarg(0).basector(parentFactory); // complex overrides (the main work is here, below the stack) if (ctx.CompilingBaseline) { // baseline compilation involves creating classes and properties // for every single node and svd/fla in the scenario ImplementCreateChildren(ctx, t, Vault); ImplementCreateProperties(ctx, t, Vault); } else { var cum = ctx.ScheduledCumulations; MTLog.Say(String.Format("Scheduled cumulations: [{0}]{1}", cum.Count, cum.Count == 0 ? String.Empty : Environment.NewLine + cum.Select(kvp => String.Format("[{0}] {1}{2}", kvp.Value.Length, kvp.Key, cum.Count == 0 ? String.Empty : Environment.NewLine + kvp.Value.Select(co => String.Format(">>{0}: {1}", co.Reason, co.Subject) ).StringJoin(Environment.NewLine)) ).StringJoin(Environment.NewLine))); // cumulative compilation only should regenerate pieces of code // that are affected by events mentioned in ctx.NormalizedChangeSet cum.Keys.ForEach(host => CompileNode(ctx, host)); } // factory finalization ctx.Factory.il().ret(); var t_factory_created = ctx.FactoryType.CreateType(); _types.Add(t_factory_created); var createNodeFactory = t.DefineOverride(typeof(CompiledScenario).GetMethod("CreateNodeFactory", BF.All)); createNodeFactory.il().newobj(t_factory_created).ret(); // finalize the type var t_created = t.CreateType(); _types.Add(t_created); return t_created; #if TRACE } finally { // attempts to finalize the type so that we can dump an assembly to disk even after exceptions if (!t.IsCreated()) { Action<Action> neverFail = code => {try{code();}catch{}}; neverFail(() => t.DefineOverrideReadonly(typeof(CompiledScenario).GetProperty("Version")).il().ldc_i4(0).ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Name")).il().ldnull().ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("VPath")).il().ldnull().ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Revision")).il().ldc_i8(0).ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Seq")).il().ldc_i4(0).ret()); neverFail(() => t.DefineOverride(typeof(CompiledNode).GetMethod("CreateChildren", BF.All)).il().ret()); neverFail(() => t.DefineOverride(typeof(CompiledNode).GetMethod("CreateProperties", BF.All)).il().ret()); neverFail(() => ctx.CCs[t].il().ret()); neverFail(() => ctx.CPs[t].il().ret()); neverFail(() => t.DefineOverride(typeof(CompiledScenario).GetMethod("CreateNodeFactory", BF.All)).il().ldnull().ret()); neverFail(() => t.CreateType()); } if (!ctx.FactoryType.IsCreated()) { Action<Action> neverFail = code => {try{code();}catch{}}; neverFail(() => ctx.Factory.il().ret()); neverFail(() => ctx.FactoryType.CreateType()); } } #endif }
private Type CompileNode(CompilationContext ctx, IBranch b) { #if FORCE_SINGLE_THREADED Application.DoEvents(); #endif // vault modifications guard if (!ctx.StillInTouch()) throw new CompilerOutOfTouchException(); // type var t_parent = ctx.PrevSuccessfulType(b.VPath) ?? typeof(CompiledNode); (t_parent == typeof(CompiledNode) ^ (ctx.CumulativeCompilation && ctx.ScheduledCumulations.ContainsKey(b))).AssertTrue(); var t = _mod.DefineType(ctx.RequestName(b.GetClassName()), TA.Public, t_parent); #if TRACE try { #endif // constructor var ctor = t.DefineConstructor(MA.PublicCtor, CallingConventions.Standard, typeof(CompiledNode).MkArray()); ctor.DefineParameter(1, ParmA.None, "parent"); ctor.il().ld_args(2).basector(typeof(CompiledNode), typeof(CompiledNode)).ret(); // the [VPath(...)] attribute for reflection-based analysis t.SetCustomAttribute(new CustomAttributeBuilder( typeof(VPathAttribute).GetConstructor(typeof(String).MkArray()), b.VPath.ToString().MkArray())); // the [Revision(...)] attribute for reflection-based analysis t.SetCustomAttribute(new CustomAttributeBuilder( typeof(RevisionAttribute).GetConstructor(new[] { typeof(ulong) }), new object[] { Vault.Revision })); // the [Seq(...)] attribute for synchronization and namespacing purposes t.SetCustomAttribute(new CustomAttributeBuilder( typeof(SeqAttribute).GetConstructor(new[] { typeof(int) }), new object[] { ctx.WorkerSeq })); // trivial overrides t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Name")) .il() .ldstr(b.GetClassName() + "_seq" + ctx.WorkerSeq) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("VPath")) .il() .ldstr(b.VPath) .newobj(typeof(VPath), typeof(String)) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Revision")) .il() .ldc_i8(Vault.Revision) .ret(); t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Seq")) .il() .ldc_i4(ctx.WorkerSeq) .ret(); ImplementCreateChildren(ctx, t, b); ImplementCreateProperties(ctx, t, b); // finalize the type var t_created = t.CreateType(); _types.Add(t_created); // create a factory method for this class var factoryName = b.GetClassName().Substring(b.GetClassName().LastIndexOf(".") + 1); var factoryMethod = ctx.FactoryType.DefineMethod("Create_" + ctx.RequestName(factoryName), MA.PublicStatic, typeof(CompiledNode), typeof(CompiledNode).MkArray()); factoryMethod.SetCustomAttribute(new CustomAttributeBuilder( typeof(VPathAttribute).GetConstructor(typeof(String).MkArray()), b.VPath.ToString().MkArray())); factoryMethod.il().ldarg(0).newobj(t_created, typeof(CompiledNode).MkArray()).ret(); // register self within the factory ctx.Factory.il() .ldarg(0) .ldstr(b.VPath.ToString()) .newobj(typeof(VPath), typeof(String)) .ldnull() .ldftn(factoryMethod) .newobj(typeof(Func<CompiledNode, CompiledNode>), typeof(Object), typeof(IntPtr)) .callvirt(typeof(NodeFactory).GetMethod("Register", BF.All)); return t_created; #if TRACE } finally { // attempts to finalize the type so that we can dump an assembly to disk after certain exceptions if (!t.IsCreated()) { Action<Action> neverFail = code => {try{code();}catch{}}; neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Name")).il().ldnull().ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("VPath")).il().ldnull().ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Revision")).il().ldc_i8(0).ret()); neverFail(() => t.DefineOverrideReadonly(typeof(CompiledNode).GetProperty("Seq")).il().ldc_i4(0).ret()); neverFail(() => t.DefineOverride(typeof(CompiledNode).GetMethod("CreateChildren", BF.All)).il().ret()); neverFail(() => t.DefineOverride(typeof(CompiledNode).GetMethod("CreateProperties", BF.All)).il().ret()); neverFail(() => ctx.CCs[t].il().ret()); neverFail(() => ctx.CPs[t].il().ret()); neverFail(() => t.CreateType()); } } #endif }
private void WorkerLogic(CompilationContext ctx) { #if TRACE var s_workingSet = 0L; var s_typesInDynamicAssembly = -1; var s_time = TimeSpan.Zero; #endif try { #if TRACE GC.Collect(); s_workingSet = Process.GetCurrentProcess().WorkingSet64; s_typesInDynamicAssembly = _types.Count(); s_time = Process.GetCurrentProcess().TotalProcessorTime; var firstLine = String.Format( "Entering the worker thread: rev={0} ({1} mode)", ctx.Revision, ctx.CompilingBaseline ? "baseline" : "cumulative"); var secondLine = String.Format( "The context is: revision={0}, seq={1}, lastSeq={2}", ctx.Revision, ctx.WorkerSeq, ctx.LastSuccessfulWorkerSeq); var thirdLine = "Compilation mode: " + (ctx.CompilingBaseline ? "baseline" : "cumulative"); var fourthLine = String.Format( "The changeset is: [{0}]{1}", ctx.Changeset.Length, ctx.Changeset.Length == 0 ? String.Empty : Environment.NewLine + ctx.Changeset.Select(e => String.Format(" *{0}: {1}", e.Reason, e.Subject)).StringJoin(Environment.NewLine)); var fifthLine = String.Format( "The normalized changeset is: [{0}]{1}", ctx.NormalizedChangeSet.Count, ctx.NormalizedChangeSet.Count == 0 ? String.Empty : Environment.NewLine + ctx.NormalizedChangeSet.Select(kvp => String.Format(" *{0}: {1}", kvp.Value, kvp.Key)).StringJoin(Environment.NewLine)); var finalMessage = String.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}", Environment.NewLine, firstLine, secondLine, thirdLine, fourthLine, fifthLine); MTLog.Say(finalMessage); #endif // was used to test empty thread footprint: ~64k // var t_scenario = _asm.GetTypes().FirstOrDefault(t => typeof(ICompiledScenario).IsAssignableFrom(t)); // t_scenario = t_scenario ?? CompileScenarioRecursively(ctx); var t_scenario = CompileScenarioRecursively(ctx); var pool = new CompiledScenarioCache(Vault, t_scenario); MTLog.Say("Waiting at the barrier, pending successfully completed"); lock (_barrier) { // final modification guard (btw, the compiler logic also has such guards: for every node being compiled) // if we fail here during baseline compilation - sigh... so much memory is gonna to be wasted if (!ctx.StillInTouch()) throw new CompilerOutOfTouchException(); MTLog.Say("Breached the barrier"); _isBroken = false; _brokenChangeSet = null; _isPermanentlyBroken = false; _compilingBaseline = false; _changeSet.Clear(); _lastResult = pool; _lastSuccessfulWorkerSeq = ctx.WorkerSeq; MTLog.Say("Successfully completed"); } } catch (Exception ex) { MTLog.Say("Waiting at the barrier, pending error: " + ex); lock (_barrier) { MTLog.Say("Breached the barrier"); _isBroken = true; _brokenChangeSet = ctx.Changeset; if (ctx.CompilingBaseline) _baselineCompilationFailures++; _isPermanentlyBroken = _baselineCompilationFailures >= MaxBaselineCompilationFailures; // don't touch _compilingBaseline -> it should remain unchanged // don't touch _changeSet -> it should remain unchanged _lastResult = null; MTLog.Say("Error: " + ex); } } finally { try { GC.Collect(); #if TRACE var f_WorkingSet = Process.GetCurrentProcess().WorkingSet64; var f_typesInDynamicAssembly = _types.Count(); var f_time = Process.GetCurrentProcess().TotalProcessorTime; var memDelta = (f_WorkingSet - s_workingSet) / 1024; var typesDelta = f_typesInDynamicAssembly - s_typesInDynamicAssembly; var timeDelta_sec = (f_time - s_time).Seconds; var timeDelta_ms = (f_time - s_time).Milliseconds; MTLog.Say(String.Format("Leaving the worker thread.{0}"+ "Time delta: {1}.{2} sec, memory delta: {3} Kb ({4} Kb total), types generated: {5}, avg memory per type: {6} Kb", Environment.NewLine, timeDelta_sec, timeDelta_ms, s_workingSet == 0 ? "??" : memDelta.ToString(), f_WorkingSet, s_typesInDynamicAssembly == -1 ? "??" : typesDelta.ToString(), (s_typesInDynamicAssembly == -1 || s_workingSet == 0 || typesDelta == 0) ? "??" : (memDelta / typesDelta).ToString())); #else MTLog.Say("Leaving the worker thread"); #endif } finally { _worker = null; _flag.Set(); } } }