Re: need help w/ multi-threaded, multi-CPU tick count / stopwatch



"not_a_commie" <notacommie@xxxxxxxxx> wrote in message news:1171488846.353914.46990@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The StopWatch class isn't inherently thread-safe. Are you
synchronizing access to it appropriately. Can you post some code
demonstrating the problem?

Right. The StopWatch class expects that the Reset/Start/Stop/Elapsed*
methods are all accessed from the same thread. Not only that, they all
have to be accessed from the same CPU. Does StopWatch use the
BeginThreadAffinity internally to make this happen? I'll assume that
it does. The problem is that if you need to call Reset in one thread
and Elapsed in another, this is very difficult. I did manage to make a
class to do it, however. Here it is for your critique:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace Blah
{
/// <summary>
/// Represents a high-resolution stopwatch that is thread-safe and
CPU safe.
/// </summary>
public sealed class StopWatch
{
static private Thread _backgroundThread = null;
static private EventWaitHandle _tickCountUpdateRequest = new
EventWaitHandle(false, EventResetMode.AutoReset);
static private EventWaitHandle _tickCountUpdateDone = new
EventWaitHandle(false, EventResetMode.AutoReset);

static public long Frequency = 0;
static public long CurrentTickCount = 0;

public long StartTickCount = 0;

/// <summary>
/// A thread to make sure CPU tick counts are always read from the
same CPU
/// </summary>
private static void _backgroundThreadFunc()
{
Thread.BeginThreadAffinity();
Frequency = System.Diagnostics.Stopwatch.Frequency;
while (true)
{
_tickCountUpdateRequest.WaitOne();
CurrentTickCount = System.Diagnostics.Stopwatch.GetTimestamp();
_tickCountUpdateDone.Set();
}
//Thread.EndThreadAffinity(); // never called
}

/// <summary>
/// The System.Diagnostics.Stopwatch (and hence, performance
counters) get their tick marks from the CPU that the thread is on. We
create a thread to make sure the tick counts are always read on the
same CPU.
/// </summary>
static StopWatch() {
_backgroundThread = new Thread(new
ThreadStart(_backgroundThreadFunc));
_backgroundThread.IsBackground = true;
_backgroundThread.Start();
}

/// <summary>
/// Initializes a new instance of the StopWatch class.
/// </summary>
/// <exception cref="NotSupportedException">The system does not have
a high-resolution performance counter.</exception>
public StopWatch()
{
Reset();
}

/// <summary>
/// Resets the stopwatch. This method should be called when you
start measuring.
/// </summary>
/// <exception cref="NotSupportedException">The system does not have
a high-resolution performance counter.</exception>
public void Reset()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
StartTickCount = CurrentTickCount;
}
}

/// <summary>
/// Peg the processor for this many seconds; mainly used for testing
/// </summary>
/// <param name="seconds"></param>
public static void BusyLoop(double seconds)
{
DateTime endTime = DateTime.Now.AddSeconds(seconds);
int i = 0;
while (DateTime.Now < endTime) { i++; }
}

public long GetCurrentTime_ms()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return CurrentTickCount * 1000 / StopWatch.Frequency;
}
}

/// <summary>
/// Get the time elapsed, in seconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapsed time in seconds.</returns>
public double GetElapsed_s()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return (double)(CurrentTickCount - StartTickCount) /
(double)StopWatch.Frequency;
}
}

/// <summary>
/// Get the time elapsed, in milliseconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapsed time in milliseconds.</returns>
public long GetElapsed_ms()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return (CurrentTickCount - StartTickCount) * 1000 /
StopWatch.Frequency;
}
}
}
}



Please read my other reply, your issue is probably related to a failure of the SMP HAL to synchronize the CPU clocks when running this on anything else than Vista or Longorn.

Willy.



.



Relevant Pages

  • Re: need help w/ multi-threaded, multi-CPU tick count / stopwatch
    ... The problem is that if you need to call Reset in one thread ... CPU safe. ... public sealed class StopWatch ... lock ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: TECH: what the hell is going on with my sys80 CPU!???
    ... clock out of CPU. ... I decided to hook up the reset board that came with the game. ... Mainly just because it was "something else" that plugged into that 40 pin ... that 40 pin header. ...
    (rec.games.pinball)
  • Re: MPC5200 coming out of reset - does it actually work?
    ... not make it out of reset, the highest 3 address lines may be non-0 ... I made the POR longer; ... The PCI clock comes up at 33 MHz regardless of whether the CPU will ... as well as the suggested pulldowns on ...
    (comp.sys.powerpc.tech)
  • Re: [PATCH] Use BIOS reboot on Toshiba Portege 4000
    ... That should be enough for the cpu to triple fault and let ... the BIOS do what it needs to do on that cpu i.e. outb ... and toggle a motherboard level reset? ... Won't it happily POST it's way to boot (leaving the other ...
    (Linux-Kernel)
  • Re: Help with Defender Repair
    ... Defender CPU board - the 6809 CPU has a constant reset on pin 37. ... Because the schematics do not match the boards in question, ... If you were running it in a Stargate Cocktail and it was flipping, ...
    (rec.games.video.arcade)