Skip to content

Instantly share code, notes, and snippets.

@jmcd
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmcd/ca9188f35ff62036ca90 to your computer and use it in GitHub Desktop.
Save jmcd/ca9188f35ff62036ca90 to your computer and use it in GitHub Desktop.
public class Counter
{
private readonly string directoryPath;
private int count = 0;
private static readonly object lockable = new object();
public const string AggregatedCountFilename = "agg_count.txt";
public const string PendingFileExt = "pnd";
public Counter(string directoryPath)
{
Action<string, object> log = (s, o) => Console.Error.WriteLine(Thread.CurrentThread.Name + " " + s, o);
lock (lockable)
{
this.directoryPath = directoryPath;
Directory.CreateDirectory(directoryPath);
log("in {0}", directoryPath);
var aggregatedCount = this.ReadAggregatedCount();
log("read {0}", aggregatedCount);
var pathsOfPendingFiles = this.GetPathsOfPendingFiles();
log("read {0} paths of pending files", pathsOfPendingFiles.Length);
var newCount = aggregatedCount + pathsOfPendingFiles.Length;
this.ResetAggregatedCount(newCount);
log("reset addregated count to {0}", newCount);
log("starting deleted pending", "");
DeleteFiles(pathsOfPendingFiles);
log("deleted pending", "");
}
}
private static void DeleteFiles(string[] filePaths)
{
foreach (var filePath in filePaths)
{
try
{
File.Delete(filePath);
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
}
}
private string[] GetPathsOfPendingFiles()
{
return Directory.GetFiles(this.directoryPath, string.Format("*.{0}", PendingFileExt));
}
private int ReadAggregatedCount()
{
var path = this.AggregatedCountFilePath();
if (File.Exists(path))
{
var fileContent = File.ReadAllText(path).Trim();
return int.Parse(fileContent);
}
return 0;
}
private void ResetAggregatedCount(int newCount)
{
var path = this.AggregatedCountFilePath();
var contents = newCount.ToString(CultureInfo.InvariantCulture);
File.WriteAllText(path, contents);
this.count = newCount;
}
private string AggregatedCountFilePath()
{
return Path.Combine(this.directoryPath, AggregatedCountFilename);
}
public void Increment()
{
// lock (lockable)
{
//Interlocked.Increment(ref this.count);
this.count++;
var path = Path.Combine(this.directoryPath, string.Format("{0}.{1}", Guid.NewGuid(), PendingFileExt));
var contents = this.CurrentValue().ToString(CultureInfo.InvariantCulture);
File.WriteAllText(path, contents); // just to have something to write, write the current value
}
}
public int CurrentValue()
{
return this.count;
}
}
[TestFixture]
public class CounterTests
{
private string roodDirPath;
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
this.roodDirPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
[TestFixtureTearDown]
public void TestFixtureTearDown()
{
Directory.Delete(this.roodDirPath, true);
}
private string MakeDirectoryPath()
{
return Path.Combine(this.roodDirPath, Path.GetRandomFileName());
}
[Test]
public void CanCreateNewCounter()
{
var directoryPath = this.MakeDirectoryPath();
var counter = new Counter(directoryPath);
Assert.AreEqual(0, counter.CurrentValue());
counter.Increment();
Assert.AreEqual(1, counter.CurrentValue());
}
[Test]
public void IncrementingCounterCausesPendingFiles()
{
var directoryPath = this.MakeDirectoryPath();
var counter = new Counter(directoryPath);
for (var i = 0; i < 1000; i++)
{
counter.Increment();
}
Assert.AreEqual(1000, GetPathsOfPendingFiles(directoryPath).Length);
}
[Test]
public void CreatingANewCounterAggregatesPendingFilesAndResetsTheAggregatedFile()
{
var directoryPath = this.MakeDirectoryPath();
var counter0 = new Counter(directoryPath);
for (var i = 0; i < 1000; i++)
{
counter0.Increment();
}
var counter1 = new Counter(directoryPath);
Assert.AreEqual(0, GetPathsOfPendingFiles(directoryPath).Length);
Assert.AreEqual(1000, int.Parse(File.ReadAllText(AggregatedCountFilePath(directoryPath))));
}
[Test]
public void UseCaseOf1000SubmissionsADayAndWeeklyAppRestartsFor10Years()
{
var directoryPath = this.MakeDirectoryPath();
for (var month = 0; month < 10; month++)
{
var counter = default(Counter);
for (var day = 0; day < 30; day++)
{
if (day%7 == 0)
{
counter = new Counter(directoryPath);
}
for (var i = 0; i < 1000; i++)
{
counter.Increment();
}
}
}
var finalCounter = new Counter(directoryPath);
Assert.AreEqual(6*30*1000, finalCounter.CurrentValue());
Assert.AreEqual(0, GetPathsOfPendingFiles(directoryPath).Length);
Assert.AreEqual(6*30*1000, int.Parse(File.ReadAllText(AggregatedCountFilePath(directoryPath))));
}
[Test]
public void Multiball()
{
var directoryPath = this.MakeDirectoryPath();
const int threadCount = 25;
const int numberOfIncrementsPerThread = 1000;
var threads = new Thread[threadCount];
for (var threadIndex = 0; threadIndex < threadCount; threadIndex++)
{
var t = new Thread(() =>
{
var counter = new Counter(directoryPath);
for (var i = 0; i < 1000; i++)
{
counter.Increment();
}
});
t.Name = string.Format("T{0}", threadIndex);
threads[threadIndex] = t;
}
foreach (var thread in threads)
{
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}
var finalCounter = new Counter(directoryPath);
Assert.AreEqual(numberOfIncrementsPerThread*threadCount, finalCounter.CurrentValue());
}
private static string[] GetPathsOfPendingFiles(string directoryPath)
{
return Directory.GetFiles(directoryPath, string.Format("*.{0}", Counter.PendingFileExt));
}
private static string AggregatedCountFilePath(string directoryPath)
{
return Path.Combine(directoryPath, Counter.AggregatedCountFilename);
}
}
public class Counter
{
private static readonly object Lockable = new object();
public const string AggregatedCountFilename = "count.txt";
private readonly string countFilePath;
public Counter(string directoryPath)
{
lock (Lockable)
{
this.countFilePath = Path.Combine(directoryPath, AggregatedCountFilename);
Directory.CreateDirectory(directoryPath);
}
}
private int ReadCount()
{
var path = this.countFilePath;
if (File.Exists(path))
{
var fileContent = File.ReadAllText(path).Trim();
return int.Parse(fileContent);
}
return 0;
}
private void WriteCount(int i)
{
var contents = i.ToString(CultureInfo.InvariantCulture);
File.WriteAllText(this.countFilePath, contents);
}
public void Increment()
{
lock (Lockable)
{
var i = this.ReadCount();
i++;
this.WriteCount(i);
}
}
public int CurrentValue()
{
lock (Lockable)
{
return this.ReadCount();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment