mirror of
https://github.com/JonasNilson/idle_master_extended.git
synced 2025-05-12 02:46:39 +02:00
109 lines
No EOL
3.1 KiB
C#
109 lines
No EOL
3.1 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace IdleMasterExtended
|
|
{
|
|
public static class Logger
|
|
{
|
|
private static readonly object LogLock = new object();
|
|
|
|
private static readonly string ExceptionPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @".\error.log";
|
|
|
|
public static void Exception(Exception ex, params string[] messages)
|
|
{
|
|
var contents = string.Concat(DateTime.Now, " ", string.Join(Environment.NewLine, messages),
|
|
Environment.NewLine, ex.ToString(), Environment.NewLine);
|
|
Write(contents, ExceptionPath);
|
|
}
|
|
|
|
private static void Write(string contents, string path)
|
|
{
|
|
Console.WriteLine(contents);
|
|
using (TimedLock.Lock(LogLock))
|
|
{
|
|
File.AppendAllText(path, contents, Encoding.UTF8);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Thanks to Eric Gunnerson for recommending this be a struct rather
|
|
// than a class - avoids a heap allocation.
|
|
// Thanks to Change Gillespie and Jocelyn Coulmance for pointing out
|
|
// the bugs that then crept in when I changed it to use struct...
|
|
// Thanks to John Sands for providing the necessary incentive to make
|
|
// me invent a way of using a struct in both release and debug builds
|
|
// without losing the debug leak tracking.
|
|
|
|
public struct TimedLock : IDisposable
|
|
{
|
|
public static TimedLock Lock(object o)
|
|
{
|
|
return Lock(o, TimeSpan.FromSeconds(10));
|
|
}
|
|
|
|
public static TimedLock Lock(object o, TimeSpan timeout)
|
|
{
|
|
var tl = new TimedLock(o);
|
|
if (!Monitor.TryEnter(o, timeout))
|
|
{
|
|
#if DEBUG
|
|
GC.SuppressFinalize(tl.leakDetector);
|
|
#endif
|
|
throw new LockTimeoutException();
|
|
}
|
|
|
|
return tl;
|
|
}
|
|
|
|
private TimedLock(object o)
|
|
{
|
|
target = o;
|
|
#if DEBUG
|
|
leakDetector = new Sentinel();
|
|
#endif
|
|
}
|
|
private object target;
|
|
|
|
public void Dispose()
|
|
{
|
|
Monitor.Exit(target);
|
|
|
|
// It's a bad error if someone forgets to call Dispose,
|
|
// so in Debug builds, we put a finalizer in to detect
|
|
// the error. If Dispose is called, we suppress the
|
|
// finalizer.
|
|
#if DEBUG
|
|
GC.SuppressFinalize(leakDetector);
|
|
#endif
|
|
}
|
|
|
|
#if DEBUG
|
|
// (In Debug mode, we make it a class so that we can add a finalizer
|
|
// in order to detect when the object is not freed.)
|
|
private class Sentinel
|
|
{
|
|
~Sentinel()
|
|
{
|
|
// If this finalizer runs, someone somewhere failed to
|
|
// call Dispose, which means we've failed to leave
|
|
// a monitor!
|
|
Debug.Fail("Undisposed lock");
|
|
}
|
|
}
|
|
private Sentinel leakDetector;
|
|
#endif
|
|
|
|
}
|
|
public class LockTimeoutException : ApplicationException
|
|
{
|
|
public LockTimeoutException()
|
|
: base("Timeout waiting for lock")
|
|
{
|
|
}
|
|
}
|
|
} |