Skip to content

Instantly share code, notes, and snippets.

@aidanmorgan
Created June 24, 2011 09:24
Show Gist options
  • Save aidanmorgan/1044490 to your computer and use it in GitHub Desktop.
Save aidanmorgan/1044490 to your computer and use it in GitHub Desktop.
/// <summary>
/// This writer thread attempts to serialize all writes to the MongoDB database in a background thread.
/// </summary>
public class MongoDbLoggerWriteThread
{
/// <summary>
/// The lock object guarding access to the <see cref="_entries"/> and <see cref="_keyedEntries"/> lists.
/// </summary>
private static readonly object LOCK_OBJECT = new object();
/// <summary>
/// The <see cref="ILogRepository"/> that this writer will use to perform the actual writes.
/// </summary>
private readonly ILogRepository _repositoryImpl;
/// <summary>
/// A <see cref="IList"/> of <see cref="LogEntry"/> that are waiting to be picked up by the background thread.
/// This list will be automatically cleared by the background thread when it clones the lists for processing.
/// </summary>
private readonly IList<LogEntry> _entries;
/// <summary>
/// A <see cref="IList"/> of <see cref="KeyedLogEntry"/> that are waiting to be picked up by the background thread.
/// This list will be automatically cleared by the background thread when it clones the lists for processing.
/// </summary>
private readonly IList<KeyedLogEntry> _keyedEntries;
/// <summary>
/// Constructor
/// </summary>
public MongoDbLoggerWriteThread(ILogRepository repo)
{
_repositoryImpl = repo;
_entries = new List<LogEntry>();
_keyedEntries = new List<KeyedLogEntry>();
}
/// <summary>
/// Adds the provided <see cref="LogEntry"/> to the internal queue, waiting to be processed by the background thread.
/// </summary>
public void AddLogEntry(LogEntry entry)
{
// lock on the LOCK_OBJECT to ensure we won't attempt to append to the queue if the background thread is currently pulling
// values from the queue
lock (LOCK_OBJECT)
{
_entries.Add(entry);
Monitor.PulseAll(LOCK_OBJECT);
}
}
/// <summary>
/// Adds the provided <see cref="KeyedLogEntry"/> to the internal queue, waiting to be processed by the background thread.
/// </summary>
public void AddLogEntry(KeyedLogEntry entry)
{
// lock on the LOCK_OBJECT to ensure we won't attempt to append to the queue if the background thread is currently pulling
// values from the queue
lock (LOCK_OBJECT)
{
_keyedEntries.Add(entry);
Monitor.PulseAll(LOCK_OBJECT);
}
}
public void Run()
{
while (true)
{
List<LogEntry> entries;
List<KeyedLogEntry> keyedEntries;
// Lock on the LOCK_OBJECT to gain exclusive access to the queues, ensures that no other threads are enqueueing items
// whilst we are taking them off.
lock(LOCK_OBJECT)
{
// clone the queues to prevent data race conditions.
entries = new List<LogEntry>(_entries);
keyedEntries = new List<KeyedLogEntry>(_keyedEntries);
_entries.Clear();
_keyedEntries.Clear();
}
// if there is nothing to process, just wait on the wait condition.
if(entries.Count == 0 && keyedEntries.Count == 0)
{
lock (LOCK_OBJECT)
{
Monitor.Wait(LOCK_OBJECT);
}
}
else
{
// we have been pulsed, and there are entries to be processed!
foreach (LogEntry le in entries)
{
try
{
_repositoryImpl.AddLog(le.Logger, le);
}
catch(SocketException) {}
}
foreach (KeyedLogEntry kle in keyedEntries)
{
try
{
_repositoryImpl.AddLog(kle.Logger, kle);
}catch(SocketException) {}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment