/**
 * =========================================================================
 * File        : whrt.cpp
 * Project     : 0 A.D.
 * Description : Windows High Resolution Timer
 * =========================================================================
 */

// license: GPL; see lib/license.txt

#include "precompiled.h"
#include "whrt.h"

#include <process.h>    // _beginthreadex

#include "lib/sysdep/win/win.h"
#include "lib/sysdep/win/winit.h"
#include "lib/sysdep/win/wcpu.h"
#include "lib/sysdep/acpi.h"
#include "lib/adts.h"
#include "lib/bits.h"

#include "counter.h"

WINIT_REGISTER_EARLY_INIT2(whrt_Init);  // whrt -> wtime
WINIT_REGISTER_LATE_SHUTDOWN(whrt_Shutdown);


namespace ERR
{
    const LibError WHRT_COUNTER_UNSAFE = 140000;
}


//-----------------------------------------------------------------------------
// choose best available safe counter

// (moved into a separate function to simplify error handling)
static inline LibError ActivateCounter(ICounter* counter)
{
    RETURN_ERR(counter->Activate());

    if(!counter->IsSafe())
        return ERR::WHRT_COUNTER_UNSAFE;    // NOWARN (happens often)

    return INFO::OK;
}

/**
 * @return the newly created and unique instance of the next best counter
 * that is deemed safe, or 0 if all have already been created.
 **/
static ICounter* GetNextBestSafeCounter()
{
    for(;;)
    {
        static uint nextCounterId = 0;
        ICounter* counter = CreateCounter(nextCounterId++);
        if(!counter)
            return 0;   // tried all, none were safe

        LibError err = ActivateCounter(counter);
        if(err == INFO::OK)
        {
            debug_printf("HRT/ using name=%s freq=%f\n", counter->Name(), counter->NominalFrequency());
            return counter; // found a safe counter
        }
        else
        {
            char buf[100];
            debug_printf("HRT/ activating %s failed: %s\n", counter->Name(), error_description_r(err, buf, ARRAY_SIZE(buf)));
            DestroyCounter(counter);
        }
    }
}


//-----------------------------------------------------------------------------
// counter that drives the timer

static ICounter* counter;
// (these counter properties are cached for efficiency and convenience:)
static double nominalFrequency;
static double resolution;
static uint counterBits;
static u64 counterMask;

static void InitCounter()
{
    // we used to support switching counters at runtime, but that's
    // unnecessarily complex. it need and should only be done once.
    debug_assert(counter == 0);
    counter = GetNextBestSafeCounter();
    debug_assert(counter != 0);

    nominalFrequency = counter->NominalFrequency();
    resolution       = counter->Resolution();
    counterBits      = counter->CounterBits();

    counterMask = bit_mask64(counterBits);

    // sanity checks
    debug_assert(nominalFrequency >= 500.0-DBL_EPSILON);
    debug_assert(resolution <= 2e-3+DBL_EPSILON);
    debug_assert(8 <= counterBits && counterBits <= 64);
}

static void ShutdownCounter()
{
    DestroyCounter(counter);
}

static inline u64 Counter()
{
    return counter->Counter();
}

/**
 * @return difference [ticks], taking rollover into account.
 * (time-critical, so it's not called through ICounter.)
 **/
static inline u64 CounterDelta(u64 oldCounter, u64 newCounter)
{
    return (newCounter -oldCounter) & counterMask;
}

double whrt_Resolution()
{
    return resolution;
}


//-----------------------------------------------------------------------------
// timer state

// we're not going to bother calibrating the counter (i.e. measuring its
// current frequency by means of a second timer). rationale:
// - all counters except the TSC are stable and run at fixed frequencies;
// - it's not clear that any other HRT or the tick count would be useful
//   as a stable time reference (if it were, we should be using it instead);
// - calibration would complicate the code (we'd have to make sure the
//   secondary counter is safe and can co-exist with the primary).

/**
 * stores all timer state shared between readers and the update thread.
 * (must be POD because it's used before static ctors run.)
 **/
struct TimerState
{
    // value of the counter at last update.
    u64 counter;

    // total elapsed time [seconds] since first update.
    // converted from tick deltas with the *then current* frequency
    // (this enables calibration, which is currently not implemented,
    // but leaving open the possibility costs nothing)
    double time;
};

// how do we detect when the old TimerState is no longer in use and can be
// freed? we use two static instances (avoids dynamic allocation headaches)
// and swap between them ('double-buffering'). it is assumed that all
// entered critical sections (the latching of TimerState fields) will have
// been exited before the next update comes around; if not, TimerState.time
// changes, the critical section notices and re-reads the new values.
static TimerState timerStates[2];
// note: exchanging pointers is easier than XORing an index.
static TimerState* volatile ts  = &timerStates[0];
static TimerState* volatile ts2 = &timerStates[1];

