/* * Util.LogWriter.cs * Authors: Adam Nabinger * Copyright (c) 2007-2008 Cornell University This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; namespace Util { internal sealed class LogWriter : IDisposable { #region Fields // The path name of the file to write to private readonly string outputFilename; // The queue of log events to be processed private readonly Queue MyQueue; // The thread used for processing all the file output private readonly Thread WorkerThread; // The file writer to use private StreamWriter writer; // Whether or not the log event processing thread should be running private bool run; // Keeps track of how many writers are currently running private int sources; // The time when the log writer was first initialized private long startTick; // Whether or not this has been initialized private bool initialized; // Whether or not this has been disposed private bool disposed; #endregion #region Creation /// /// Constructor /// /// Path name of the file to write to internal LogWriter(string filename) { outputFilename = filename; MyQueue = new Queue(); WorkerThread = new Thread(LogBackgroundThread); WorkerThread.Name = "LogWriter WorkerThread for: " + filename; WorkerThread.Priority = ThreadPriority.BelowNormal; } #endregion #region Destruction /// /// Deconstructor /// ~LogWriter() { if (!initialized) { return; } MyQueue.Enqueue(new LogEvent("Unexpected_End_of_Log", Stopwatch.GetTimestamp())); ShutDown(); } /// /// Cleans up and shuts down the separate thread used for writing /// private void ShutDown() { if (!initialized || disposed) { return; } disposed = true; run = false; lock (this) { Monitor.Pulse(this); } if(WorkerThread.ThreadState != System.Threading.ThreadState.Unstarted) WorkerThread.Join(); // Clean up the writer writer.Flush(); writer.Close(); writer.Dispose(); } /// /// Disposes of all unmanaged objects /// public void Dispose() { if (!initialized) { return; } if (--sources != 0) { return; } MyQueue.Enqueue(new LogEvent("End_of_Log", Stopwatch.GetTimestamp())); ShutDown(); GC.SuppressFinalize(this); } #endregion #region Initialization /// /// Initializes all needed information for class objects at creation /// public void Initialize() { ++sources; if (initialized) { return; } initialized = true; run = true; // Opening a new logfile; Write log preamble. startTick = Stopwatch.GetTimestamp(); MyQueue.Enqueue(new LogEvent(ProjectInfo.SVNInfoString, startTick)); MyQueue.Enqueue(new LogEvent(ProjectInfo.SystemInfoString, startTick)); MyQueue.Enqueue(new LogEvent(ProjectInfo.TimeInfoString, startTick)); // ---------- MyQueue.Enqueue(new LogEvent("Writing of Codes to the Parallel Port is " + (Settings.ParallelPortEnabled ? "Enabled." : "Disabled."), startTick)); FileStream stream = Logger.Append ? new FileStream(outputFilename, FileMode.Append, FileAccess.Write, FileShare.Read) : new FileStream(outputFilename, FileMode.Create, FileAccess.Write, FileShare.Read); writer = new StreamWriter(stream); writer.AutoFlush = false; WorkerThread.Start(); } #endregion #region Queue Modifier /// /// Adds the given log event to the queue of events to write /// /// The log event to be processed internal void Enqueue(LogEvent e) { MyQueue.Enqueue(e); lock (this) { Monitor.Pulse(this); } } #endregion #region Processing Thread /// /// This function will be run by a background thread, so the main game doesn't have to wait for IO /// private void LogBackgroundThread() { do { lock (this) { if(run) Monitor.Wait(this); } while (MyQueue.Count > 0) { LogEvent e = MyQueue.Dequeue(); if (e != null) { writer.Write("{0:###000.000000}", (e.timestamp - startTick) / (double)Stopwatch.Frequency); writer.Write(Logger.LogSeparator); if (Logger.WriteHighPrecisionTicks) { writer.Write(e.timestamp); writer.Write(Logger.LogSeparator); } writer.Write(e.eventcode); writer.Write(Logger.LogSeparator); writer.WriteLine(e.data); } else { writer.WriteLine("NULL EVENT LOGGED"); } #if DEBUG if (!Logger.DEBUG_DuplicateToConsole) { continue; } if (e != null) { Console.Write("{0:###000.000000}", (e.timestamp - startTick) / (double)Stopwatch.Frequency); Console.Write(Logger.LogSeparator); if (Logger.WriteHighPrecisionTicks) { Console.Write(e.timestamp); Console.Write(Logger.LogSeparator); } Console.Write(e.eventcode); Console.Write(Logger.LogSeparator); Console.WriteLine(e.data); } else { Console.WriteLine("NULL EVENT LOGGED"); } #endif } } while (run); } #endregion } }