Esempio n. 1
0
static void benchExecute()
{
    SDL_Rect rect = { 0, 0, (Uint16)guGuestXRes, (Uint16)guGuestYRes };
    RTTIMESPEC t1, t2;

    RTTimeNow(&t1);
    for (unsigned i=0; i<guLoop; i++)
    {
#ifdef VBOX_OPENGL
        if (!gfOpenGL)
        {
#endif
            /* SDL backend */
            checkSDL("SDL_BlitSurface", SDL_BlitSurface(gSurfVRAM, &rect, gScreen, &rect));
            if ((gScreen->flags & SDL_HWSURFACE) == 0)
                SDL_UpdateRect(gScreen, rect.x, rect.y, rect.w, rect.h);
#ifdef VBOX_OPENGL
        }
        else
        {
            /* OpenGL backend */
            glBindTexture(GL_TEXTURE_2D, gTexture);
            glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
            glPixelStorei(GL_UNPACK_SKIP_ROWS,   rect.y);
            glPixelStorei(GL_UNPACK_ROW_LENGTH,  gSurfVRAM->pitch / gSurfVRAM->format->BytesPerPixel);
            switch (gSurfVRAM->format->BitsPerPixel)
            {
                case 16: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.w, rect.h,
                                         GL_RGB, GL_UNSIGNED_SHORT_5_6_5, gSurfVRAM->pixels);
                         break;
                case 24: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.w, rect.h,
                                         GL_BGR, GL_UNSIGNED_BYTE, gSurfVRAM->pixels);
                         break;
                case 32: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.w, rect.h,
                                         GL_BGRA, GL_UNSIGNED_BYTE, gSurfVRAM->pixels);
                         break;
                default: RTPrintf("BitsPerPixel=%d?\n", gSurfVRAM->format->BitsPerPixel);
                         return;
            }
            GLfloat tx = (GLfloat)((float)rect.w) / guTextureWidth;
            GLfloat ty = (GLfloat)((float)rect.h) / guTextureHeight;
            glBegin(GL_QUADS);
            glColor4f(1.0, 1.0, 1.0, 1.0);
            glTexCoord2f(0.0, 0.0);  glVertex2i(rect.x,          rect.y         );
            glTexCoord2f(0.0,  ty);  glVertex2i(rect.x,          rect.y + rect.h);
            glTexCoord2f(tx,   ty);  glVertex2i(rect.x + rect.w, rect.y + rect.h);
            glTexCoord2f(tx,  0.0);  glVertex2i(rect.x + rect.w, rect.y         );
            glEnd();
            glFlush();
        }
#endif
    }
    RTTimeNow(&t2);
    int64_t ms = RTTimeSpecGetMilli(&t2) - RTTimeSpecGetMilli(&t1);
    printf("  %.1fms/frame\n", (double)ms / guLoop);
}
Esempio n. 2
0
/**
 * @interface_method_impl{VBOXSERVICE,pfnWorker}
 */
