forked from Imagelibrary/rtems
551 lines
14 KiB
C
551 lines
14 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/*
|
|
* COPYRIGHT (c) 1989-2012.
|
|
* On-Line Applications Research Corporation (OAR).
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
|
|
#include "pmacros.h"
|
|
#include "pritime.h"
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/score/todimpl.h>
|
|
|
|
const char rtems_test_name[] = "PSXCLOCK";
|
|
|
|
static void check_enosys(int status)
|
|
{
|
|
if ( (status == -1) && (errno == ENOSYS) )
|
|
return;
|
|
puts( "ERROR -- did not return ENOSYS as expected" );
|
|
rtems_test_exit(0);
|
|
}
|
|
|
|
static void wait_ticks( rtems_interval ticks )
|
|
{
|
|
/*
|
|
* Avoid any clock related sleep calls
|
|
*/
|
|
rtems_test_assert( rtems_task_wake_after( ticks ) == RTEMS_SUCCESSFUL );
|
|
}
|
|
|
|
struct clock_context;
|
|
typedef struct clock_context clock_context;
|
|
typedef void (*clock_sleeper)(clock_context* ctx);
|
|
struct clock_context {
|
|
const char* name;
|
|
int instance;
|
|
rtems_id tid;
|
|
int counter;
|
|
int result;
|
|
rtems_interval ticks;
|
|
struct timespec tspec;
|
|
clock_sleeper sleeper;
|
|
};
|
|
|
|
static void clock_context_init(
|
|
clock_context* ctx, const char* name, int instance,
|
|
time_t secs, long nsecs, clock_sleeper sleeper)
|
|
{
|
|
memset( ctx, 0, sizeof( *ctx ) );
|
|
ctx->name = name;
|
|
ctx->instance = instance;
|
|
ctx->tspec.tv_sec = secs;
|
|
ctx->tspec.tv_nsec = nsecs;
|
|
ctx->sleeper = sleeper;
|
|
}
|
|
|
|
static void test_nanosleep( clock_context* ctx )
|
|
{
|
|
if ( nanosleep ( &ctx->tspec, NULL ) < 0 )
|
|
{
|
|
ctx->result = errno;
|
|
}
|
|
}
|
|
|
|
static void test_clock_nanosleep_realtime( clock_context* ctx )
|
|
{
|
|
if ( clock_nanosleep ( CLOCK_REALTIME, 0, &ctx->tspec, NULL ) < 0 )
|
|
{
|
|
ctx->result = errno;
|
|
}
|
|
}
|
|
|
|
static void test_clock_nanosleep_monotonic( clock_context* ctx )
|
|
{
|
|
if ( clock_nanosleep ( CLOCK_MONOTONIC, 0, &ctx->tspec, NULL ) < 0 )
|
|
{
|
|
ctx->result = errno;
|
|
}
|
|
}
|
|
|
|
static void test_clock_check( clock_context* ctx, const rtems_interval ticks_per_sec )
|
|
{
|
|
const long tick_period_nsec = 1000000000LLU / ticks_per_sec;
|
|
rtems_interval ticks =
|
|
(((ctx->tspec.tv_sec * 1000000000LLU) + ctx->tspec.tv_nsec) / tick_period_nsec) + 1;
|
|
rtems_test_assert( ctx->result == 0 );
|
|
printf(
|
|
"clock: %s: sec=%" PRIdtime_t" nsec=%li ticks=%u expected-ticks=%u\n",
|
|
ctx->name, ctx->tspec.tv_sec, ctx->tspec.tv_nsec, ctx->ticks, ticks);
|
|
rtems_test_assert( ctx->ticks == ticks );
|
|
}
|
|
|
|
static void task_clock( rtems_task_argument arg )
|
|
{
|
|
clock_context* ctx = (clock_context*) arg;
|
|
rtems_interval start = rtems_clock_get_ticks_since_boot();
|
|
rtems_interval end;
|
|
ctx->result = 0;
|
|
ctx->sleeper( ctx );
|
|
end = rtems_clock_get_ticks_since_boot();
|
|
ctx->ticks = end - start;
|
|
++ctx->counter;
|
|
rtems_task_delete( RTEMS_SELF );
|
|
}
|
|
|
|
static void test_start_task( clock_context* ctx )
|
|
{
|
|
rtems_status_code sc;
|
|
sc = rtems_task_create(
|
|
rtems_build_name( 'C', 'R', 'T', '0' + ctx->instance ),
|
|
1,
|
|
RTEMS_MINIMUM_STACK_SIZE,
|
|
RTEMS_DEFAULT_MODES,
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&ctx->tid
|
|
);
|
|
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
|
sc = rtems_task_start( ctx->tid, task_clock, (rtems_task_argument) ctx );
|
|
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
|
}
|
|
|
|
static void test_settime_and_sleeping_step( int step )
|
|
{
|
|
const rtems_interval ticks_per_sec = rtems_clock_get_ticks_per_second();
|
|
struct timespec tv;
|
|
|
|
clock_context ns_ctx;
|
|
clock_context cnr_ctx;
|
|
clock_context cnm_ctx;
|
|
|
|
printf( "\nClock settime while sleeping: step=%i\n", step );
|
|
|
|
clock_context_init(
|
|
&ns_ctx, "nanosleep", 0, 0, 500000000,
|
|
test_nanosleep );
|
|
clock_context_init(
|
|
&cnr_ctx, "clock_nanosleep(CLOCK_REALTIME)", 0, 0, 500000000,
|
|
test_clock_nanosleep_realtime );
|
|
clock_context_init(
|
|
&cnm_ctx, "clock_nanosleep(CLOCK_MONOTONIC)", 0, 0, 500000000,
|
|
test_clock_nanosleep_monotonic );
|
|
|
|
/* Sat, 01 Jan 2000 00:00:00 GMT */
|
|
tv.tv_sec = 946684800;
|
|
tv.tv_nsec = 0;
|
|
rtems_test_assert( clock_settime( CLOCK_REALTIME, &tv ) == 0 );
|
|
|
|
wait_ticks( 1 );
|
|
|
|
test_start_task( &ns_ctx );
|
|
test_start_task( &cnr_ctx );
|
|
test_start_task( &cnm_ctx );
|
|
|
|
wait_ticks( 2 );
|
|
|
|
/*
|
|
* Jump forwards 1 second
|
|
*/
|
|
if ( step != 0 )
|
|
{
|
|
tv.tv_sec = 946684800 + step;
|
|
tv.tv_nsec = 0;
|
|
rtems_test_assert( clock_settime( CLOCK_REALTIME, &tv ) == 0 );
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
int counts = 0;
|
|
wait_ticks( ticks_per_sec / 4 );
|
|
counts += ns_ctx.counter;
|
|
counts += cnr_ctx.counter;
|
|
counts += cnm_ctx.counter;
|
|
if (counts == 3)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
test_clock_check( &ns_ctx, ticks_per_sec );
|
|
test_clock_check( &cnr_ctx, ticks_per_sec );
|
|
test_clock_check( &cnm_ctx, ticks_per_sec );
|
|
}
|
|
|
|
static void test_settime_and_sleeping( void )
|
|
{
|
|
test_settime_and_sleeping_step( 0 );
|
|
test_settime_and_sleeping_step( 1 );
|
|
test_settime_and_sleeping_step( -1 );
|
|
}
|
|
|
|
typedef struct {
|
|
int counter;
|
|
struct timespec delta;
|
|
} nanosleep_contex;
|
|
|
|
static void task_nanosleep( rtems_task_argument arg )
|
|
{
|
|
nanosleep_contex *ctx;
|
|
|
|
ctx = (nanosleep_contex *) arg;
|
|
++ctx->counter;
|
|
nanosleep( &ctx->delta, NULL );
|
|
}
|
|
|
|
static void test_far_future_nanosleep( void )
|
|
{
|
|
rtems_status_code sc;
|
|
rtems_id id;
|
|
nanosleep_contex ctx;
|
|
|
|
sc = rtems_task_create(
|
|
rtems_build_name( 'N', 'A', 'N', 'O' ),
|
|
1,
|
|
RTEMS_MINIMUM_STACK_SIZE,
|
|
RTEMS_DEFAULT_MODES,
|
|
RTEMS_DEFAULT_ATTRIBUTES,
|
|
&id
|
|
);
|
|
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
|
|
|
ctx.counter = 0;
|
|
ctx.delta.tv_sec = INT64_MAX;
|
|
ctx.delta.tv_nsec = 999999999;
|
|
sc = rtems_task_start( id, task_nanosleep, (rtems_task_argument) &ctx );
|
|
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
|
|
|
sc = rtems_task_wake_after( RTEMS_YIELD_PROCESSOR );
|
|
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
|
|
|
rtems_test_assert( ctx.counter == 1 );
|
|
|
|
sc = rtems_task_delete( id );
|
|
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
|
|
}
|
|
|
|
static rtems_task Init(
|
|
rtems_task_argument argument
|
|
)
|
|
{
|
|
struct timespec tv;
|
|
struct timespec tr;
|
|
int sc;
|
|
time_t seconds;
|
|
time_t seconds1;
|
|
unsigned int remaining;
|
|
struct tm tm;
|
|
struct timespec delay_request;
|
|
|
|
TEST_BEGIN();
|
|
|
|
tm_build_time( &tm, TM_FRIDAY, TM_MAY, 24, 96, 11, 5, 0 );
|
|
|
|
/* error cases in clock_gettime and clock_settime */
|
|
|
|
puts( "Init: clock_gettime - EINVAL (NULL timespec)" );
|
|
errno = 0;
|
|
sc = clock_gettime( CLOCK_REALTIME, NULL );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_gettime - EINVAL (invalid clockid)" );
|
|
errno = 0;
|
|
sc = clock_gettime( (clockid_t)-1, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_settime - EINVAL (invalid clockid)" );
|
|
errno = 0;
|
|
sc = clock_settime( (clockid_t)-1, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
/* way back near the dawn of time :D */
|
|
tv.tv_sec = 1;
|
|
tv.tv_nsec = 0;
|
|
printf( ctime( &tv.tv_sec ) );
|
|
puts( "Init: clock_settime - before 1988 EINVAL" );
|
|
errno = 0;
|
|
sc = clock_settime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_settime - invalid nanoseconds EINVAL" );
|
|
tv.tv_sec = 946681200;
|
|
tv.tv_nsec = 2000000000;
|
|
errno = 0;
|
|
sc = clock_settime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_settime - 2400-01-01T00:00:00.999999999Z SUCCESSFUL" );
|
|
tv.tv_sec = 13569465600;
|
|
tv.tv_nsec = 999999999;
|
|
sc = clock_settime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( sc == 0 );
|
|
|
|
puts( "Init: clock_settime - 2400-01-01T00:00:01Z EINVAL" );
|
|
tv.tv_sec = 13569465601;
|
|
tv.tv_nsec = 0;
|
|
errno = 0;
|
|
sc = clock_settime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_settime - far future EINVAL" );
|
|
tv.tv_sec = 17179869184;
|
|
tv.tv_nsec = 0;
|
|
errno = 0;
|
|
sc = clock_settime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
/* exercise clock_getres */
|
|
|
|
puts( "Init: clock_getres - EINVAL (invalid clockid)" );
|
|
errno = 0;
|
|
sc = clock_getres( (clockid_t) -1, &tv );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_getres - EINVAL (NULL resolution)" );
|
|
errno = 0;
|
|
sc = clock_getres( CLOCK_REALTIME, NULL );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
puts( "Init: clock_getres - SUCCESSFUL" );
|
|
sc = clock_getres( CLOCK_REALTIME, &tv );
|
|
printf( "Init: resolution = sec (%" PRIdtime_t "), nsec (%ld)\n", tv.tv_sec, tv.tv_nsec );
|
|
rtems_test_assert( !sc );
|
|
|
|
/* set the time of day, and print our buffer in multiple ways */
|
|
|
|
tv.tv_sec = mktime( &tm );
|
|
rtems_test_assert( tv.tv_sec != -1 );
|
|
|
|
tv.tv_nsec = 0;
|
|
|
|
/* now set the time of day */
|
|
|
|
empty_line();
|
|
|
|
printf( asctime( &tm ) );
|
|
puts( "Init: clock_settime - SUCCESSFUL" );
|
|
sc = clock_settime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( !sc );
|
|
|
|
printf( asctime( &tm ) );
|
|
printf( ctime( &tv.tv_sec ) );
|
|
|
|
/* use sleep to delay */
|
|
|
|
remaining = sleep( 3 );
|
|
rtems_test_assert( !remaining );
|
|
|
|
/* print new times to make sure it has changed and we can get the realtime */
|
|
sc = clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &tv );
|
|
rtems_test_assert( !sc );
|
|
printf(
|
|
"Time since last CPU usage reset: (%" PRIdtime_t ", %ld)\n",
|
|
tv.tv_sec,
|
|
tv.tv_nsec
|
|
);
|
|
|
|
sc = clock_gettime( CLOCK_THREAD_CPUTIME_ID, &tv );
|
|
rtems_test_assert( !sc );
|
|
printf(
|
|
"CPU time used since last CPU usage reset: (%" PRIdtime_t ", %ld)\n",
|
|
tv.tv_sec,
|
|
tv.tv_nsec
|
|
);
|
|
|
|
sc = clock_gettime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( !sc );
|
|
|
|
printf( ctime( &tv.tv_sec ) );
|
|
|
|
seconds = time( NULL );
|
|
printf( ctime( &seconds ) );
|
|
|
|
/* just to have the value copied out through the parameter */
|
|
|
|
seconds = time( &seconds1 );
|
|
rtems_test_assert( seconds == seconds1 );
|
|
|
|
/* check the time remaining */
|
|
|
|
printf( "Init: seconds remaining (%d)\n", (int)remaining );
|
|
rtems_test_assert( !remaining );
|
|
|
|
test_far_future_nanosleep();
|
|
|
|
/* error cases in nanosleep */
|
|
|
|
empty_line();
|
|
puts( "Init: nanosleep - EINVAL (NULL time)" );
|
|
sc = nanosleep ( NULL, &tr );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
tv.tv_sec = 0;
|
|
tv.tv_nsec = TOD_NANOSECONDS_PER_SECOND * 2;
|
|
puts( "Init: nanosleep - EINVAL (too many nanoseconds)" );
|
|
errno = 0;
|
|
sc = nanosleep ( &tv, &tr );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
/* this is an error */
|
|
tv.tv_sec = -1;
|
|
tv.tv_nsec = 0;
|
|
puts( "Init: nanosleep - negative seconds - EINVAL" );
|
|
errno = 0;
|
|
sc = nanosleep ( &tv, &tr );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
/* this is also an error */
|
|
tv.tv_sec = 0;
|
|
tv.tv_nsec = -1;
|
|
puts( "Init: nanosleep - negative nanoseconds - EINVAL" );
|
|
errno = 0;
|
|
sc = nanosleep ( &tv, &tr );
|
|
rtems_test_assert( sc == -1 );
|
|
rtems_test_assert( errno == EINVAL );
|
|
|
|
/* this is actually a small delay */
|
|
tv.tv_sec = 0;
|
|
tv.tv_nsec = 1;
|
|
puts( "Init: nanosleep - delay so small results in one tick" );
|
|
sc = nanosleep ( &tv, &tr );
|
|
rtems_test_assert( !sc );
|
|
rtems_test_assert( !tr.tv_sec );
|
|
rtems_test_assert( !tr.tv_nsec );
|
|
|
|
/* use nanosleep to yield */
|
|
|
|
tv.tv_sec = 0;
|
|
tv.tv_nsec = 0;
|
|
|
|
puts( "Init: nanosleep - yield with remaining" );
|
|
sc = nanosleep ( &tv, &tr );
|
|
rtems_test_assert( !sc );
|
|
rtems_test_assert( !tr.tv_sec );
|
|
rtems_test_assert( !tr.tv_nsec );
|
|
|
|
puts( "Init: nanosleep - yield with NULL time remaining" );
|
|
sc = nanosleep ( &tv, NULL );
|
|
rtems_test_assert( !sc );
|
|
rtems_test_assert( !tr.tv_sec );
|
|
rtems_test_assert( !tr.tv_nsec );
|
|
|
|
/* use nanosleep to delay */
|
|
|
|
tv.tv_sec = 3;
|
|
tv.tv_nsec = 500000;
|
|
|
|
puts( "Init: nanosleep - 1.05 seconds" );
|
|
sc = nanosleep ( &tv, &tr );
|
|
rtems_test_assert( !sc );
|
|
|
|
/* print the current real time again */
|
|
sc = clock_gettime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( !sc );
|
|
printf( ctime( &tv.tv_sec ) );
|
|
|
|
/* check the time remaining */
|
|
|
|
printf( "Init: sec (%" PRIdtime_t "), nsec (%ld) remaining\n", tr.tv_sec, tr.tv_nsec );
|
|
rtems_test_assert( !tr.tv_sec && !tr.tv_nsec );
|
|
|
|
puts( "Init: nanosleep - 1.35 seconds" );
|
|
delay_request.tv_sec = 1;
|
|
delay_request.tv_nsec = 35000000;
|
|
sc = nanosleep( &delay_request, NULL );
|
|
rtems_test_assert( !sc );
|
|
|
|
/* print the current real time again */
|
|
sc = clock_gettime( CLOCK_REALTIME, &tv );
|
|
rtems_test_assert( !sc );
|
|
printf( ctime( &tv.tv_sec ) );
|
|
|
|
empty_line();
|
|
|
|
puts( "clock_settime - CLOCK_PROCESS_CPUTIME_ID -- ENOSYS" );
|
|
#if defined(_POSIX_CPUTIME)
|
|
{
|
|
struct timespec tp;
|
|
sc = clock_settime( CLOCK_PROCESS_CPUTIME_ID, &tp );
|
|
check_enosys( sc );
|
|
}
|
|
#endif
|
|
|
|
puts( "clock_settime - CLOCK_THREAD_CPUTIME_ID -- ENOSYS" );
|
|
#if defined(_POSIX_THREAD_CPUTIME)
|
|
{
|
|
struct timespec tp;
|
|
sc = clock_settime( CLOCK_THREAD_CPUTIME_ID, &tp );
|
|
check_enosys( sc );
|
|
}
|
|
#endif
|
|
|
|
test_settime_and_sleeping( );
|
|
|
|
TEST_END();
|
|
rtems_test_exit(0);
|
|
}
|
|
|
|
|
|
/* configuration information */
|
|
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
|
|
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
|
|
|
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
|
|
|
|
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
|
#define CONFIGURE_MAXIMUM_TASKS 4
|
|
|
|
#define CONFIGURE_INIT
|
|
#include <rtems/confdefs.h>
|