static void UpdateTimerState()
{
    // how can we synchronize readers and the update thread? locks are
    // preferably avoided since they're dangerous and can be slow. what we
    // need to ensure is that TimerState doesn't change while another thread is
    // accessing it. the first step is to linearize the update, i.e. have it
    // appear to happen in an instant (done by building a new TimerState and
    // having it go live by switching pointers). all that remains is to make
    // reads of the state variables consistent, done by latching them all and
    // retrying if an update came in the middle of this.

    const u64 counter = Counter();
    const u64 deltaTicks = CounterDelta(ts->counter, counter);
    ts2->counter = counter;
    ts2->time = ts->time + deltaTicks/nominalFrequency;
    ts = (TimerState*)InterlockedExchangePointer(&ts2, ts);
}

double whrt_Time()
{
retry:
    // latch timer state (counter and time must be from the same update)
    const double time = ts->time;
    const u64 counter = ts->counter;
    // ts changed after reading time. note: don't compare counter because
    // it _might_ have the same value after two updates.
    if(time != ts->time)
        goto retry;

    const u64 deltaTicks = CounterDelta(counter, Counter());
    return (time + deltaTicks/nominalFrequency);
}


//-----------------------------------------------------------------------------
// update thread

// note: we used to discipline the HRT timestamp to the system time, so it
// was advantageous to trigger updates via WinMM event (thus reducing
// instances where we're called in the middle of a scheduler tick).
// since that's no longer relevant, we prefer using a thread, because that
// avoids the dependency on WinMM and its lengthy startup time.

// rationale: (+ and - are reasons for longer and shorter lengths)
// + minimize CPU usage
// + ensure all threads currently using TimerState return from those
//   functions before the next interval
// - avoid more than 1 counter rollover per interval (InitUpdateThread makes
//   sure our interval is shorter than the current counter's rollover rate)
static const DWORD UPDATE_INTERVAL_MS = 1000;

static HANDLE hExitEvent;
static HANDLE hUpdateThread;

static unsigned __stdcall UpdateThread(void* UNUSED(data))
{
    debug_set_thread_name("whrt_UpdateThread");

    for(;;)
    {
        const DWORD ret = WaitForSingleObject(hExitEvent, UPDATE_INTERVAL_MS);
        // owner terminated or wait failed or exit event signaled - exit thread
        if(ret != WAIT_TIMEOUT)
            break;

        UpdateTimerState();
    }

    return 0;
}

static inline LibError InitUpdateThread()
{
    // make sure our interval isn't too long
    // (counterBits can be 64 => BIT64 would overflow => calculate period/2)
    const double period_2 = BIT64(counterBits-1) / nominalFrequency;
    const uint rolloversPerInterval = UPDATE_INTERVAL_MS / cpu_i64FromDouble(period_2*2.0*1000.0);
    debug_assert(rolloversPerInterval <= 1);

    hExitEvent = CreateEvent(0, TRUE, FALSE, 0);    // manual reset, initially false
    if(hExitEvent == INVALID_HANDLE_VALUE)
        WARN_RETURN(ERR::LIMIT);

    hUpdateThread = (HANDLE)_beginthreadex(0, 0, UpdateThread, 0, 0, 0);
    if(!hUpdateThread)
        WARN_RETURN(ERR::LIMIT);

    return INFO::OK;
}

static inline void ShutdownUpdateThread()
{
    // signal thread
    BOOL ok = SetEvent(hExitEvent);
    WARN_IF_FALSE(ok);
    // the nice way is to wait for it to exit
    if(WaitForSingleObject(hUpdateThread, 100) != WAIT_OBJECT_0)
        TerminateThread(hUpdateThread, 0);  // forcibly exit (dangerous)
    CloseHandle(hExitEvent);
    CloseHandle(hUpdateThread);
}


//-----------------------------------------------------------------------------

static LibError whrt_Init()
{
    // note: several counter implementations use acpi.cpp. if a counter is
    // deemed unsafe, it is shut down, which releases the (possibly only)
    // reference to the ACPI module. unloading and reloading it after trying
    // each counter would be a waste of time, so we grab a reference here.
    (void)acpi_Init();

    InitCounter();

    // latch initial counter value so that timer starts at 0
    ts->counter = Counter();    // must come before UpdateTimerState

    UpdateTimerState(); // must come before InitUpdateThread to avoid race

    RETURN_ERR(InitUpdateThread());

    return INFO::OK;
}


static LibError whrt_Shutdown()
{
    ShutdownUpdateThread();

    ShutdownCounter();

    acpi_Shutdown();

    return INFO::OK;
}