DECLCALLBACK(int) vgsvcTimeSyncWorker(bool volatile *pfShutdown)
{
    RTTIME Time;
    char sz[64];
    int rc = VINF_SUCCESS;

    /*
     * Tell the control thread that it can continue spawning services.
     */
    RTThreadUserSignal(RTThreadSelf());

    /*
     * The Work Loop.
     */
    for (;;)
    {
        /*
         * Try get a reliable time reading.
         */
        int cTries = 3;
        do
        {
            /* query it. */
            RTTIMESPEC GuestNow0, GuestNow, HostNow;
            RTTimeNow(&GuestNow0);
            int rc2 = VbglR3GetHostTime(&HostNow);
            if (RT_FAILURE(rc2))
            {
                if (g_cTimeSyncErrors++ < 10)
                    VGSvcError("vgsvcTimeSyncWorker: VbglR3GetHostTime failed; rc2=%Rrc\n", rc2);
                break;
            }
            RTTimeNow(&GuestNow);

            /* calc latency and check if it's ok. */
            RTTIMESPEC GuestElapsed = GuestNow;
            RTTimeSpecSub(&GuestElapsed, &GuestNow0);
            if ((uint32_t)RTTimeSpecGetMilli(&GuestElapsed) < g_TimeSyncMaxLatency)
            {
                /*
                 * Set the time once after we were restored.
                 * (Of course only if the drift is bigger than MinAdjust)
                 */
                uint32_t TimeSyncSetThreshold = g_TimeSyncSetThreshold;
                if (g_fTimeSyncSetOnRestore)
                {
                    uint64_t idNewSession = g_idTimeSyncSession;
                    VbglR3GetSessionId(&idNewSession);
                    if (idNewSession != g_idTimeSyncSession)
                    {
                        VGSvcVerbose(3, "vgsvcTimeSyncWorker: The VM session ID changed, forcing resync.\n");
                        TimeSyncSetThreshold = 0;
                        g_idTimeSyncSession  = idNewSession;
                    }
                }

                /*
                 * Calculate the adjustment threshold and the current drift.
                 */
                uint32_t MinAdjust = RTTimeSpecGetMilli(&GuestElapsed) * g_TimeSyncLatencyFactor;
                if (MinAdjust < g_TimeSyncMinAdjust)
                    MinAdjust = g_TimeSyncMinAdjust;

                RTTIMESPEC Drift = HostNow;
                RTTimeSpecSub(&Drift, &GuestNow);
                if (RTTimeSpecGetMilli(&Drift) < 0)
                    MinAdjust += g_TimeSyncMinAdjust; /* extra buffer against moving time backwards. */

                RTTIMESPEC AbsDrift = Drift;
                RTTimeSpecAbsolute(&AbsDrift);
                if (g_cVerbosity >= 3)
                {
                    VGSvcVerbose(3, "vgsvcTimeSyncWorker: Host:    %s    (MinAdjust: %RU32 ms)\n",
                                 RTTimeToString(RTTimeExplode(&Time, &HostNow), sz, sizeof(sz)), MinAdjust);
                    VGSvcVerbose(3, "vgsvcTimeSyncWorker: Guest: - %s => %RDtimespec drift\n",
                                 RTTimeToString(RTTimeExplode(&Time, &GuestNow), sz, sizeof(sz)), &Drift);
                }

                uint32_t AbsDriftMilli = RTTimeSpecGetMilli(&AbsDrift);
                if (AbsDriftMilli > MinAdjust)
                {
                    /*
                     * Ok, the drift is above the threshold.
                     *
                     * Try a gradual adjustment first, if that fails or the drift is
                     * too big, fall back on just setting the time.
                     */

                    if (    AbsDriftMilli > TimeSyncSetThreshold
                        ||  g_fTimeSyncSetNext
                        ||  !vgsvcTimeSyncAdjust(&Drift))
                    {
                        vgsvcTimeSyncCancelAdjust();
                        vgsvcTimeSyncSet(&Drift);
                    }
                }
                else
                    vgsvcTimeSyncCancelAdjust();
                break;
            }
            VGSvcVerbose(3, "vgsvcTimeSyncWorker: %RDtimespec: latency too high (%RDtimespec) sleeping 1s\n", GuestElapsed);
            RTThreadSleep(1000);
        } while (--cTries > 0);

        /* Clear the set-next/set-start flag. */
        g_fTimeSyncSetNext = false;

        /*
         * Block for a while.
         *
         * The event semaphore takes care of ignoring interruptions and it
         * allows us to implement service wakeup later.
         */
        if (*pfShutdown)
            break;
        int rc2 = RTSemEventMultiWait(g_TimeSyncEvent, g_TimeSyncInterval);
        if (*pfShutdown)
            break;
        if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
        {
            VGSvcError("vgsvcTimeSyncWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
            rc = rc2;
            break;
        }
    }

    vgsvcTimeSyncCancelAdjust();
    RTSemEventMultiDestroy(g_TimeSyncEvent);
    g_TimeSyncEvent = NIL_RTSEMEVENTMULTI;
    return rc;
}
Esempio n. 3
0
/**
 * Try adjust the time using adjtime or similar.
 *
 * @returns true on success, false on failure.
 *
 * @param   pDrift          The time adjustment.
 */
static bool vgsvcTimeSyncAdjust(PCRTTIMESPEC pDrift)
{
#ifdef RT_OS_WINDOWS
/** @todo r=bird: g_hTokenProcess cannot be NULL here.
 *        vgsvcTimeSyncInit will fail and the service will not be started with
 *        it being NULL.  vgsvcTimeSyncInit OTOH will *NOT* be called until the
 *        service thread has terminated.  If anything
 *        else is the case, there is buggy code somewhere.*/
    if (g_hTokenProcess == NULL) /* Is the token already closed when shutting down? */
        return false;

    DWORD dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwWinTimeIncrement;
    BOOL  fWinTimeAdjustmentDisabled;
    if (GetSystemTimeAdjustment(&dwWinTimeAdjustment, &dwWinTimeIncrement, &fWinTimeAdjustmentDisabled))
    {
        DWORD dwDiffMax = g_dwWinTimeAdjustment * 0.50;
        DWORD dwDiffNew =   dwWinTimeAdjustment * 0.10;

        if (RTTimeSpecGetMilli(pDrift) > 0)
        {
            dwWinNewTimeAdjustment = dwWinTimeAdjustment + dwDiffNew;
            if (dwWinNewTimeAdjustment > (g_dwWinTimeAdjustment + dwDiffMax))
            {
                dwWinNewTimeAdjustment = g_dwWinTimeAdjustment + dwDiffMax;
                dwDiffNew = dwDiffMax;
            }
        }
        else
        {
            dwWinNewTimeAdjustment = dwWinTimeAdjustment - dwDiffNew;
            if (dwWinNewTimeAdjustment < (g_dwWinTimeAdjustment - dwDiffMax))
            {
                dwWinNewTimeAdjustment = g_dwWinTimeAdjustment - dwDiffMax;
                dwDiffNew = dwDiffMax;
            }
        }

        VGSvcVerbose(3, "vgsvcTimeSyncAdjust: Drift=%lldms\n", RTTimeSpecGetMilli(pDrift));
        VGSvcVerbose(3, "vgsvcTimeSyncAdjust: OrgTA=%ld, CurTA=%ld, NewTA=%ld, DiffNew=%ld, DiffMax=%ld\n",
                     g_dwWinTimeAdjustment, dwWinTimeAdjustment, dwWinNewTimeAdjustment, dwDiffNew, dwDiffMax);
        if (SetSystemTimeAdjustment(dwWinNewTimeAdjustment, FALSE /* Periodic adjustments enabled. */))
        {
            g_cTimeSyncErrors = 0;
            return true;
        }

        if (g_cTimeSyncErrors++ < 10)
             VGSvcError("vgsvcTimeSyncAdjust: SetSystemTimeAdjustment failed, error=%u\n", GetLastError());
    }
    else if (g_cTimeSyncErrors++ < 10)
        VGSvcError("vgsvcTimeSyncAdjust: GetSystemTimeAdjustment failed, error=%ld\n", GetLastError());

#elif defined(RT_OS_OS2) || defined(RT_OS_HAIKU)
    /* No API for doing gradual time adjustments. */

#else /* PORTME */
    /*
     * Try use adjtime(), most unix-like systems have this.
     */
    struct timeval tv;
    RTTimeSpecGetTimeval(pDrift, &tv);
    if (adjtime(&tv, NULL) == 0)
    {
        if (g_cVerbosity >= 1)
            VGSvcVerbose(1, "vgsvcTimeSyncAdjust: adjtime by %RDtimespec\n", pDrift);
        g_cTimeSyncErrors = 0;
        return true;
    }
#endif

    /* failed */
    return false;
}
/**
 * Process the testcases found in the filter.
 *
 * @param   pszFilter   The filter (winnt) to pass to RTDirOpenFiltered for
 *                      selecting the testcases.
 * @param   pszDir      The directory we're processing.
 */
static void Process(const char *pszFilter, const char *pszDir)
{
    /*
     * Open and enumerate the directory.
     */
    PRTDIR pDir;
    int rc = RTDirOpenFiltered(&pDir, pszFilter, RTDIRFILTER_WINNT, 0);
    if (RT_SUCCESS(rc))
    {
        for (;;)
        {
            RTDIRENTRY DirEntry;
            rc = RTDirRead(pDir, &DirEntry, NULL);
            if (RT_FAILURE(rc))
            {
                if (rc == VERR_NO_MORE_FILES)
                    rc = VINF_SUCCESS;
                else
                    RTPrintf("tstRunTestcases: reading '%s' -> %Rrc\n", pszFilter, rc);
                break;
            }

            /*
             * Construct the testcase name.
             */
            char *pszTestcase;
            RTStrAPrintf(&pszTestcase, "%s/%s", pszDir, DirEntry.szName);
            if (!pszTestcase)
            {
                RTPrintf("tstRunTestcases: out of memory!\n");
                rc = VERR_NO_MEMORY;
                break;
            }
            if (IsTestcaseIncluded(pszTestcase))
            {
                /*
                 * Execute the testcase.
                 */
                RTPrintf("*** %s: Executing...\n", pszTestcase);  RTStrmFlush(g_pStdOut);
                const char *papszArgs[2];
                papszArgs[0] = pszTestcase;
                papszArgs[1] = NULL;
                RTPROCESS Process;
                rc = RTProcCreate(pszTestcase, papszArgs, RTENV_DEFAULT, 0, &Process);
                if (RT_SUCCESS(rc))
                {
                    /*
                     * Wait for the process and collect it's return code.
                     * If it takes too long, we'll terminate it and continue.
                     */
                    RTTIMESPEC Start;
                    RTTimeNow(&Start);
                    RTPROCSTATUS ProcStatus;
                    for (;;)
                    {
                        rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
                        if (rc != VERR_PROCESS_RUNNING)
                            break;
                        RTTIMESPEC Now;
                        if (RTTimeSpecGetMilli(RTTimeSpecSub(RTTimeNow(&Now), &Start)) > 120*1000 /* 1 min */)
                        {
                            RTPrintf("*** %s: FAILED - timed out. killing it.\n", pszTestcase);
                            RTProcTerminate(Process);
                            RTThreadSleep(100);
                            RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus);
                            g_cFailures++;
                            break;
                        }
                        RTThreadSleep(100);
                    }

                    /*
                     * Examin the exit status.
                     */
                    if (RT_SUCCESS(rc))
                    {
                        if (    ProcStatus.enmReason == RTPROCEXITREASON_NORMAL
                            &&  ProcStatus.iStatus == 0)
                        {
                            RTPrintf("*** %s: PASSED\n", pszTestcase);
                            g_cPasses++;
                        }
                        else
                        {
                            RTPrintf("*** %s: FAILED\n", pszTestcase);
                            g_cFailures++;
                        }
                    }
                    else if (rc != VERR_PROCESS_RUNNING)
                    {
                        RTPrintf("tstRunTestcases: %s: RTProcWait failed -> %Rrc\n", pszTestcase, rc);
                        g_cFailures++;
                    }
                }
                else
                {
                    RTPrintf("tstRunTestcases: %s: failed to start -> %Rrc\n", pszTestcase, rc);
                    g_cFailures++;
                }

            }
            else
            {
                RTPrintf("tstRunTestcases: %s: SKIPPED\n", pszTestcase);
                g_cSkipped++;
            }
            RTStrFree(pszTestcase);
        } /* enumeration loop */

        RTDirClose(pDir);
    }
    else
        RTPrintf("tstRunTestcases: opening '%s' -> %Rrc\n", pszDir, rc);
}
Esempio n. 5
0
int main()
{
    RTTIMESPEC      Now;
    RTTIMESPEC      Ts1;
    RTTIMESPEC      Ts2;
    RTTIME          T1;
    RTTIME          T2;
#ifdef RTTIME_INCL_TIMEVAL
    struct timeval  Tv1;
    struct timeval  Tv2;
    struct timespec Tsp1;
    struct timespec Tsp2;
#endif
    RTTEST          hTest;

    int rc = RTTestInitAndCreate("tstRTTimeSpec", &hTest);
    if (rc)
        return rc;

    /*
     * Simple test with current time.
     */
    RTTestSub(hTest, "Current time (UTC)");
    CHECK_NZ(RTTimeNow(&Now));
    CHECK_NZ(RTTimeExplode(&T1, &Now));
    RTTestIPrintf(RTTESTLVL_ALWAYS, "   %RI64 ns - %s\n", RTTimeSpecGetNano(&Now), ToString(&T1));
    CHECK_NZ(RTTimeImplode(&Ts1, &T1));
    if (!RTTimeSpecIsEqual(&Ts1, &Now))
        RTTestIFailed("%RI64 != %RI64\n", RTTimeSpecGetNano(&Ts1), RTTimeSpecGetNano(&Now));

    /*
     * Simple test with current local time.
     */
    RTTestSub(hTest, "Current time (local)");
    CHECK_NZ(RTTimeLocalNow(&Now));
    CHECK_NZ(RTTimeExplode(&T1, &Now));
    RTTestIPrintf(RTTESTLVL_ALWAYS, "   %RI64 ns - %s\n", RTTimeSpecGetNano(&Now), ToString(&T1));
    CHECK_NZ(RTTimeImplode(&Ts1, &T1));
    if (!RTTimeSpecIsEqual(&Ts1, &Now))
        RTTestIFailed("%RI64 != %RI64\n", RTTimeSpecGetNano(&Ts1), RTTimeSpecGetNano(&Now));

    /*
     * Some simple tests with fixed dates (just checking for smoke).
     */
    RTTestSub(hTest, "Smoke");
    TEST_NS(INT64_C(0));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    TEST_NS(INT64_C(86400000000000));
    CHECK_TIME(&T1, 1970,01,02, 00,00,00,        0,   2, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    TEST_NS(INT64_C(1));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        1,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    TEST_NS(INT64_C(-1));
    CHECK_TIME(&T1, 1969,12,31, 23,59,59,999999999, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /*
     * Test the limits.
     */
    RTTestSub(hTest, "Extremes");
    TEST_NS(INT64_MAX);
    TEST_NS(INT64_MIN);
    TEST_SEC(1095379198);
    CHECK_TIME(&T1, 2004, 9,16, 23,59,58,        0, 260, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    TEST_SEC(1095379199);
    CHECK_TIME(&T1, 2004, 9,16, 23,59,59,        0, 260, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    TEST_SEC(1095379200);
    CHECK_TIME(&T1, 2004, 9,17, 00,00,00,        0, 261, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    TEST_SEC(1095379201);
    CHECK_TIME(&T1, 2004, 9,17, 00,00,01,        0, 261, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);


    /*
     * Test normalization (UTC).
     */
    RTTestSub(hTest, "Normalization (UTC)");
    /* simple */
    CHECK_NZ(RTTimeNow(&Now));
    CHECK_NZ(RTTimeExplode(&T1, &Now));
    T2 = T1;
    CHECK_NZ(RTTimeNormalize(&T1));
    if (memcmp(&T1, &T2, sizeof(T1)))
        RTTestIFailed("simple normalization failed\n");
    CHECK_NZ(RTTimeImplode(&Ts1, &T1));
    CHECK_NZ(RTTimeSpecIsEqual(&Ts1, &Now));

    /* a few partial dates. */
    memset(&T1, 0, sizeof(T1));
    SET_TIME(  &T1, 1970,01,01, 00,00,00,        0,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1970,00,00, 00,00,00,        1,   1, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1970,01,01, 00,00,00,        1,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2007,12,06, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1968,01,30, 00,19,24,        5,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1968,01,30, 00,19,24,        5,  30, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 1969,01,31, 00, 9, 2,        7,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,01,31, 00, 9, 2,        7,  31, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,03,31, 00, 9, 2,        7,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,03,31, 00, 9, 2,        7,  90, 0, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,31, 00,00,00,        9,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,31, 00,00,00,        9, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,30, 00,00,00,       30,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,30, 00,00,00,       30, 364, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,00,00, 00,00,00,       30, 363, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,29, 00,00,00,       30, 363, 0, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,00,00, 00,00,00,       30, 362, 6, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,28, 00,00,00,       30, 362, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,27, 00,00,00,       30,   0, 5, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,27, 00,00,00,       30, 361, 5, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,00,00, 00,00,00,       30, 360, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,26, 00,00,00,       30, 360, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,25, 00,00,00,       12,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,25, 00,00,00,       12, 359, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 1969,12,24, 00,00,00,       16,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1969,12,24, 00,00,00,       16, 358, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /* outside the year table range */
    SET_TIME(  &T1, 1200,01,30, 00,00,00,        2,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1200,01,30, 00,00,00,        2,  30, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 2555,11,29, 00,00,00,        2,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2555,11,29, 00,00,00,        2, 333, 5, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2555,00,00, 00,00,00,        3, 333, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2555,11,29, 00,00,00,        3, 333, 5, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /* time overflow */
    SET_TIME(  &T1, 1969,12,30, 255,255,255, UINT32_MAX, 364, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 1970,01, 9, 19,19,19,294967295,   9, 4, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /* date overflow */
    SET_TIME(  &T1, 2007,11,36, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2007,10,67, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2007,10,98, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2008,01,06, 02,15,23,        1,   6, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 2006,24,06, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    SET_TIME(  &T1, 2003,60,37, 02,15,23,        1,   0, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2008,01,06, 02,15,23,        1,   6, 6, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);

    SET_TIME(  &T1, 2003,00,00, 02,15,23,        1,1801, 0, 0, 0);
    CHECK_NZ(RTTimeNormalize(&T1));
    CHECK_TIME(&T1, 2007,12,06, 02,15,23,        1, 340, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);

    /*
     * Conversions.
     */
#define CHECK_NSEC(Ts1, T2) \
    do { \
        RTTIMESPEC TsTmp; \
        RTTESTI_CHECK_MSG( RTTimeSpecGetNano(&(Ts1)) == RTTimeSpecGetNano(RTTimeImplode(&TsTmp, &(T2))), \
                          ("line %d: %RI64, %RI64\n", __LINE__, \
                           RTTimeSpecGetNano(&(Ts1)),   RTTimeSpecGetNano(RTTimeImplode(&TsTmp, &(T2)))) ); \
    } while (0)
    RTTestSub(hTest, "Conversions, positive");
    SET_TIME(&T1, 1980,01,01, 00,00,00,        0,   1, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetDosSeconds(&Ts2,    0) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetDosSeconds(&Ts2) == 0);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1980,01,01, 00,00,00,        0,   1, 1, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_LEAP_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetNtTime(&Ts2,    INT64_C(119600064000000000)) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetNtTime(&Ts2) == INT64_C(119600064000000000));
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetSeconds(&Ts2,    1) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetSeconds(&Ts2) == 1);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetMilli(&Ts2,    1000) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetMilli(&Ts2) == 1000);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetMicro(&Ts2,    1000000) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetMicro(&Ts2) == 1000000);
    CHECK_NSEC(Ts2, T1);

    SET_TIME(&T1, 1970,01,01, 00,00,01,        0,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    RTTESTI_CHECK(RTTimeSpecSetNano(&Ts2,    1000000000) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetNano(&Ts2) == 1000000000);
    CHECK_NSEC(Ts2, T1);

#ifdef RTTIME_INCL_TIMEVAL
    SET_TIME(&T1, 1970,01,01, 00,00,01,     5000,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tv1.tv_sec  = 1;
    Tv1.tv_usec = 5;
    RTTESTI_CHECK(RTTimeSpecSetTimeval(&Ts2, &Tv1) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetMicro(&Ts2) == 1000005);
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimeval(&Ts2, &Tv2) == &Tv2);
    RTTESTI_CHECK(Tv1.tv_sec == Tv2.tv_sec); RTTESTI_CHECK(Tv1.tv_usec == Tv2.tv_usec);
#endif

#ifdef RTTIME_INCL_TIMESPEC
    SET_TIME(&T1, 1970,01,01, 00,00,01,        5,   1, 3, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tsp1.tv_sec  = 1;
    Tsp1.tv_nsec = 5;
    RTTESTI_CHECK(RTTimeSpecSetTimespec(&Ts2, &Tsp1) == &Ts2);
    RTTESTI_CHECK(RTTimeSpecGetNano(&Ts2) == 1000000005);
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimespec(&Ts2, &Tsp2) == &Tsp2);
    RTTESTI_CHECK(Tsp1.tv_sec == Tsp2.tv_sec); RTTESTI_CHECK(Tsp1.tv_nsec == Tsp2.tv_nsec);
#endif


    RTTestSub(hTest, "Conversions, negative");

#ifdef RTTIME_INCL_TIMEVAL
    SET_TIME(&T1, 1969,12,31, 23,59,58,999995000, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tv1.tv_sec  = -2;
    Tv1.tv_usec = 999995;
    RTTESTI_CHECK(RTTimeSpecSetTimeval(&Ts2, &Tv1) == &Ts2);
    RTTESTI_CHECK_MSG(RTTimeSpecGetMicro(&Ts2) == -1000005, ("%RI64\n", RTTimeSpecGetMicro(&Ts2)));
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimeval(&Ts2, &Tv2) == &Tv2);
    RTTESTI_CHECK(Tv1.tv_sec == Tv2.tv_sec); RTTESTI_CHECK(Tv1.tv_usec == Tv2.tv_usec);
#endif

#ifdef RTTIME_INCL_TIMESPEC
    SET_TIME(&T1, 1969,12,31, 23,59,58,999999995, 365, 2, 0, RTTIME_FLAGS_TYPE_UTC | RTTIME_FLAGS_COMMON_YEAR);
    Tsp1.tv_sec  = -2;
    Tsp1.tv_nsec = 999999995;
    RTTESTI_CHECK(RTTimeSpecSetTimespec(&Ts2, &Tsp1) == &Ts2);
    RTTESTI_CHECK_MSG(RTTimeSpecGetNano(&Ts2) == -1000000005, ("%RI64\n", RTTimeSpecGetMicro(&Ts2)));
    CHECK_NSEC(Ts2, T1);
    RTTESTI_CHECK(RTTimeSpecGetTimespec(&Ts2, &Tsp2) == &Tsp2);
    RTTESTI_CHECK(Tsp1.tv_sec == Tsp2.tv_sec); RTTESTI_CHECK(Tsp1.tv_nsec == Tsp2.tv_nsec);
#endif

    /*
     * Summary
     */
    return RTTestSummaryAndDestroy(hTest);
}