Changeset bd41ac52 in mainline for uspace/lib/c/generic/time.c


Ignore:
Timestamp:
2018-08-25T22:21:25Z (6 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
cca80a2
Parents:
e2625b1a
Message:

Get rid of sys/time.h

This commit moves the POSIX-like time functionality from libc's
sys/time.h to libposix and introduces C11-like or HelenOS-specific
interfaces to libc.

Specifically, use of sys/time.h, struct timeval, suseconds_t and
gettimeofday is replaced by time.h (C11), struct timespec (C11), usec_t
(HelenOS) and getuptime / getrealtime (HelenOS).

Also attempt to fix the implementation of clock() to return microseconds
(clocks) rather than processor cycles and move it to libc.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/time.c

    re2625b1a rbd41ac52  
    3535 */
    3636
    37 #include <sys/time.h>
    3837#include <time.h>
    3938#include <stdbool.h>
     
    5150#include <loc.h>
    5251#include <device/clock_dev.h>
     52#include <stats.h>
    5353
    5454#define ASCTIME_BUF_LEN  26
     
    5757#define MINS_PER_HOUR  60
    5858#define SECS_PER_MIN   60
    59 #define USECS_PER_SEC  1000000
     59#define NSECS_PER_SEC  1000000000ll
    6060#define MINS_PER_DAY   (MINS_PER_HOUR * HOURS_PER_DAY)
    6161#define SECS_PER_HOUR  (SECS_PER_MIN * MINS_PER_HOUR)
     
    7171static async_sess_t *clock_conn = NULL;
    7272
     73/**
     74 * Get CPU time used since the process invocation.
     75 *
     76 * @return Consumed microseconds by this process or -1 if not available.
     77 */
     78clock_t clock(void)
     79{
     80        static_assert(CLOCKS_PER_SEC == 1000000);
     81
     82        size_t count;
     83        stats_cpu_t *cpu_stats = stats_get_cpus(&count);
     84        if (!cpu_stats)
     85                return (clock_t) -1;
     86        if (!cpu_stats->frequency_mhz) {
     87                free(cpu_stats);
     88                return (clock_t) -1;
     89        }
     90
     91        clock_t total_usecs = -1;
     92        if (cpu_stats) {
     93                stats_task_t *task_stats = stats_get_task(task_get_id());
     94                if (task_stats) {
     95                        total_usecs = (clock_t) (task_stats->kcycles +
     96                            task_stats->ucycles) / cpu_stats->frequency_mhz;
     97                        free(task_stats);
     98                }
     99                free(cpu_stats);
     100        }
     101
     102        return total_usecs;
     103}
     104
    73105/** Check whether the year is a leap year.
    74106 *
     
    252284 *
    253285 * @param tm Broken-down time to normalize.
    254  * @param tv Timeval to add.
     286 * @param ts Timespec to add.
    255287 *
    256288 * @return 0 on success, -1 on overflow
    257289 *
    258290 */
    259 static int normalize_tm_tv(struct tm *tm, const struct timeval *tv)
     291static int normalize_tm_ts(struct tm *tm, const struct timespec *ts)
    260292{
    261293        // TODO: DST correction
    262294
    263295        /* Set initial values. */
    264         time_t usec = tm->tm_usec + tv->tv_usec;
    265         time_t sec = tm->tm_sec + tv->tv_sec;
     296        time_t nsec = tm->tm_nsec + ts->tv_nsec;
     297        time_t sec = tm->tm_sec + ts->tv_sec;
    266298        time_t min = tm->tm_min;
    267299        time_t hour = tm->tm_hour;
     
    271303
    272304        /* Adjust time. */
    273         sec += floor_div(usec, USECS_PER_SEC);
    274         usec = floor_mod(usec, USECS_PER_SEC);
     305        sec += floor_div(nsec, NSECS_PER_SEC);
     306        nsec = floor_mod(nsec, NSECS_PER_SEC);
    275307        min += floor_div(sec, SECS_PER_MIN);
    276308        sec = floor_mod(sec, SECS_PER_MIN);
     
    321353
    322354        /* And put the values back to the struct. */
    323         tm->tm_usec = (int) usec;
     355        tm->tm_nsec = (int) nsec;
    324356        tm->tm_sec = (int) sec;
    325357        tm->tm_min = (int) min;
     
    340372static int normalize_tm_time(struct tm *tm, time_t time)
    341373{
    342         struct timeval tv = {
     374        struct timespec ts = {
    343375                .tv_sec = time,
    344                 .tv_usec = 0
     376                .tv_nsec = 0
    345377        };
    346378
    347         return normalize_tm_tv(tm, &tv);
     379        return normalize_tm_ts(tm, &ts);
    348380}
    349381
     
    456488}
    457489
    458 static void tv_normalize(struct timeval *tv)
    459 {
    460         while (tv->tv_usec > USECS_PER_SEC) {
    461                 tv->tv_sec++;
    462                 tv->tv_usec -= USECS_PER_SEC;
    463         }
    464         while (tv->tv_usec < 0) {
    465                 tv->tv_sec--;
    466                 tv->tv_usec += USECS_PER_SEC;
    467         }
    468 }
    469 
    470 /** Add microseconds to given timeval.
    471  *
    472  * @param tv    Destination timeval.
    473  * @param usecs Number of microseconds to add.
    474  *
    475  */
    476 void tv_add_diff(struct timeval *tv, suseconds_t usecs)
    477 {
    478         tv->tv_sec += usecs / USECS_PER_SEC;
    479         tv->tv_usec += usecs % USECS_PER_SEC;
    480         tv_normalize(tv);
    481 }
    482 
    483 /** Add two timevals.
    484  *
    485  * @param tv1 First timeval.
    486  * @param tv2 Second timeval.
    487  */
    488 void tv_add(struct timeval *tv1, const struct timeval *tv2)
    489 {
    490         tv1->tv_sec += tv2->tv_sec;
    491         tv1->tv_usec += tv2->tv_usec;
    492         tv_normalize(tv1);
    493 }
    494 
    495 /** Subtract two timevals.
    496  *
    497  * @param tv1 First timeval.
    498  * @param tv2 Second timeval.
    499  *
    500  * @return Difference between tv1 and tv2 (tv1 - tv2) in
    501  *         microseconds.
    502  *
    503  */
    504 suseconds_t tv_sub_diff(const struct timeval *tv1, const struct timeval *tv2)
    505 {
    506         return (tv1->tv_usec - tv2->tv_usec) +
    507             ((tv1->tv_sec - tv2->tv_sec) * USECS_PER_SEC);
    508 }
    509 
    510 /** Subtract two timevals.
    511  *
    512  * @param tv1 First timeval.
    513  * @param tv2 Second timeval.
    514  *
    515  */
    516 void tv_sub(struct timeval *tv1, const struct timeval *tv2)
    517 {
    518         tv1->tv_sec -= tv2->tv_sec;
    519         tv1->tv_usec -= tv2->tv_usec;
    520         tv_normalize(tv1);
    521 }
    522 
    523 /** Decide if one timeval is greater than the other.
    524  *
    525  * @param t1 First timeval.
    526  * @param t2 Second timeval.
    527  *
    528  * @return True if tv1 is greater than tv2.
    529  * @return False otherwise.
    530  *
    531  */
    532 int tv_gt(const struct timeval *tv1, const struct timeval *tv2)
    533 {
    534         if (tv1->tv_sec > tv2->tv_sec)
     490static void ts_normalize(struct timespec *ts)
     491{
     492        while (ts->tv_nsec >= NSECS_PER_SEC) {
     493                ts->tv_sec++;
     494                ts->tv_nsec -= NSECS_PER_SEC;
     495        }
     496        while (ts->tv_nsec < 0) {
     497                ts->tv_sec--;
     498                ts->tv_nsec += NSECS_PER_SEC;
     499        }
     500}
     501
     502/** Add nanoseconds to given timespec.
     503 *
     504 * @param ts     Destination timespec.
     505 * @param nsecs  Number of nanoseconds to add.
     506 *
     507 */
     508void ts_add_diff(struct timespec *ts, nsec_t nsecs)
     509{
     510        ts->tv_sec += nsecs / NSECS_PER_SEC;
     511        ts->tv_nsec += nsecs % NSECS_PER_SEC;
     512        ts_normalize(ts);
     513}
     514
     515/** Add two timespecs.
     516 *
     517 * @param ts1  First timespec.
     518 * @param ts2  Second timespec.
     519 */
     520void ts_add(struct timespec *ts1, const struct timespec *ts2)
     521{
     522        ts1->tv_sec += ts2->tv_sec;
     523        ts1->tv_nsec += ts2->tv_nsec;
     524        ts_normalize(ts1);
     525}
     526
     527/** Subtract two timespecs.
     528 *
     529 * @param ts1  First timespec.
     530 * @param ts2  Second timespec.
     531 *
     532 * @return  Difference between ts1 and ts2 (ts1 - ts2) in nanoseconds.
     533 *
     534 */
     535nsec_t ts_sub_diff(const struct timespec *ts1, const struct timespec *ts2)
     536{
     537        return (nsec_t) (ts1->tv_nsec - ts2->tv_nsec) +
     538            SEC2NSEC((ts1->tv_sec - ts2->tv_sec));
     539}
     540
     541/** Subtract two timespecs.
     542 *
     543 * @param ts1  First timespec.
     544 * @param ts2  Second timespec.
     545 *
     546 */
     547void ts_sub(struct timespec *ts1, const struct timespec *ts2)
     548{
     549        ts1->tv_sec -= ts2->tv_sec;
     550        ts1->tv_nsec -= ts2->tv_nsec;
     551        ts_normalize(ts1);
     552}
     553
     554/** Decide if one timespec is greater than the other.
     555 *
     556 * @param ts1  First timespec.
     557 * @param ts2  Second timespec.
     558 *
     559 * @return  True if ts1 is greater than ts2.
     560 * @return  False otherwise.
     561 *
     562 */
     563bool ts_gt(const struct timespec *ts1, const struct timespec *ts2)
     564{
     565        if (ts1->tv_sec > ts2->tv_sec)
    535566                return true;
    536567
    537         if ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec > tv2->tv_usec))
     568        if ((ts1->tv_sec == ts2->tv_sec) && (ts1->tv_nsec > ts2->tv_nsec))
    538569                return true;
    539570
     
    541572}
    542573
    543 /** Decide if one timeval is greater than or equal to the other.
    544  *
    545  * @param tv1 First timeval.
    546  * @param tv2 Second timeval.
    547  *
    548  * @return True if tv1 is greater than or equal to tv2.
    549  * @return False otherwise.
    550  *
    551  */
    552 int tv_gteq(const struct timeval *tv1, const struct timeval *tv2)
    553 {
    554         if (tv1->tv_sec > tv2->tv_sec)
     574/** Decide if one timespec is greater than or equal to the other.
     575 *
     576 * @param ts1  First timespec.
     577 * @param ts2  Second timespec.
     578 *
     579 * @return  True if ts1 is greater than or equal to ts2.
     580 * @return  False otherwise.
     581 *
     582 */
     583bool ts_gteq(const struct timespec *ts1, const struct timespec *ts2)
     584{
     585        if (ts1->tv_sec > ts2->tv_sec)
    555586                return true;
    556587
    557         if ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec >= tv2->tv_usec))
     588        if ((ts1->tv_sec == ts2->tv_sec) && (ts1->tv_nsec >= ts2->tv_nsec))
    558589                return true;
    559590
     
    561592}
    562593
    563 /** Get time of day.
    564  *
    565  * The time variables are memory mapped (read-only) from kernel which
    566  * updates them periodically.
    567  *
    568  * As it is impossible to read 2 values atomically, we use a trick:
    569  * First we read the seconds, then we read the microseconds, then we
    570  * read the seconds again. If a second elapsed in the meantime, set
    571  * the microseconds to zero.
    572  *
    573  * This assures that the values returned by two subsequent calls
    574  * to gettimeofday() are monotonous.
    575  *
    576  */
    577 void gettimeofday(struct timeval *tv, struct timezone *tz)
    578 {
    579         if (tz) {
    580                 tz->tz_minuteswest = 0;
    581                 tz->tz_dsttime = DST_NONE;
    582         }
    583 
     594/** Get real time from a RTC service.
     595 *
     596 * @param[out] ts  Timespec to hold time read from the RTC service (if
     597 *                 available). If no such service exists, the returned time
     598 *                 corresponds to system uptime.
     599 */
     600void getrealtime(struct timespec *ts)
     601{
    584602        if (clock_conn == NULL) {
    585603                category_id_t cat_id;
     
    620638                goto fallback;
    621639
    622         tv->tv_usec = time.tm_usec;
    623         tv->tv_sec = mktime(&time);
     640        ts->tv_nsec = time.tm_nsec;
     641        ts->tv_sec = mktime(&time);
    624642
    625643        return;
    626644
    627645fallback:
    628         getuptime(tv);
    629 }
    630 
    631 void getuptime(struct timeval *tv)
     646        getuptime(ts);
     647}
     648
     649/** Get system uptime.
     650 *
     651 * @param[out] ts  Timespec to hold time current uptime.
     652 *
     653 * The time variables are memory mapped (read-only) from kernel which
     654 * updates them periodically.
     655 *
     656 * As it is impossible to read 2 values atomically, we use a trick:
     657 * First we read the seconds, then we read the microseconds, then we
     658 * read the seconds again. If a second elapsed in the meantime, set
     659 * the microseconds to zero.
     660 *
     661 * This assures that the values returned by two subsequent calls
     662 * to getuptime() are monotonous.
     663 *
     664 */
     665void getuptime(struct timespec *ts)
    632666{
    633667        if (ktime == NULL) {
     
    654688
    655689        read_barrier();
    656         tv->tv_usec = ktime->useconds;
     690        ts->tv_nsec = USEC2NSEC(ktime->useconds);
    657691
    658692        read_barrier();
     
    660694
    661695        if (s1 != s2) {
    662                 tv->tv_sec = max(s1, s2);
    663                 tv->tv_usec = 0;
     696                ts->tv_sec = max(s1, s2);
     697                ts->tv_nsec = 0;
    664698        } else
    665                 tv->tv_sec = s1;
     699                ts->tv_sec = s1;
    666700
    667701        return;
    668702
    669703fallback:
    670         tv->tv_sec = 0;
    671         tv->tv_usec = 0;
     704        ts->tv_sec = 0;
     705        ts->tv_nsec = 0;
    672706}
    673707
    674708time_t time(time_t *tloc)
    675709{
    676         struct timeval tv;
    677         gettimeofday(&tv, NULL);
     710        struct timespec ts;
     711        getrealtime(&ts);
    678712
    679713        if (tloc)
    680                 *tloc = tv.tv_sec;
    681 
    682         return tv.tv_sec;
    683 }
    684 
    685 void udelay(useconds_t time)
     714                *tloc = ts.tv_sec;
     715
     716        return ts.tv_sec;
     717}
     718
     719void udelay(sysarg_t time)
    686720{
    687721        (void) __SYSCALL1(SYS_THREAD_UDELAY, (sysarg_t) time);
     
    884918                        break;
    885919                case 's':
    886                         APPEND("%ld", secs_since_epoch(tm));
     920                        APPEND("%lld", secs_since_epoch(tm));
    887921                        break;
    888922                case 'S':
     
    961995
    962996        /* Set result to epoch. */
    963         result->tm_usec = 0;
     997        result->tm_nsec = 0;
    964998        result->tm_sec = 0;
    965999        result->tm_min = 0;
     
    10381072 *
    10391073 */
    1040 errno_t time_tv2tm(const struct timeval *tv, struct tm *restrict result)
     1074errno_t time_ts2tm(const struct timespec *ts, struct tm *restrict result)
    10411075{
    10421076        // TODO: Deal with timezones.
     
    10441078
    10451079        /* Set result to epoch. */
    1046         result->tm_usec = 0;
     1080        result->tm_nsec = 0;
    10471081        result->tm_sec = 0;
    10481082        result->tm_min = 0;
     
    10521086        result->tm_year = 70; /* 1970 */
    10531087
    1054         if (normalize_tm_tv(result, tv) == -1)
     1088        if (normalize_tm_ts(result, ts) == -1)
    10551089                return EOVERFLOW;
    10561090
     
    10701104errno_t time_local2tm(const time_t time, struct tm *restrict result)
    10711105{
    1072         struct timeval tv = {
     1106        struct timespec ts = {
    10731107                .tv_sec = time,
    1074                 .tv_usec = 0
     1108                .tv_nsec = 0
    10751109        };
    10761110
    1077         return time_tv2tm(&tv, result);
     1111        return time_ts2tm(&ts, result);
    10781112}
    10791113
Note: See TracChangeset for help on using the changeset viewer.