/** **************************************************************************************** * Converts the given unsigned 64 bit integer value to a string representation.<br> * Negative numbers have to be converted to positive values when invoking this method. * The maximum number of digits written are 20. The given buffer has to be large * enough to receive the characters. The method does not check any overflow within the * given character buffer. * * \note This method is used internally by various AString conversion methods. It is * advisable to use AString methods to convert integer values. * * @param value The integer value to convert. * @param buffer The character array to write the value to. Depending on the value * given, a maximum of 20 characters need to be allocated prior to * invoking this method. * @param idx The index within the character array to write the value to. * @param minDigits The minimum number of digits to append. * If given value has less digits, '0' characters are prepended. * The given value is cut to the range 1..20 (max digits of an * unsigned 64 bit integer). * @param sizeHint The maximum number of digits found in the value. This is a hint * for optimizing the loop. If -1 is provided, the number is detected. * If a number is provided that is lower than the actual 'log10(value)', * the method produces unpredictable results. * * @return The index of the new new end of the buffer. ******************************************************************************************/ public int IntegerToString(ulong value, char[] buffer, int idx, int minDigits, int sizeHint) { // adjust minDigits to 1..20 and maxDigits to 1..minDigits if (minDigits < 1) { minDigits = 1; } if (minDigits > 20) { minDigits = 20; } if (sizeHint < minDigits) { sizeHint = minDigits; } if (sizeHint > 20) { sizeHint = 20; } int actDigit = sizeHint; bool printStarted = false; while (--actDigit >= 0) { // rest is zeros? if (value == 0) { while (actDigit >= 0) { if (!(printStarted |= actDigit-- < minDigits)) { continue; } buffer[idx++] = '0'; } return(idx); } // get next d ulong actBase = pow10_0to19[actDigit]; int digitValue = (int)(value / actBase); ALIB.ASSERT(digitValue <= 9); // did we hit i for the first time if (!(printStarted |= (digitValue != 0) || actDigit < minDigits)) { continue; } // print the digit buffer[idx++] = (char)(48 + digitValue); // 48= '0' // next value = value % actBase; } // return me for concatenated operations return(idx); }
public void Log_Threads() { UT_INIT(); Log.AddDebugLogger(); Log.SetDomain("TEST/THREAD1", Scope.Method); Log.SetVerbosity(Log.DebugLogger, Verbosity.Verbose, "TEST/THREAD1"); // if this gets commented out, the test might crash. At least the console will // become scrambled! //Log.LOX.Lock.setUnsafe( true ); Thread thread = new Thread(new ThreadStart(testDifferentThreadMethod)) { Name = "Thread2" }; thread.Start(); for (int i = 0; i < 50; i++) { Log.Info("This is the main thread ");// + i ); ALIB.SleepMillis(4); } }
void SampleALibReport() { Console.WriteLine("Sample: ALib Report via using ALox"); Log.AddDebugLogger(); Log.SetDomain("/SAMPLE", Scope.Filename); Log.SetVerbosity(Log.DebugLogger, Verbosity.Info, ""); Log.Info("Method \"Log.AddDebugLogger()\" by default creates a replacement for ALibs'\n" + "error/warning reporter. If this is a debug compiliation, let's have a try and\n" + "create 3 Messages:"); Report.GetDefault().PushHaltFlags(false, false); ALIB.ERROR("This is an error report!"); ALIB.WARNING("And this is a warning!"); AString illegalAccess = new AString(10); illegalAccess._("1234"); illegalAccess.SetCharAt_NC(5, '5'); Report.GetDefault().PopHaltFlags(); Log.SetVerbosity(Log.DebugLogger, Verbosity.Verbose, ALox.InternalDomains); ALIB.REPORT(2, "This is an ALib Report. Types other than '0' and '1' are user defined.\n" + "Verbosity of ALox.InternalDomains has to be increased to see them when using" + " ALoxReportWriter."); Log.Info("Note the domain prefix '" + ALox.InternalDomains.ToString() + "'. This addresses " + "the tree of internal domains\nof the Lox, which the report writer is just " + "using for ALib reports."); Log.RemoveDebugLogger(); }
public void testDifferentThreadMethod() { Log.SetDomain("TEST/THREAD2", Scope.Method); Log.SetVerbosity(Log.DebugLogger, Verbosity.Verbose, "TEST/THREAD2"); for (int i = 0; i < 20; i++) { Log.Info("This is a different Thread. Cnt= " + i); ALIB.SleepMillis(3); } }
/** **************************************************************************************** * This method must (may) be called prior to using the ALox library, e.g. at the beginning of * the main() method of an application. It is OK, to call this method more than once, which * allows independent code blocks (e.g. libraries) to bootstrap ALox without interfering. * But only the first call is effective and may be used to set the command line arguments * as configuration plug-in. * * In the C# version of the AWorx library, the invocation of this method is optional. * However, it is good practice to invoke this method in the main() method of a process * and provide the command line arguments. See \ref cs::aworx::lib::ALIB::Init "ALIB.Init" * for more information on the configuration parameters. * * @param useEnv If true, a * \ref cs::aworx::lib::config::EnvironmentPlugIn "EnvironmentPlugIn" * is attached to the * \ref cs::aworx::lib::ALIB::Config "ALIB.Config" singleton. Hence, * environment variables are read and potentially overwrite * configuration variables in other configuration plug-ins.<br> * Defaults to true. * @param args Parameter which in the standard case is taken from C/C++ main() * method providing the command line arguments. * If arguments are provided, a * \ref cs::aworx::lib::config::CommandLinePlugIn "CommandLinePlugIn" * is attached to the * \ref cs::aworx::lib::ALIB::Config "ALIB.Config" singleton. Hence, * command line options are read and those potentially overwrite * configuration variables in other configuration plug-ins.<br> * Defaults to null. ******************************************************************************************/ public static void Init(bool useEnv = true, String[] args = null) { if (isInitialized) { return; } isInitialized = true; // initialize ALIB ALIB.Init(useEnv, args); }
public bool RemovePlugin(ConfigurationPlugIn plugin) { for (int i = 0; i < plugins.Count; i++) { if (plugins[i].plugIn == plugin) { plugin.Parent = null; plugins.RemoveAt(i); return(true); } } ALIB.WARNING("No Plug-in was removed "); return(false); }
public void ThreadLock_Threaded() { UT_INIT(); Log.SetVerbosity(new ConsoleLogger(), Verbosity.Verbose, "/"); Log.MapThreadName("UnitTest"); Log.SetDomain("TestTLock", Scope.Filename); Log.SetVerbosity("CONSOLE", Verbosity.Verbose, "ALIB"); ThreadLock aLock = new ThreadLock(); Test_ThreadLock_SharedInt shared = new Test_ThreadLock_SharedInt(); Log.Info("starting thread locked"); aLock.Acquire(); Test_ThreadLock_TestThreadParams tParam = new Test_ThreadLock_TestThreadParams(aLock, 10, 1, true, shared); Thread thread = new Thread(new ParameterizedThreadStart(Test_ThreadLock_Test_run)); thread.Name = "A Thread"; thread.Start(tParam); Log.Info("We wait 1100 ms. This should give a warning! "); ALIB.SleepMillis(1100); aLock.Release(); // wait until t ended while (thread.IsAlive) { ALIB.SleepMillis(1); } // now we do the same with a higher wait limit, no erro should come aLock.waitWarningTimeLimitInMillis = 5; aLock.Acquire(); tParam = new Test_ThreadLock_TestThreadParams(aLock, 10, 1, true, shared); thread = new Thread(new ParameterizedThreadStart(Test_ThreadLock_Test_run)); thread.Start(tParam); Log.Info("We wait 1 ms. This should NOT give a warning! "); ALIB.SleepMillis(1); aLock.Release(); // wait until t ended while (thread.IsAlive) { ALIB.SleepMillis(1); } }
/** ******************************************************************************************** * Releases ownership of this object. If Acquire() was called multiple times before, the same * number of calls to this method have to be performed to release ownership. **********************************************************************************************/ public void Release() { // are we in unsafe mode? if (mutex == null) { // not locked if (lockMode == LockMode.Recursive && lockCount == 0) { ALIB.ERROR("Release() without Acquire() (unsafe mode). This must never happen, check your code, set lock to safe mode!"); } // we are still decreasing the lockCount lockCount = lockMode == LockMode.Recursive ? lockCount - 1 : 0; // end of unsafe version of this method return; } // synchronize on mutex lock ( mutex ) { // not locked if (lockCount == 0) { ALIB.ERROR("Release() without Acquire(). This must never happen, check your code!"); } // decreasing the lockCount lockCount = lockMode == LockMode.Recursive ? lockCount - 1 : 0; // release and notify next waiting thread if (lockCount == 0) { owner = null; Monitor.Pulse(mutex); #if DEBUG acquirementLineNumber = -1; acquirementSourcefile = null; acquirementMethodName = null; #endif } } // synchronized }
// ############################################################################################# // static entrance (Main) // ############################################################################################# static void Main(string[] args) { ALIB.Init(true, args); // create us AloxSamples samples = new AloxSamples(); // do some release logging tests. Console.WriteLine("PRINT: Debug logging:"); samples.DebugLogging(); ALoxSampleReset(); // do some release logging tests. Console.WriteLine("PRINT: Release logging:"); samples.ReleaseLogging(); ALoxSampleReset(); // do some performance tests. Console.WriteLine("PRINT: Performance test (debug logging):"); samples.PerformanceTest(); ALoxSampleReset(); // do some performance tests. Console.WriteLine("PRINT: Performance test (release logging):"); samples.PerformanceTestRL(); ALoxSampleReset(); // test class TextFileLogger Console.WriteLine("PRINT: test class TextFileLogger:"); samples.TextFileLogger(); ALoxSampleReset(); // test class terminal test (colors and styles) Console.WriteLine("PRINT: Colors (depending on detected terminal):"); samples.ColorTest(); ALoxSampleReset(); Console.WriteLine("PRINT: Thats it!"); // sample ALib report facility through ALox samples.SampleALibReport(); ALIB.TerminationCleanUp(); }
public void ThreadLock_HeavyLoad() { UT_INIT(); Log.SetVerbosity(new ConsoleLogger(), Verbosity.Verbose, "/"); Log.MapThreadName("UnitTest"); Log.SetDomain("TestTLock", Scope.Filename); ThreadLock aLock = new ThreadLock(); // uncomment this for unsafe mode // lock.setUnsafe( true ); Test_ThreadLock_SharedInt shared = new Test_ThreadLock_SharedInt(); int holdTime = 0; int repeats = 5000; bool verbose = false; Test_ThreadLock_TestThreadParams p1 = new Test_ThreadLock_TestThreadParams(aLock, holdTime, repeats, verbose, shared); Test_ThreadLock_TestThreadParams p2 = new Test_ThreadLock_TestThreadParams(aLock, holdTime, repeats, verbose, shared); Test_ThreadLock_TestThreadParams p3 = new Test_ThreadLock_TestThreadParams(aLock, holdTime, repeats, verbose, shared); Log.Info("starting three threads"); Thread t1 = new Thread(new ParameterizedThreadStart(Test_ThreadLock_Test_run)); Thread t2 = new Thread(new ParameterizedThreadStart(Test_ThreadLock_Test_run)); Thread t3 = new Thread(new ParameterizedThreadStart(Test_ThreadLock_Test_run)); t1.Start(p1); t2.Start(p2); t3.Start(p3); // wait until all ended while (t1.IsAlive || t2.IsAlive || t3.IsAlive) { ALIB.SleepMillis(1); } Log.Info("All threads ended. Shared value=" + shared.val); UT_TRUE(shared.val == 0); }
/** ******************************************************************************************** * If parameter is true, the whole locking system is disabled. The only objective here is to * to gain execution speed, as thread synchronization causes relatively expensive system calls. * Use this method only if you are 100% sure that your (otherwise) critical section are executed * in a single threaded environment. And: "relative expensive" means: they are not really * expensive. This is provided only for the rare case that your critical section is very, * very frequently executed. * * @param safeness Determines if this object should use a mutex (\c Safeness.Safe) * or just do nothing (\c Safeness.Unsafe). *************************************************************************************************/ public void SetSafeness(Safeness safeness) { // are we in unsafe mode? if (mutex == null) { // already locked? ALIB Error if (lockCount != 0) { ALIB.ERROR("Cannot switch safeness mode while already locked. Current mode: unsafe, requested mode: " + safeness.ToString()); return; } // switch on? if (safeness == Safeness.Safe) { mutex = new Object(); } // end of unsafe version of this method return; } // synchronize on mutex lock ( mutex ) { // already locked? ALIB Error if (owner != null) { ALIB.ERROR("Cannot switch safeness mode while already locked. Current mode: safe, requested mode: " + safeness.ToString()); return; } // switch off? if (safeness == Safeness.Unsafe) { mutex = null; } } }
public void InsertPlugin(ConfigurationPlugIn plugin, int priority) { int i; for (i = 0; i < plugins.Count; i++) { ALIB.ASSERT_ERROR(plugins[i].prio != priority, "Configuration.InsertPlugin(): Plug-in with same priority exists"); if (plugins[i].prio < priority) { break; } } PluginAndPrio pap = new PluginAndPrio(); pap.plugIn = plugin; pap.prio = priority; plugins.Insert(i, pap); plugin.Parent = this; }
public void Test_ThreadLock_Test_run(Object o) { Test_ThreadLock_TestThreadParams p = o as Test_ThreadLock_TestThreadParams; for (int i = 0; i < p.repeats; i++) { if (p.verbose) { Log.Info("Thread: " + Thread.CurrentThread.Name + " acuiring lock..."); } p.aLock.Acquire(); if (p.verbose) { Log.Info("Thread: " + Thread.CurrentThread.Name + " has lock."); } int sVal = ++p.shared.val; ALIB.SleepMillis(p.holdTime); p.shared.val = sVal - 1; if (p.verbose) { Log.Info("Thread: " + Thread.CurrentThread.Name + " releasing lock."); } p.aLock.Release(); if (p.verbose) { Log.Info("Thread: " + Thread.CurrentThread.Name + " released lock."); } } p.result = 0; Log.Info("Thread: " + Thread.CurrentThread.Name + " terminates."); }
void Register(Lox lox, ContainerOp operation) { try { ALIB.Lock.Acquire(); // check if (lox == null) { ALIB.ERROR("null given"); return; } // remove if (operation == ContainerOp.Remove) { if (!loxes.Remove(lox)) { ALIB.WARNING("A lox named \"" + lox.GetName() + "\" could not be found for removal."); } } // insert else { foreach (Lox it in loxes) { if (it.GetName().Equals(lox.GetName( ))) { ALIB.ERROR("A lox named \"" + lox.GetName() + "\" was already registered. Registration ignored"); return; } } loxes.Add(lox); } } finally { ALIB.Lock.Release(); } }
// ############################################################################################# // Interface // ############################################################################################# /** ******************************************************************************************** * Thread which invokes this method gets registered as the current owner of this object, until the * same thread releases the ownership invoking Release(). * In the case that this object is already owned by another thread, the invoking thread is suspended * until ownership can be gained. * Multiple (nested) calls to this method are counted and the object is only released when the same * number of Release() calls have been made. * * @param cln (Optional) Caller info, compiler generated. Please omit. * @param csf (Optional) Caller info, compiler generated. Please omit. * @param cmn (Optional) Caller info, compiler generated. Please omit. **********************************************************************************************/ public void Acquire( [CallerLineNumber] int cln = 0, [CallerFilePath] String csf = "", [CallerMemberName] String cmn = "") { // are we in unsafe mode? if (mutex == null) { // we are still increasing the lockCount lockCount = lockMode == LockMode.Recursive ? lockCount + 1 : 1; // reached warning limit if (lockCount <= 0) { ALIB.ERROR("Unsafe mode: Counter invalid (<= 0): This should never happen. Set lock to safe mode!"); } else if (lockCount % RecursionWarningThreshold == 0) { ALIB.WARNING("Recursion depth " + lockCount + ". To prevent this, change ThreadSafe.recursionWarningThreshold or fix your code!"); } // end of unsafe version of this method return; } // get current thread Thread thisThread = Thread.CurrentThread; // synchronize on mutex lock ( mutex ) { // we already own the thread if (owner == thisThread) { // we are still increasing the lockCount lockCount = lockMode == LockMode.Recursive ? lockCount + 1 : 1; // reached warning limit if (lockCount % RecursionWarningThreshold == 0) { ALIB.WARNING("Recursion depth " + lockCount + ". To prevent this, change ThreadSafe.recursionWarningThreshold or fix your code!"); } return; } // we do not own this thread, wait until lock is free bool hasWarned = false; while (owner != null) { try { // wait unconditional if (waitWarningTimeLimitInMillis <= 0 || hasWarned) { Monitor.Wait(mutex); } // wait with time limit else { waitTime.Set(); Monitor.Wait(mutex, waitWarningTimeLimitInMillis); long time = waitTime.Age().InMillis(); if (time >= waitWarningTimeLimitInMillis) { hasWarned = true; ALIB.WARNING("Timeout (" + waitWarningTimeLimitInMillis + " ms). Change your codes critical section length if possible." + CString.NewLineChars + "This thread: " + thisThread.ManagedThreadId + "/" + thisThread.Name + CString.NewLineChars + "Owning thread: " + (owner != null ? (owner.ManagedThreadId + "/" + owner.Name) : "null") #if DEBUG + CString.NewLineChars + "Location of acquirement: " + acquirementSourcefile + ":" + acquirementLineNumber + " " + acquirementMethodName + "()" #endif ); } } } catch (Exception) {} // ignore spurious wakeups } // take control owner = thisThread; #if DEBUG acquirementLineNumber = cln; acquirementSourcefile = csf; acquirementMethodName = cmn; #endif lockCount = 1; } // synchronized }
// ############################################################################################# // logText // ############################################################################################# /** ******************************************************************************************** * * The implementation of the abstract method of parent class TextLogger. Logs messages to the * application console and/or the VStudio output window. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param msg The log message * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. For * single line messages this is -1. **********************************************************************************************/ override protected void logText(Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber) { // loop over message, print the parts between the escape sequences Tokenizer msgParts = new Tokenizer(msg, '\x001B'); Substring actual = msgParts.Actual; Substring rest = msgParts.Rest; int column = 0; for (;;) { msgParts.Next(Whitespaces.Keep); // check if this is an ANSI sequence already if (rest.CharAtStart() == '[') { // read the 'm' int idx = rest.IndexOf('m'); if (idx < 0) // unknown ANSI Code { ALIB.WARNING("Unknown ANSI ESC Code "); textWriter.Write(actual.Buf, actual.Start, actual.Length()); continue; } column += actual.Length(); actual.End = rest.Start + idx; rest.Start += idx + 1; textWriter.Write(actual.Buf, actual.Start, actual.Length()); continue; } else { if (actual.IsNotEmpty()) { textWriter.Write(actual.Buf, actual.Start, actual.Length()); column += actual.Length(); } } // end of loop? if (!msgParts.HasNext()) { break; } // found an ESC sequence char c = rest.Consume(); // Colors bool isForeGround = true; if (c == 'C' || c == 'c') { isForeGround = c == 'c'; c = rest.Consume(); int colNo = c - '0'; ALIB.ASSERT_WARNING(colNo >= 0 && colNo <= 9, "Unknown ESC-c code"); // add bg colNo += isForeGround ? 0 : 10; // add light colNo += (isForeGround ? !IsBackgroundLight : IsBackgroundLight) ? 20 : 0; textWriter.Write(ansiCols[colNo]); } // Styles else if (c == 's') { // bold/italics style not supported in Windows console // reset all if (rest.Consume() == 'a') { textWriter.Write(ANSI_RESET); } } // auto tab / end of meta else if (c == 't' || c == 'A') { bool endOfMeta = c == 'A'; c = rest.Consume(); int extraSpace = c >= '0' && c <= '9' ? (int)(c - '0') : (int)(c - 'A') + 10; int tabStop = AutoSizes.Next(column, extraSpace); Util.WriteSpaces(textWriter, tabStop - column); column = tabStop; if (endOfMeta) { String msgPrefix; switch (verbosity) { case lox.Verbosity.Verbose: msgPrefix = MsgPrefixVerbose; break; case lox.Verbosity.Info: msgPrefix = MsgPrefixInfo; break; case lox.Verbosity.Warning: msgPrefix = MsgPrefixWarning; break; case lox.Verbosity.Error: msgPrefix = MsgPrefixError; break; default: msgPrefix = ""; break; } textWriter.Write(msgPrefix); } } // Link (we just colorize links here) else if (c == 'l') { textWriter.Write(rest.Consume() == 'S' ? (IsBackgroundLight ? ANSI_LIGHT_BLUE : ANSI_LIGHT_BLUE) : ANSI_STD_COL); } else { ALIB.WARNING("Unknown ESC code"); } } // write loop textWriter.WriteLine(MsgSuffix); }
public void Log_Data() { UT_INIT(); Log.AddDebugLogger(); Log.SetVerbosity(Log.DebugLogger, Verbosity.Verbose, ALox.InternalDomains); Log.SetDomain("Data", Scope.Method); // LogData Constructors { LogData ld; AString asnull = new AString(); ld = new LogData( ); UT_EQ(asnull, ld.StringValue); UT_EQ(0, ld.IntegerValue); UT_TRUE(ld.ObjectValue == null); ld = new LogData(3); UT_EQ(asnull, ld.StringValue); UT_EQ(3, ld.IntegerValue); UT_TRUE(ld.ObjectValue == null); ld = new LogData(3, this); UT_EQ(asnull, ld.StringValue); UT_EQ(3, ld.IntegerValue); UT_TRUE(ld.ObjectValue == this); ld = new LogData("ABC"); UT_EQ("ABC", ld.StringValue); UT_EQ(0, ld.IntegerValue); UT_TRUE(ld.ObjectValue == null); ld = new LogData("ABC", 0, this); UT_EQ("ABC", ld.StringValue); UT_EQ(0, ld.IntegerValue); UT_TRUE(ld.ObjectValue == this); ld = new LogData("ABC", 3); UT_EQ("ABC", ld.StringValue); UT_EQ(3, ld.IntegerValue); UT_TRUE(ld.ObjectValue == null); ld = new LogData("ABC", 3, this); UT_EQ("ABC", ld.StringValue); UT_EQ(3, ld.IntegerValue); UT_TRUE(ld.ObjectValue == this); } // without key Log.Store(null, Scope.Global); Log.Store(new LogData("Replaced"), Scope.Global); Log.Store(null, Scope.Global); Log.Store(null, Scope.Global); Log.Store(new LogData("Replaced"), Scope.Global); Log.Store(new LogData("Global"), Scope.Global); Log.Store(new LogData("Replaced"), Scope.ThreadOuter); Log.Store(new LogData("ThreadOuter"), Scope.ThreadOuter); Log.Store(new LogData("Replaced"), Scope.Path, 1); Log.Store(new LogData("Path1"), Scope.Path, 1); Log.Store(new LogData("Replaced"), Scope.Path); Log.Store(new LogData("Path"), Scope.Path); Log.Store(new LogData("Replaced"), Scope.Filename); Log.Store(new LogData("FileName"), Scope.Filename); Log.Store(new LogData("Replaced"), Scope.Method); Log.Store(new LogData("Method"), Scope.Method); Log.Store(new LogData("Replaced"), Scope.ThreadInner); Log.Store(new LogData("ThreadInner"), Scope.ThreadInner); LogData data = null; data = Log.Retrieve(Scope.Global); UT_EQ("Global", data.StringValue); data = Log.Retrieve(Scope.ThreadOuter); UT_EQ("ThreadOuter", data.StringValue); data = Log.Retrieve(Scope.Path, 1); UT_EQ("Path1", data.StringValue); data = Log.Retrieve(Scope.Path); UT_EQ("Path", data.StringValue); data = Log.Retrieve(Scope.Filename); UT_EQ("FileName", data.StringValue); data = Log.Retrieve(Scope.Method); UT_EQ("Method", data.StringValue); data = Log.Retrieve(Scope.ThreadInner); UT_EQ("ThreadInner", data.StringValue); // wit key Log.Store(new LogData("Replaced"), "mykey", Scope.Global); Log.Store(new LogData("Global"), "mykey", Scope.Global); Log.Store(new LogData("Replaced"), "mykey", Scope.ThreadOuter); Log.Store(new LogData("ThreadOuter"), "mykey", Scope.ThreadOuter); Log.Store(new LogData("Replaced"), "mykey", Scope.Path, 1); Log.Store(new LogData("Path1"), "mykey", Scope.Path, 1); Log.Store(new LogData("Replaced"), "mykey", Scope.Path); Log.Store(new LogData("Path"), "mykey", Scope.Path); Log.Store(new LogData("Replaced"), "mykey", Scope.Filename); Log.Store(new LogData("FileName"), "mykey", Scope.Filename); Log.Store(new LogData("Replaced"), "mykey", Scope.Method); Log.Store(new LogData("Method"), "mykey", Scope.Method); Log.Store(new LogData("Replaced"), "mykey", Scope.ThreadInner); Log.Store(new LogData("ThreadInner"), "mykey", Scope.ThreadInner); data = Log.Retrieve("mykey", Scope.Global); UT_EQ("Global", data.StringValue); data = Log.Retrieve("mykey", Scope.ThreadOuter); UT_EQ("ThreadOuter", data.StringValue); data = Log.Retrieve("mykey", Scope.Path, 1); UT_EQ("Path1", data.StringValue); data = Log.Retrieve("mykey", Scope.Path); UT_EQ("Path", data.StringValue); data = Log.Retrieve("mykey", Scope.Filename); UT_EQ("FileName", data.StringValue); data = Log.Retrieve("mykey", Scope.Method); UT_EQ("Method", data.StringValue); data = Log.Retrieve("mykey", Scope.ThreadInner); UT_EQ("ThreadInner", data.StringValue); // threaded Log.Store(new LogData("Main Thread Data"), Scope.ThreadOuter); Log.Store(new LogData("Main Thread Data, keyed"), "mykey", Scope.ThreadOuter); Thread thread = new Thread(new ThreadStart(StoreDataTestThreadRun)); thread.Start(); while (thread.IsAlive) { ; } ALIB.SleepMicros(1); data = Log.Retrieve(Scope.ThreadOuter); UT_EQ("Main Thread Data", data.StringValue); data = Log.Retrieve("mykey", Scope.ThreadOuter); UT_EQ("Main Thread Data, keyed", data.StringValue); }
public void Log_LogOnce() { UT_INIT(); Log.AddDebugLogger(); MemoryLogger ml = new MemoryLogger(); Log.SetVerbosity(ml, Verbosity.Verbose); Log.SetVerbosity(Log.DebugLogger, Verbosity.Verbose, ALox.InternalDomains); Log.SetDomain("ONCE", Scope.Method); //-------------------- associated to scope method----------------- for (int i = 0; i < 5; i++) { Log.Once(Verbosity.Info, "Once(Scope method) 1x", Scope.Method); } Log.Once(Verbosity.Info, "Once(Scope method) 1x", Scope.Method); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; //-------------------- associated to scope filename ----------------- for (int i = 0; i < 5; i++) { Log.Once("Subdom", Verbosity.Info, "Once(Scope filename) 4x", Scope.Filename, 0, 4); logOnceMethod(); } Log.Once(Verbosity.Info, "Once(Scope filename) 4x", Scope.Filename, 0, 4); UT_EQ(4, ml.CntLogs); ml.CntLogs = 0; //-------------------- associated to scope thread ----------------- Log.Once(Verbosity.Info, "Once(Scope.ThreadOuter) 2x - main thread", Scope.ThreadOuter, 0, 2); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Thread thread = new Thread(new ThreadStart(LogOnceTestThreadRun)); thread.Start(); while (thread.IsAlive) { ; } ALIB.SleepMicros(1); UT_EQ(2, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(Scope.ThreadOuter) 2x - main thread", Scope.ThreadOuter, 0, 2); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(Scope.ThreadOuter) 2x - main thread", Scope.ThreadOuter, 0, 2); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(Scope.ThreadOuter) 2x - main thread", Scope.ThreadOuter, 0, 2); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; // different group Log.Once("Once(group, Scope.ThreadOuter) 2x - main thread", "group", Scope.ThreadOuter, 0, 1); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once("Once(group, Scope.ThreadOuter) 2x - main thread", "group", Scope.ThreadOuter, 0, 1); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; //-------------------- associated to line (using group interface) ----------------- for (int i = 0; i < 5; i++) { Log.Once(Verbosity.Info, "Once(line) 1x"); } UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; for (int i = 0; i < 5; i++) { Log.Once(Verbosity.Info, "Once(line other) 2x", "", 2); } UT_EQ(2, ml.CntLogs); ml.CntLogs = 0; for (int i = 0; i < 5; i++) { Log.Once(Verbosity.Info, "Once(line other) 1x", "", 1); } UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; for (int i = 0; i < 5; i++) { Log.Once(Verbosity.Info, "Once(line other) 0x", "", 0); } UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; for (int i = 0; i < 5; i++) { Log.Once("Once(line) Just msg and number", 2); } UT_EQ(2, ml.CntLogs); ml.CntLogs = 0; for (int i = 0; i < 5; i++) { Log.Once("Once(line) Just msg "); } UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; //-------------------- associated to group ----------------- for (int i = 0; i < 5; i++) { Log.Once(Verbosity.Info, "Once(\"a group\") 1x", "a group"); } UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"a group\") 1x but tricked up", "a group", 2); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"a group\") 1x", "a group"); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"b group\") 1x", "b group"); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"b group\") 1x", "b group"); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"c group\") 2x", "c group", 2); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"c group\") 2x", "c group", 2); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"c group\") 2x", "c group", 2); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"a group\") 1x", "a group"); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"b group\") 1x", "b group"); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; Log.Once(Verbosity.Info, "Once(\"c group\") 2x", "c group", 2); UT_EQ(0, ml.CntLogs); ml.CntLogs = 0; // AString Log.Once(new AString("Once with AString")); UT_EQ(1, ml.CntLogs); ml.CntLogs = 0; //-------------------- Log every Nth ----------------- for (int i = 0; i < 10; i++) { Log.Once(Verbosity.Info, "Every 2nd ", -2); } UT_EQ(5, ml.CntLogs); ml.CntLogs = 0; for (int i = 0; i < 10; i++) { Log.Once(Verbosity.Info, "Every 3rd ", -3); } UT_EQ(4, ml.CntLogs); ml.CntLogs = 0; }
public void Lox_ScopeDomains() { UT_INIT(); // we have tell alox to include more directories in the scope path Log.ClearSourcePathTrimRules(Inclusion.Include, false); Log.SetSourcePathTrimRule("*/alox/src.cs/", Inclusion.Exclude); Lox lox = new Lox("ReleaseLox"); TextLogger consoleLogger = Lox.CreateConsoleLogger(); MemoryLogger ml = new MemoryLogger(); ml.MetaInfo.Format._()._("@%D#"); lox.SetVerbosity(ml, Verbosity.Verbose); lox.SetVerbosity(consoleLogger, Verbosity.Verbose); lox.SetVerbosity(consoleLogger, Verbosity.Verbose, ALox.InternalDomains); // scope global lox.SetDomain("REPLACE", Scope.Global); lox.Info("", ""); SDCHECK_RL("@/REPLACE#", ml); lox.SetDomain("GLOBAL", Scope.Global); lox.Info("", ""); SDCHECK_RL("@/GLOBAL#", ml); lox.SetDomain(null, Scope.Global); lox.Info("", ""); SDCHECK_RL("@/#", ml); // scope source lox.SetDomain("REPLACE", Scope.Filename); lox.Info("", ""); CICHECK_RL("@/REPLACE#", ml); lox.SetDomain("FILE", Scope.Filename); lox.Info("", ""); CICHECK_RL("@/FILE#", ml); // scope method lox.SetDomain("REPLACE", Scope.Method); lox.Info("", ""); CICHECK_RL("@/FILE/REPLACE#", ml); lox.SetDomain("Method", Scope.Method); lox.Info("", ""); CICHECK_RL("@/FILE/METHOD#", ml); lox.SetDomain("/ABS", Scope.Method); lox.Info("", ""); CICHECK_RL("@/ABS#", ml); // unset method with null lox.SetDomain(null, Scope.Method); lox.Info("", ""); CICHECK_RL("@/FILE#", ml); // unset method with "" lox.SetDomain("/METHOD", Scope.Method); lox.Info("", ""); CICHECK_RL("@/METHOD#", ml); lox.SetDomain("", Scope.Method); lox.Info("", ""); CICHECK_RL("@/FILE#", ml); lox.SetDomain("Method", Scope.Method); lox.Info("", ""); CICHECK_RL("@/FILE/METHOD#", ml); // source path lox.SetDomain("REPLACE", Scope.Path); lox.Info("", ""); CICHECK_RL("@/REPLACE/FILE/METHOD#", ml); lox.SetDomain("PATH", Scope.Path); lox.Info("", ""); CICHECK_RL("@/PATH/FILE/METHOD#", ml); lox.SetDomain("REPLACE", Scope.Path, 1); lox.Info("", ""); CICHECK_RL("@/REPLACE/PATH/FILE/METHOD#", ml); lox.SetDomain("PO1", Scope.Path, 1); lox.Info("", ""); CICHECK_RL("@/PO1/PATH/FILE/METHOD#", ml); lox.SetDomain("REPLACE", Scope.Path, 2); lox.Info("", ""); CICHECK_RL("@/REPLACE/PO1/PATH/FILE/METHOD#", ml); lox.SetDomain("PO2", Scope.Path, 2); lox.Info("", ""); CICHECK_RL("@/PO2/PO1/PATH/FILE/METHOD#", ml); lox.SetDomain("GLOBAL", Scope.Global); lox.Info("", ""); CICHECK_RL("@/GLOBAL/PO2/PO1/PATH/FILE/METHOD#", ml); // remove all previous scope domains lox.SetDomain("", Scope.Global); lox.SetDomain("", Scope.Path); lox.SetDomain("", Scope.Path, 1); lox.SetDomain("", Scope.Path, 2); lox.SetDomain("", Scope.Filename); lox.SetDomain("", Scope.Method); lox.Info("LOC", ""); SDCHECK_RL("@/LOC#", ml); // Thread-related lox.SetDomain("T_O", Scope.ThreadOuter); lox.Info("", ""); SDCHECK_RL("@/T_O#", ml); lox.SetDomain("GL", Scope.Global); lox.Info("", ""); SDCHECK_RL("@/GL/T_O#", ml); lox.SetDomain("MET", Scope.Method); lox.Info("", ""); CICHECK_RL("@/GL/T_O/MET#", ml); lox.Info("LOC", ""); CICHECK_RL("@/GL/T_O/MET/LOC#", ml); lox.SetDomain("T_I", Scope.ThreadInner); lox.Info("", ""); CICHECK_RL("@/GL/T_O/MET/T_I#", ml); lox.Info("LOC", ""); CICHECK_RL("@/GL/T_O/MET/LOC/T_I#", ml); lox.SetDomain("T_O2", Scope.ThreadOuter); lox.Info("", ""); CICHECK_RL("@/GL/T_O/T_O2/MET/T_I#", ml); lox.SetDomain("T_I2", Scope.ThreadInner); lox.Info("", ""); CICHECK_RL("@/GL/T_O/T_O2/MET/T_I/T_I2#", ml); lox.SetDomain("/T_O3", Scope.ThreadOuter); lox.Info("", ""); CICHECK_RL("@/T_O3/MET/T_I/T_I2#", ml); lox.SetDomain("/T_I3", Scope.ThreadInner); lox.Info("", ""); SDCHECK_RL("@/T_I3#", ml); lox.SetDomain("", Scope.Method); lox.Info("", ""); SDCHECK_RL("@/T_I3#", ml); lox.SetDomain("", Scope.ThreadInner); lox.Info("", ""); SDCHECK_RL("@/T_O3/T_I/T_I2#", ml); lox.RemoveThreadDomain("T_IXX", Scope.ThreadInner); lox.Info("", ""); SDCHECK_RL("@/T_O3/T_I/T_I2#", ml); lox.RemoveThreadDomain("T_I", Scope.ThreadInner); lox.Info("", ""); SDCHECK_RL("@/T_O3/T_I2#", ml); lox.RemoveThreadDomain("", Scope.ThreadInner); lox.Info("", ""); SDCHECK_RL("@/T_O3/T_I2#", ml); lox.SetDomain("", Scope.ThreadInner); lox.Info("", ""); SDCHECK_RL("@/T_O3#", ml); lox.SetDomain("", Scope.ThreadOuter); lox.Info("", ""); SDCHECK_RL("@/GL/T_O/T_O2#", ml); lox.SetDomain("T_O3", Scope.ThreadOuter); lox.Info("", ""); SDCHECK_RL("@/GL/T_O/T_O2/T_O3#", ml); lox.RemoveThreadDomain("T_O2", Scope.ThreadOuter); lox.Info("", ""); SDCHECK_RL("@/GL/T_O/T_O3#", ml); lox.RemoveThreadDomain("T_O", Scope.ThreadOuter); lox.Info("", ""); SDCHECK_RL("@/GL/T_O3#", ml); lox.SetDomain("", Scope.ThreadOuter); lox.Info("", ""); SDCHECK_RL("@/GL#", ml); lox.SetDomain("", Scope.Global); lox.Info("", ""); SDCHECK_RL("@/#", ml); // second thread Thread thread = new Thread(new ParameterizedThreadStart(DomainTestThreadRun_RL)); lox.SetDomain("THIS_THREAD", Scope.ThreadOuter); lox.SetDomain("OTHER_THREAD", Scope.ThreadOuter, thread); thread.Start(lox); while (thread.IsAlive) { ALIB.SleepMillis(1); } UT_EQ("@/OTHER_THREAD/DTT#", ml.MemoryLog); ml.MemoryLog._(); ml.AutoSizes.Reset(); lox.Info("ME", ""); UT_EQ("@/THIS_THREAD/ME#", ml.MemoryLog); ml.MemoryLog._(); ml.AutoSizes.Reset(); // cleanup lox.RemoveLogger(consoleLogger); lox.RemoveLogger(ml); }
public void Log_ScopeDomains() { UT_INIT(); // we have tell alox to include more directories in the scope path Log.ClearSourcePathTrimRules(Inclusion.Include, false); Log.SetSourcePathTrimRule("*/alox/src.cs/", Inclusion.Exclude); Log.AddDebugLogger(); MemoryLogger ml = new MemoryLogger(); ml.MetaInfo.Format._()._("@%D#"); Log.SetVerbosity(ml, Verbosity.Verbose); Log.SetVerbosity(Log.DebugLogger, Verbosity.Verbose, ALox.InternalDomains); Log.SetDomain("REPLACE", Scope.Global); Log.Info(""); SDCHECK("@/REPLACE#", ml); Log.SetDomain("GLOBAL", Scope.Global); Log.Info(""); SDCHECK("@/GLOBAL#", ml); Log.SetDomain(null, Scope.Global); Log.Info(""); SDCHECK("@/#", ml); // scope source Log.SetDomain("REPLACE", Scope.Filename); Log.Info(""); SDCHECK("@/REPLACE#", ml); Log.SetDomain("FILE", Scope.Filename); Log.Info(""); SDCHECK("@/FILE#", ml); // scope method Log.SetDomain("REPLACE", Scope.Method); Log.Info(""); SDCHECK("@/FILE/REPLACE#", ml); Log.SetDomain("METHOD", Scope.Method); Log.Info(""); SDCHECK("@/FILE/METHOD#", ml); Log.SetDomain("/ABS", Scope.Method); Log.Info(""); SDCHECK("@/ABS#", ml); // unset method with null Log.SetDomain(null, Scope.Method); Log.Info(""); SDCHECK("@/FILE#", ml); // unset method with "" Log.SetDomain("/METHOD", Scope.Method); Log.Info(""); SDCHECK("@/METHOD#", ml); Log.SetDomain("", Scope.Method); Log.Info(""); SDCHECK("@/FILE#", ml); Log.SetDomain("METHOD", Scope.Method); Log.Info(""); SDCHECK("@/FILE/METHOD#", ml); // source path Log.SetDomain("REPLACE", Scope.Path); Log.Info(""); SDCHECK("@/REPLACE/FILE/METHOD#", ml); Log.SetDomain("PATH", Scope.Path); Log.Info(""); SDCHECK("@/PATH/FILE/METHOD#", ml); Log.SetDomain("REPLACE", Scope.Path, 1); Log.Info(""); SDCHECK("@/REPLACE/PATH/FILE/METHOD#", ml); Log.SetDomain("PO1", Scope.Path, 1); Log.Info(""); SDCHECK("@/PO1/PATH/FILE/METHOD#", ml); Log.SetDomain("REPLACE", Scope.Path, 2); Log.Info(""); SDCHECK("@/REPLACE/PO1/PATH/FILE/METHOD#", ml); Log.SetDomain("PO2", Scope.Path, 2); Log.Info(""); SDCHECK("@/PO2/PO1/PATH/FILE/METHOD#", ml); Log.SetDomain("PO50", Scope.Path, 50); Log.Info(""); SDCHECK("@/PO50/PO2/PO1/PATH/FILE/METHOD#", ml); Log.SetDomain("GLOBAL", Scope.Global); Log.Info(""); SDCHECK("@/GLOBAL/PO50/PO2/PO1/PATH/FILE/METHOD#", ml); CS_ALox_domains_helper.help(); SDCHECK("@/GLOBAL/PO50/PO2/PO1/PATH/HFILE/HMETHOD#", ml); Log.Info(""); SDCHECK("@/GLOBAL/PO50/PO2/PO1/PATH/FILE/METHOD#", ml); //Log.LogConfig( "", Verbosity.Info, "Configuration now is:" ); ml.MemoryLog._(); ml.AutoSizes.Reset(); // remove all previous scope domains Log.SetDomain("", Scope.Global); Log.SetDomain("", Scope.Path); Log.SetDomain("", Scope.Path, 1); Log.SetDomain("", Scope.Path, 2); Log.SetDomain("", Scope.Path, 45); // same as 50 above! (test) Log.SetDomain("", Scope.Filename); Log.SetDomain("", Scope.Method); Log.Info("LOC", ""); SDCHECK("@/LOC#", ml); // Thread-related Log.SetDomain("T_O", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/T_O#", ml); Log.SetDomain("GL", Scope.Global); Log.Info("", ""); SDCHECK("@/GL/T_O#", ml); Log.SetDomain("MET", Scope.Method); Log.Info("", ""); SDCHECK("@/GL/T_O/MET#", ml); Log.SetDomain("MET", Scope.Method); Log.Info("LOC", ""); SDCHECK("@/GL/T_O/MET/LOC#", ml); Log.SetDomain("T_I", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/GL/T_O/MET/T_I#", ml); Log.Info("LOC", ""); SDCHECK("@/GL/T_O/MET/LOC/T_I#", ml); Log.SetDomain("T_O2", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/GL/T_O/T_O2/MET/T_I#", ml); Log.SetDomain("T_I2", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/GL/T_O/T_O2/MET/T_I/T_I2#", ml); Log.SetDomain("/T_O3", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/T_O3/MET/T_I/T_I2#", ml); Log.SetDomain("/T_I3", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/T_I3#", ml); Log.SetDomain("", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/T_O3/MET/T_I/T_I2#", ml); Log.RemoveThreadDomain("T_IXX", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/T_O3/MET/T_I/T_I2#", ml); Log.RemoveThreadDomain("T_I", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/T_O3/MET/T_I2#", ml); Log.RemoveThreadDomain("", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/T_O3/MET/T_I2#", ml); Log.SetDomain("", Scope.ThreadInner); Log.Info("", ""); SDCHECK("@/T_O3/MET#", ml); Log.SetDomain("", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/GL/T_O/T_O2/MET#", ml); Log.SetDomain("T_O3", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/GL/T_O/T_O2/T_O3/MET#", ml); Log.RemoveThreadDomain("T_O2", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/GL/T_O/T_O3/MET#", ml); Log.RemoveThreadDomain("T_O", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/GL/T_O3/MET#", ml); Log.SetDomain("", Scope.ThreadOuter); Log.Info("", ""); SDCHECK("@/GL/MET#", ml); Log.SetDomain("", Scope.Global); Log.Info("", ""); SDCHECK("@/MET#", ml); Log.SetDomain("", Scope.Method); Log.Info("", ""); SDCHECK("@/#", ml); // second thread Thread thread = new Thread(new ThreadStart(DomainTestThreadRun)); Log.SetDomain("THIS_THREAD", Scope.ThreadOuter); Log.SetDomain("OTHER_THREAD", Scope.ThreadOuter, thread); thread.Start(); while (thread.IsAlive) { ALIB.SleepMillis(1); } UT_EQ("@/OTHER_THREAD/DTT#", ml.MemoryLog); ml.MemoryLog._(); ml.AutoSizes.Reset(); Log.Info("ME", ""); UT_EQ("@/THIS_THREAD/ME#", ml.MemoryLog); ml.MemoryLog._(); ml.AutoSizes.Reset(); }
/** **************************************************************************************** * Removes an acquirer. * @param acquirerToRemove The acquirer to remove. * @return The new number of \e acquirers set. ******************************************************************************************/ public virtual int RemoveAcquirer(ThreadLock acquirerToRemove) { int count = 0; bool errNotFound = true; bool errWasAcquired = false; try { ALIB.Lock.Acquire(); #if DEBUG errWasAcquired = DbgCountAcquirements() != 0; #endif // search acquirer if (acquirers.IndexOf(acquirerToRemove) >= 0) { errNotFound = false; // switch off? if (acquirers.Count == 2) { ThreadLock acquirer1 = acquirers[0]; ThreadLock acquirer2 = acquirers[1]; if (acquirer1 == acquirerToRemove) { acquirer1 = null; } if (acquirer2 == acquirerToRemove) { acquirer2 = null; } // Aquire acquirers in their order of appearance if (acquirer1 != null) { acquirer1.Acquire(); } if (acquirer2 != null) { acquirer2.Acquire(); } SetSafeness(Safeness.Unsafe); acquirers.Remove(acquirerToRemove); if (acquirer2 != null) { acquirer2.Release(); } if (acquirer1 != null) { acquirer1.Release(); } } // just remove acquirer, keep mode else { acquirers.Remove(acquirerToRemove); } } count = acquirers.Count; } finally { ALIB.Lock.Release(); } ALIB.ASSERT_ERROR(!errNotFound, "Acquirer not found."); ALIB.ASSERT_ERROR(!errWasAcquired, "Aquired on release. Hint: Acquirers must acquire only when acquired themselves!"); return(count); }
/** **************************************************************************************** * Returns logger of given number. * @param no The number of the \e Logger to return. * @return The the \e Logger found with number \p no. ******************************************************************************************/ public Logger GetLogger(int no) { ALIB.ASSERT_ERROR(no >= 0 && no < (int)Data.Count, "Internal error: Illegal Logger Number"); return(Data[no].Logger); }
public void Ages() { UT_INIT(); Log.SetVerbosity(new ConsoleLogger(), Verbosity.Verbose, "/"); Log.MapThreadName("UnitTest"); Log.SetDomain("TickWatch", Scope.Method); TickWatch tt = new TickWatch(); // minimum time measuring { tt.Start(); tt.Sample(); tt.Reset(); // we need to do this once before, otherwise C# might be // very slow. Obviously the optimizer... tt.Start(); tt.Sample(); long ttAverageInNanos = tt.GetAverage().InNanos(); long ttAverageInMicros = tt.GetAverage().InMicros(); long ttAverageInMillis = tt.GetAverage().InMillis(); Log.Info("TickWatch minimum measurement nanos: " + ttAverageInNanos); Log.Info("TickWatch minimum measurement micros: " + ttAverageInMicros); Log.Info("TickWatch minimum measurement millis: " + ttAverageInMillis); UT_TRUE(ttAverageInNanos < 5000); UT_TRUE(ttAverageInMicros <= 5); UT_TRUE(ttAverageInMillis == 0); } // minimum sleep time measuring { tt.Reset(); for (int i = 0; i < 100; i++) { ALIB.SleepNanos(1); tt.Sample(); } Ticks avg = tt.GetAverage(); Log.Info("100 probes of 1 ns of sleep leads to average sleep time of " + avg.InNanos() + " ns"); } // sleep two times 20 ms and probe it to an average { tt.Reset(); tt.Start(); ALIB.SleepMillis(20); tt.Sample(); ALIB.SleepMillis(80); tt.Start(); ALIB.SleepMillis(20); tt.Sample(); long cnt = tt.GetSampleCnt(); long avg = tt.GetAverage().InMillis(); double hertz = tt.GetAverage().InHertz(1); Log.Info("TickWatch sum is " + tt.GetCumulated().InMillis() + " after " + cnt + " times 20 ms sleep"); Log.Info(" average is: " + avg + " ms"); Log.Info(" in Hertz: " + hertz); UT_TRUE(hertz < 53); UT_TRUE(hertz > 30); // should work even on heavily loaded machines UT_EQ(2, cnt); UT_TRUE(avg >= 18); UT_TRUE(avg < 45); // should work even on heavily loaded machines } // Ticks Since { Ticks tt1 = new Ticks(); tt1.FromSeconds(1000); Ticks tt2 = new Ticks(); tt2.FromSeconds(1001); UT_TRUE(tt2.Since(tt1).InMillis() == 1000L); UT_TRUE(tt2.Since(tt1).InMicros() == 1000L * 1000L); UT_TRUE(tt2.Since(tt1).InNanos() == 1000L * 1000L * 1000L); } }
/** **************************************************************************************** * Writes hash tables stored in a ScopeStore. Keys are AStrings. * Value types currently supported are LogData and int (in C# different method). * @param store The store to use. * @return The total number of hash table entries written. ******************************************************************************************/ public int writeStoreMap <T>(ScopeStore <Dictionary <AString, T> > store) { bool firstEntry = true; int cnt = 0; if (store.globalStore != null && store.globalStore.Count > 0) { cnt += store.globalStore.Count; if (firstEntry) { firstEntry = false; } else { target.NewLine(); } target._NC(" Scope.Global:").NewLine(); maxKeyLength = writeStoreMapHelper(store.globalStore, " "); } foreach (KeyValuePair <Thread, List <Dictionary <AString, T> > > thread in store.threadOuterStore) { if (thread.Value.Count == 0) { continue; } ALIB.ASSERT(thread.Value.Count == 1); if (firstEntry) { firstEntry = false; } else { target.NewLine(); } target._NC(" Scope.ThreadOuter "); storeThreadToScope(thread.Key)._(':').NewLine(); cnt += thread.Value[0].Count; maxKeyLength = writeStoreMapHelper(thread.Value[0], " "); } foreach (PathMap <Dictionary <AString, T> > map in store.languageStore) { if (firstEntry) { firstEntry = false; } else { target.NewLine(); } target._NC(" "); storeKeyToScope(map)._(':').NewLine(); cnt += map.Value.Count; maxKeyLength = writeStoreMapHelper(map.Value, " "); } foreach (KeyValuePair <Thread, List <Dictionary <AString, T> > > thread in store.threadInnerStore) { if (thread.Value.Count == 0) { continue; } ALIB.ASSERT(thread.Value.Count == 1); if (firstEntry) { firstEntry = false; } else { target.NewLine(); } target._NC(" Scope.ThreadInner "); storeThreadToScope(thread.Key)._(':').NewLine(); cnt += thread.Value[0].Count; maxKeyLength = writeStoreMapHelper(thread.Value[0], " "); } return(cnt); }
// ############################################################################################# // Interface // ############################################################################################# /** **************************************************************************************** * Adds an acquirer. * @param newAcquirer The acquirer to add. * @return The new number of \e acquirers set. ******************************************************************************************/ public virtual int AddAcquirer(ThreadLock newAcquirer) { int count = -1; #if DEBUG bool errAllreadyAdded = true; bool errHasToBeRecursive = false; int errWasAcquired = 0; #endif try { ALIB.Lock.Acquire(); count = acquirers.Count; // check doubly added if (newAcquirer == null || acquirers.IndexOf(newAcquirer) < 0) { #if DEBUG errAllreadyAdded = false; errWasAcquired = DbgCountAcquirements() == 0 ? 0 : 1; #endif // switch on? if (acquirers.Count == 1) { #if DEBUG errAllreadyAdded = false; #endif ThreadLock firstAcquirer = acquirers[0]; // non-anonymous acquirer? if (firstAcquirer != null) { if (firstAcquirer.GetMode() == LockMode.Recursive) { firstAcquirer.Acquire(); SetSafeness(Safeness.Safe); acquirers.Add(newAcquirer); count++; firstAcquirer.Release(); } #if DEBUG else { errHasToBeRecursive = false; } #endif } // critical section: our first acquirer is anonymous. As documented in class, // this must happen only in situations, when we are sure, that we are safe, e.g. still // single threaded execution of process bootstrap. else { // If this assert happens, its only good luck: we detected a misuse. But it should // be very seldom to be detected this way :-/ #if DEBUG if (errWasAcquired == 1) { errWasAcquired = 2; } #endif SetSafeness(Safeness.Safe); acquirers.Add(newAcquirer); count++; } } else { acquirers.Add(newAcquirer); } } } finally { ALIB.Lock.Release(); } #if DEBUG ALIB.ASSERT_ERROR(!errAllreadyAdded, "Acquirer already registered."); ALIB.ASSERT_ERROR(!errHasToBeRecursive, "Acquireres need to be in recursive mode "); ALIB.ASSERT_ERROR(errWasAcquired != 1, "Already aquired. Hint: Acquirer[0] must not acquire this before adding itself!"); ALIB.ASSERT_ERROR(errWasAcquired != 2, "Aquired and acquirer[0] anonymous. Misuse of SmartLock!"); #endif return(count); }
// ############################################################################################# // Internals // ############################################################################################# /** **************************************************************************************** * Internal, recursive helper of #Find. * * @param domainPath Path to search. * @param sensitivity Denotes if domain name search is treated case sensitive or not. * @param maxCreate The maximum number of sub domains that are created if not * found at the end of the path. * @param[out] wasCreated Output parameter that is set \c true if domain was not found * and hence created. * @return The domain found or created. ******************************************************************************************/ protected Domain findRecursive(Substring domainPath, Case sensitivity, int maxCreate, ref bool wasCreated) { //--- get act sub-name and rest of path domainPath.Consume(PathSeparator); int endSubName = domainPath.IndexOf(PathSeparator); ALIB.ASSERT_ERROR(endSubName != 0, "Internal Error"); // find end of actual domain name and save rest Substring restOfDomainPath = tSubstring2; restOfDomainPath.SetNull(); if (endSubName > 0) { domainPath.Split(endSubName, restOfDomainPath, 1); } // search sub-domain Domain subDomain = null; // "." if (domainPath.Equals(".")) { subDomain = this; } // ".." else if (domainPath.Equals("..")) { subDomain = Parent != null ? Parent : this; } // search in sub-domain else { int i; bool fixedOnce = false; for (;;) { for (i = 0; i < SubDomains.Count; i++) { int comparison = SubDomains[i].Name.CompareTo(domainPath, sensitivity); if (comparison >= 0) { if (comparison == 0) { subDomain = SubDomains[i]; } break; } } // domain found? if (subDomain != null) { break; } // try and fix name if (!fixedOnce) { fixedOnce = true; bool illegalCharacterFound = false; for (int cp = 0; cp < domainPath.Length(); ++cp) { char c = domainPath.CharAt(cp); if (c < '-' || c > 'z' || c == '<' || c == '>' || c == '[' || c == ']' || c == '=' || c == '?' || c == ';' || c == ':' || c == '\\' || c == '\'' || c == '.' || c == ',' ) { illegalCharacterFound = true; domainPath.Buf[domainPath.Start + cp] = '#'; } } if (illegalCharacterFound) { continue; } } // create if (maxCreate == 0) { return(null); } wasCreated = true; SubDomains.Insert(i, subDomain = new Domain(this, new AString(domainPath))); maxCreate--; if (maxCreate == 0) { return(subDomain); } break; } } // recursion? if (restOfDomainPath.IsNotEmpty()) { domainPath.Set(restOfDomainPath); return(subDomain.findRecursive(domainPath, sensitivity, maxCreate, ref wasCreated)); } // that's it return(subDomain); }
/** ******************************************************************************************** * The implementation of the abstract method of parent class TextLogger. * Loops over the log text, removes or ignores ESC sequences (all but ESC.TAB) and invokes * abstract methods of descendants as follows: * - \ref notifyLogOp "notifyLogOp(true)" * - #logSubstring() * - ... * - \ref notifyLogOp "notifyLogOp(Phase.End)" * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this stage * and is provided to be able to be logged out only. * @param msg The log message. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. For single * line messages this is -1. **********************************************************************************************/ override protected void logText(Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber) { if (!notifyLogOp(Phase.Begin)) { return; } // loop over message, print the parts between the escape sequences int msgLength = msg.Length(); int start = 0; int end; int column = 0; while (start < msgLength) { bool foundESC = true; end = msg.IndexOf('\x1B', start); if (end < 0) { foundESC = false; end = msgLength; } if (end > start) { if (!logSubstring(msg, start, end - start)) { return; } column += end - start; } // interpret escape sequence if (foundESC) { char c = msg[++end]; // auto tab or end of meta info part if (c == 't' || c == 'A') { end++; c = msg[end++]; int extraSpace = c >= '0' && c <= '9' ? (int)(c - '0') : (int)(c - 'A') + 10; int tabStop = AutoSizes.Next(column, extraSpace); if (tabStop > column) { AString spaces = Util.GetSpaces(); int spacesLength = spaces.Length(); int qty = tabStop - column; while (qty > 0) { int size = qty < spacesLength ? qty : spacesLength; if (!logSubstring(spaces, 0, size)) { return; } qty -= size; } column = tabStop; } } // prune or ignore all others else { if (!PruneESCSequences) { if (!logSubstring(msg, end - 1, 3)) { return; } } end += 2; } } // next start = end; } // write loop ALIB.ASSERT_WARNING(start == msgLength, "Loop error when pruning ESC codes"); notifyLogOp(Phase.End); }
public void Basics() { UT_INIT(); Log.SetVerbosity(new ConsoleLogger(), Verbosity.Verbose, "/"); Log.MapThreadName("UnitTest"); Log.SetDomain("TickWatch", Scope.Method); Log.Info("\n### TicksBasics ###"); // check times { Ticks t = new Ticks(); t.FromNanos(42); // beyond resolution in C#: UT_EQ( t.InNanos(), 42L); UT_EQ(t.InMicros(), 0L); UT_EQ(t.InMillis(), 0L); UT_EQ(t.InSeconds(), 0L); t.FromMicros(42); UT_EQ(t.InNanos(), 42000L); UT_EQ(t.InMicros(), 42L); UT_EQ(t.InMillis(), 0L); UT_EQ(t.InSeconds(), 0L); t.FromMillis(42); UT_EQ(t.InNanos(), 42000000L); UT_EQ(t.InMicros(), 42000L); UT_EQ(t.InMillis(), 42L); UT_EQ(t.InSeconds(), 0L); t.FromSeconds(42); UT_EQ(t.InNanos(), 42000000000L); UT_EQ(t.InMicros(), 42000000L); UT_EQ(t.InMillis(), 42000L); UT_EQ(t.InSeconds(), 42L); Ticks diff = new Ticks(); diff.FromMillis(100); t.Add(diff); UT_EQ(t.InNanos(), 42100000000L); UT_EQ(t.InMicros(), 42100000L); UT_EQ(t.InMillis(), 42100L); UT_EQ(t.InSeconds(), 42L); t.Sub(diff); UT_EQ(t.InNanos(), 42000000000L); UT_EQ(t.InMicros(), 42000000L); UT_EQ(t.InMillis(), 42000L); UT_EQ(t.InSeconds(), 42L); t.FromMillis(100); UT_EQ(t.InHertz(), 10.0); t.FromMillis(300); UT_EQ(t.InHertz(0), 3.0); UT_EQ(t.InHertz(1), 3.3); UT_EQ(t.InHertz(2), 3.33); UT_EQ(t.InHertz(5), 3.33333); } // check internal frequency { double freq = Ticks.InternalFrequency; Log.Info("TickWatch InternalFrequency: " + freq); UT_TRUE(freq >= 1000000.0); } // check TickWatch creation time { Ticks creationTimeDiff = new Ticks(); creationTimeDiff.Sub(Ticks.CreationTime); Log.Info("TickWatch library creation was: " + creationTimeDiff.InNanos() + "ns ago"); Log.Info("TickWatch library creation was: " + creationTimeDiff.InMicros() + "µs ago"); Log.Info("TickWatch library creation was: " + creationTimeDiff.InMillis() + "ms ago"); Log.Info("TickWatch library creation was: " + creationTimeDiff.InSeconds() + "s ago"); UT_TRUE(creationTimeDiff.InNanos() > 100); // It should really take 100 nanoseconds to get here! UT_TRUE(creationTimeDiff.InSeconds() < 3600); // these test will probably not last an hour } // check if we could sleep for 100ms { Ticks start = new Ticks(); ALIB.SleepMillis(30); Ticks sleepTime = new Ticks(); sleepTime.Sub(start); Log.Info("TickWatch diff after 100ms sleep: " + sleepTime.InMillis() + " ms"); UT_TRUE(sleepTime.InMillis() > 28); UT_TRUE(sleepTime.InMillis() < 150); // should work even on heavily loaded machines } }
/** ******************************************************************************************** * Processes the next command found in the format string, by writing formatted information * into the given buffer. * The given Substring holds the next command. When method returns, the command is cut * from the front. * * @param logger The logger that we are embedded in. * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param dest The buffer to write meta information into. * @param variable The variable to read (may have more characters appended) * * @return The number of tab sequences that were written (by adding ESC::TAB to the buffer). **********************************************************************************************/ protected virtual int processVariable(TextLogger logger, Domain domain, Verbosity verbosity, ScopeInfo scope, AString dest, Substring variable) { // process commands char c2; switch (variable.Consume()) { // scope info case 'S': { // read sub command AString val; switch (c2 = variable.Consume()) { case 'P': // SP: full path { int length; String path = scope.GetFullPath(out length); if (length > 0) { dest._(path, 0, length); return(0); } val = NoSourceFileInfo; } break; case 'p': // Sp: trimmed path { val = scope.GetTrimmedPath(); if (val.IsEmpty()) { val = NoSourceFileInfo; } } break; case 'F': // file name { val = scope.GetFileName(); if (val.IsEmpty()) { val = NoSourceFileInfo; } } break; case 'f': // file name without extension { val = scope.GetFileNameWithoutExtension(); if (val.IsEmpty()) { val = NoSourceFileInfo; } } break; case 'M': // method name { String method = scope.GetMethod(); if (method.Length == 0) { dest._(NoMethodInfo); } else { dest._(method); } return(0); } case 'L': // line number { dest._(scope.GetLineNumber()); return(0); } default: { if (!warnedOnce) { warnedOnce = true; ALIB.WARNING("Unknown format variable '%S" + c2 + "\' (only one warning)"); } dest._("%ERROR"); return(0); } } dest._(val); return(0); } // %Tx: Time case 'T': { // read sub command c2 = variable.Consume(); // %TD: Date if (c2 == 'D') { // get time stamp as DateTime once if (callerDateTime == null) { callerDateTime = scope.GetTimeStamp().InDotNetDateTime(); } // avoid the allocation of a) a StringBuilder (yes, a string builder is allocated inside StringBuilder.AppendFormat!) // and b) a DateTime object, if the format is the unchanged standard. And it is faster anyhow. if (DateFormat.Equals("yyyy-MM-dd")) { dest._(callerDateTime.Value.Year, 4)._('-') ._(callerDateTime.Value.Month, 2)._('-') ._(callerDateTime.Value.Day, 2); } // support user defined standards else { // detect changes of format string since last log if (detectDateFormatChanges != DateFormat) { detectDateFormatChanges = DateFormat; dateFormatString = "{0:" + DateFormat + "}"; } // get date string from system and append to log buffer formatSB.Clear(); formatSB.AppendFormat(CultureInfo.InvariantCulture, dateFormatString, callerDateTime); dest._(formatSB); } } // %TT: Time of Day else if (c2 == 'T') { // get time stamp as DateTime once if (callerDateTime == null) { callerDateTime = scope.GetTimeStamp().InDotNetDateTime(); } // avoid the allocation of a) a StringBuilder (yes, a string builder is allocated inside StringBuilder.AppendFormat!) // and b) a DateTime object, if the format is the unchanged standard. And it is faster anyhow. if (TimeOfDayFormat.Equals("HH:mm:ss")) { dest._(callerDateTime.Value.Hour, 2)._(':') ._(callerDateTime.Value.Minute, 2)._(':') ._(callerDateTime.Value.Second, 2); } // support user defined standards else { // detect changes of format string since last log if (detectTimeOfDayFormatChanges != TimeOfDayFormat) { detectTimeOfDayFormatChanges = TimeOfDayFormat; timeOfDayFormatString = "{0:" + TimeOfDayFormat + "}"; } // get time string from system and append to log buffer formatSB.Clear(); formatSB.AppendFormat(CultureInfo.InvariantCulture, timeOfDayFormatString, callerDateTime); dest._(formatSB); } } // %TC: Time elapsed since created else if (c2 == 'C') { // create TimeSpan object (on the stack by using new! :) TimeSpan elapsed = new TimeSpan(scope.GetTimeStamp().Raw() - logger.TimeOfCreation.Raw()); if (elapsed.Days > 0) { dest._(elapsed.Days)._(TimeElapsedDays); } if (elapsed.Hours > 0) { dest._(elapsed.Hours)._(':'); } dest._(elapsed.Minutes, 2)._(':') ._(elapsed.Seconds, 2)._('.') ._(elapsed.Milliseconds, 3); } // %TL: Time elapsed since last log call else if (c2 == 'L') { writeTimeDiff(dest, scope.GetTimeStamp().Since(logger.TimeOfLastLog).InNanos()); } else { if (!warnedOnce) { warnedOnce = true; ALIB.WARNING("Unknown format variable '%T" + c2 + "\' (only one warning)"); } dest._("%ERROR"); } return(0); } // thread name / ID case 't': { c2 = variable.Consume(); if (c2 == 'N') { dest.Field() ._(scope.GetThreadName()) .Field(logger.AutoSizes.Next(scope.GetThreadName().Length(), 0), Alignment.Center); } else if (c2 == 'I') { tmpAString._()._(scope.GetThreadID()); dest.Field() ._(tmpAString) .Field(logger.AutoSizes.Next(tmpAString.Length(), 0), Alignment.Center); } else { if (!warnedOnce) { warnedOnce = true; ALIB.WARNING("Unknown format variable '%t" + c2 + "\' (only one warning)"); } dest._("%ERROR"); } return(0); } case 'L': { c2 = variable.Consume(); if (c2 == 'G') { dest._NC(logger.GetName()); } else if (c2 == 'X') { dest._NC(scope.GetLoxName()); } else { if (!warnedOnce) { warnedOnce = true; ALIB.WARNING("Unknown format variable '%L" + c2 + "\' (only one warning)"); } dest._("%ERROR"); return(0); } return(0); } case 'P': { dest._NC(Util.GetProcessName()); return(0); } case 'V': dest._(verbosity == Verbosity.Error ? VerbosityError : verbosity == Verbosity.Warning ? VerbosityWarning : verbosity == Verbosity.Info ? VerbosityInfo : VerbosityVerbose); return(0); case 'D': { dest.Field()._(domain.FullPath).Field(logger.AutoSizes.Next(domain.FullPath.Length(), 0), Alignment.Left); return(0); } case '#': dest._(logger.CntLogs, LogNumberMinDigits); return(0); // A: Auto tab case 'A': { // read extra space from format string int oldStart = variable.Start; int extraSpace; variable.ConsumeInteger(out extraSpace); if (oldStart == variable.Start) { extraSpace = 1; } // insert ESC code to jump to next tab level extraSpace = Math.Min(extraSpace, 10 + ('Z' - 'A')); char escNo = extraSpace < 10 ? (char)('0' + extraSpace) : (char)('A' + extraSpace); dest._("\x1Bt")._(escNo); return(1); } default: { if (!warnedOnce) { warnedOnce = true; ALIB.WARNING("Unknown format variable \'" + variable.Buf[variable.Start - 1] + "\'"); } dest._("%ERROR"); } return(0); } }
/** ******************************************************************************************** * * The implementation of the abstract method of parent class TextLogger. Logs messages to the * application console and/or the VStudio output window. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param msg The log message. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. For * single line messages this is -1. **********************************************************************************************/ override protected void logText(Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber) { // get actual console attributes ConsoleColor actualFGCol = Console.ForegroundColor; ConsoleColor actualBGCol = Console.BackgroundColor; // loop over message, print the parts between the escape sequences Tokenizer msgParts = new Tokenizer(msg, '\x1B'); Substring actual = msgParts.Actual; Substring rest = msgParts.Rest; int column = 0; for (;;) { if (msgParts.Next(Whitespaces.Keep).IsNotEmpty()) { #if !(ALOX_WP71 || ALOX_WP8) Console.Write(msg.Buffer(), actual.Start, actual.Length()); #else Console.Write(msg.ToString(0, actual.Start, actual.Length()); #endif column += actual.Length(); } // end of loop? if (!msgParts.HasNext()) { break; } // found an ESC sequence char c = rest.Consume(); // Colors bool isForeGround = true; if (c == 'C' || c == 'c') { isForeGround = c == 'c'; c = rest.Consume(); int colNo = c - '0'; ALIB.ASSERT_WARNING(colNo >= 0 && colNo <= 9, "Unknown ESC-c code"); // set color if (colNo >= 0 && colNo <= 8 || colNo == 8) { ConsoleColor[] cols = (isForeGround ? !IsBackgroundLight : IsBackgroundLight) ? lightColors : darkColors; if (isForeGround) { Console.ForegroundColor = cols[colNo]; } else { Console.BackgroundColor = cols[colNo]; } } else if (colNo == 9) { if (isForeGround) { Console.ForegroundColor = actualFGCol; } else { Console.BackgroundColor = actualBGCol; } } else { ALIB.WARNING("Unknown ESC- code"); } } // Styles else if (c == 's') { // bold/italics style not supported in Windows console // reset all if (rest.Consume() == 'a') { Console.ForegroundColor = actualFGCol; Console.BackgroundColor = actualBGCol; } } // auto tab / end of meta else if (c == 't' || c == 'A') { bool endOfMeta = c == 'A'; c = rest.Consume(); int extraSpace = c >= '0' && c <= '9' ? (int) (c - '0') : (int)(c - 'A') + 10; int tabStop = AutoSizes.Next(column, extraSpace); Util.WriteSpaces(Console.Out, tabStop - column); column = tabStop; if (endOfMeta) { switch (verbosity) { case Verbosity.Verbose: Console.ForegroundColor = MsgColorVerbose; break; case Verbosity.Info: Console.ForegroundColor = MsgColorInfo; break; case Verbosity.Warning: Console.ForegroundColor = MsgColorWarning; break; case Verbosity.Error: Console.ForegroundColor = MsgColorError; break; default: break; } } } // Link (we just colorize links here) else if (c == 'l') { if (rest.Consume() == 'S') { Console.ForegroundColor = IsBackgroundLight ? ConsoleColor.DarkBlue : ConsoleColor.Blue; } else { Console.ForegroundColor = actualFGCol; } } else { ALIB.WARNING("Unknown ESC code"); } } // write loop // reset colors Console.ForegroundColor = actualFGCol; Console.BackgroundColor = actualBGCol; // write NL #if !(ALOX_WP71 || ALOX_WP8) Console.WriteLine(); #else Console.WriteLine(); #endif }