Mercurial > ecos-v3_0-branch
diff packages/compat/posix/current/src/time.cxx @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | 25e238959bae |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/compat/posix/current/src/time.cxx @@ -0,0 +1,648 @@ +//========================================================================== +// +// time.cxx +// +// POSIX time functions implementation +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2000-03-27 +// Purpose: POSIX time functions implementation +// Description: This file contains the implementation of the POSIX time +// functions. +// +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/hal.h> +#include <pkgconf/kernel.h> +#include <pkgconf/posix.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros + +#include "pprivate.h" // POSIX private header + +#include <time.h> // our header + +#include <cyg/kernel/thread.hxx> +#include <cyg/kernel/clock.hxx> + +#include <cyg/kernel/thread.inl> +#include <cyg/kernel/clock.inl> + +// ------------------------------------------------------------------------- +// Internal definitions + +// Handle entry to a pthread package function. +#define TIME_ENTRY() CYG_REPORT_FUNCTYPE( "returning %d" ); + +// Do a time package defined return. This requires the error code +// to be placed in errno, and if it is non-zero, -1 returned as the +// result of the function. This also gives us a place to put any +// generic tidyup handling needed for things like signal delivery and +// cancellation. +#define TIME_RETURN(err) \ +CYG_MACRO_START \ + int __retval = 0; \ + if( err != 0 ) __retval = -1, errno = err; \ + CYG_REPORT_RETVAL( __retval ); \ + return __retval; \ +CYG_MACRO_END + +//========================================================================== +// Timer control structures + +typedef struct +{ + timer_t id; // id value for checking + Cyg_Alarm *alarm; // eCos alarm object + cyg_bool armed; // is alarm enabled? + cyg_bool pending; // is expiry pending? + int overrun; // Overrun count + struct sigevent sigev; // Sigevent to raise on expiry + + // Space for alarm object + cyg_uint8 alarm_obj[sizeof(Cyg_Alarm)]; + +} posix_timer; + +// Mutex for controlling access to shared data structures +static Cyg_Mutex timer_mutex CYGBLD_POSIX_INIT; + +// Array of timer objects +static posix_timer timer_table[_POSIX_TIMER_MAX]; + +// Index of next timer to allocate from array +static int timer_next = 0; + +// This is used to make timer_t values unique even when reusing +// a table slot. This allows _POSIX_TIMER_MAX to range +// up to 1024. +#define TIMER_ID_COOKIE_INC 0x00000400 +#define TIMER_ID_COOKIE_MASK (TIMER_ID_COOKIE_INC-1) +static timer_t timer_id_cookie = TIMER_ID_COOKIE_INC; + +//----------------------------------------------------------------------------- +// new operator to allow us to invoke the constructor on +// posix_timer.alarm_obj. + +inline void *operator new(size_t size, cyg_uint8 *ptr) { return (void *)ptr; }; + +//========================================================================== +// Time conversion variables +// These are used to interconvert between ticks and POSIX timespecs. + +// Converters from sec and ns to ticks +static struct Cyg_Clock::converter ns_converter, sec_converter; + +// Converters from ticks to sec and ns +static struct Cyg_Clock::converter ns_inverter, sec_inverter; + +// tickns is the number of nanoseconds per tick. +static cyg_tick_count tickns; + +static cyg_bool converters_initialized = false; + +//========================================================================== +// Local functions + +static void init_converters() +{ + if( !converters_initialized ) + { + + // Create the converters we need. + Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1, &ns_converter ); + Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000000000, &sec_converter ); + Cyg_Clock::real_time_clock->get_clock_to_other_converter( 1, &ns_inverter ); + Cyg_Clock::real_time_clock->get_clock_to_other_converter( 1000000000, &sec_inverter ); + + tickns = Cyg_Clock::convert( 1, &ns_inverter ); + + converters_initialized = true; + } +} + +externC cyg_tick_count cyg_timespec_to_ticks( const struct timespec *tp, + cyg_bool roundup = false) +{ + init_converters(); + + // Short circuit zero timespecs + if( tp->tv_sec == 0 && tp->tv_nsec == 0 ) + { + return 0; + } + + // Convert the seconds field to ticks. + cyg_tick_count ticks = Cyg_Clock::convert( tp->tv_sec, &sec_converter ); + + if( roundup ) + { + // Convert the nanoseconds. We add (tickns-1) to round the value up + // to the next whole tick. + + ticks += Cyg_Clock::convert( (cyg_tick_count)tp->tv_nsec+tickns-1, &ns_converter ); + } + else + { + // Convert the nanoseconds. This will round down to nearest whole tick. + ticks += Cyg_Clock::convert( (cyg_tick_count)tp->tv_nsec, &ns_converter ); + } + + return ticks; +} + +externC void cyg_ticks_to_timespec( cyg_tick_count ticks, struct timespec *tp ) +{ + init_converters(); + + // short circuit zero ticks values + if( ticks == 0 ) + { + tp->tv_sec = 0; + tp->tv_nsec = 0; + return; + } + + // Calculate number of seconds + tp->tv_sec = Cyg_Clock::convert( ticks, &sec_inverter ); + + // Convert back and subtract from ticks to get fraction of second + ticks -= Cyg_Clock::convert( tp->tv_sec, &sec_converter ); + + // Convert fraction to nanoseconds + tp->tv_nsec = Cyg_Clock::convert( ticks, &ns_inverter ); + +} + +static cyg_bool valid_timespec( const struct timespec *tp ) +{ + // Fail a NULL pointer + if( tp == NULL ) return false; + + // Fail illegal nanosecond values + if( tp->tv_nsec < 0 || tp->tv_nsec > 1000000000 ) + return false; + + return true; +} + +//========================================================================== +// Startup routine. + +externC void cyg_posix_clock_start() +{ + init_converters(); +} + +//========================================================================== +// Alarm action routine +// This is called each time an alarm set up by a timer expires. + +static void alarm_action( Cyg_Alarm *alarm, CYG_ADDRWORD data ) +{ + posix_timer *timer = (posix_timer *)data; + + if( timer->pending ) + { + // If the pending flag is already set, count an overrun and + // do not bother to try and deliver the expiry. + + timer->overrun++; + } + else + { + if( timer->sigev.sigev_notify == SIGEV_SIGNAL ) + { + // Set the expiry pending and wake a thread to + // deliver the signal. + + timer->pending = true; + + sigset_t mask; + sigemptyset( &mask ); + sigaddset( &mask, timer->sigev.sigev_signo ); + cyg_posix_pthread_release_thread( &mask ); + } + else if( timer->sigev.sigev_notify == SIGEV_THREAD ) + { + // Thread style notification + // FIXME: implement SIGEV_THREAD + } + // else do nothing + } +} + +//========================================================================== +// Timer ASR routine + +externC void cyg_posix_timer_asr( pthread_info *self ) +{ + + // Loop over the timers looking for any that have an + // expiry pending and call cyg_sigqueue() for each. + + for( int i = 0; i < _POSIX_TIMER_MAX; i++ ) + { + posix_timer *timer = &timer_table[i]; + + if( timer->id != 0 && timer->pending ) + { + timer->pending = false; + + // Call into signal subsystem... + cyg_sigqueue( &timer->sigev, SI_TIMER ); + + timer->overrun = 0; + } + } +} + +//========================================================================== +// Clock functions + +//----------------------------------------------------------------------------- +// Set the clocks current time + +externC int clock_settime( clockid_t clock_id, const struct timespec *tp) +{ + TIME_ENTRY(); + + if( clock_id != CLOCK_REALTIME ) + TIME_RETURN(EINVAL); + + if( !valid_timespec( tp ) ) + TIME_RETURN(EINVAL); + + cyg_tick_count ticks = cyg_timespec_to_ticks( tp ); + + Cyg_Clock::real_time_clock->set_value( ticks ); + + TIME_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Get the clocks current time + +externC int clock_gettime( clockid_t clock_id, struct timespec *tp) +{ + TIME_ENTRY(); + + if( clock_id != CLOCK_REALTIME ) + TIME_RETURN(EINVAL); + + if( tp == NULL ) + TIME_RETURN(EINVAL); + + cyg_tick_count ticks = Cyg_Clock::real_time_clock->current_value(); + + cyg_ticks_to_timespec( ticks, tp ); + + TIME_RETURN(0); +} + + +//----------------------------------------------------------------------------- +// Get the clocks resolution + +externC int clock_getres( clockid_t clock_id, struct timespec *tp) +{ + TIME_ENTRY(); + + if( clock_id != CLOCK_REALTIME ) + TIME_RETURN(EINVAL); + + if( tp == NULL ) + TIME_RETURN(EINVAL); + + // Get the resolution of 1 tick + cyg_ticks_to_timespec( 1, tp ); + + TIME_RETURN(0); +} + + +//========================================================================== +// Timer functions + +//----------------------------------------------------------------------------- +// Create a timer based on the given clock. + +externC int timer_create( clockid_t clock_id, + struct sigevent *evp, + timer_t *timer_id) +{ + TIME_ENTRY(); + + if( clock_id != CLOCK_REALTIME ) + TIME_RETURN(EINVAL); + + timer_mutex.lock(); + + posix_timer *timer; + int next = timer_next; + + // Look for an unused slot in the table + while( timer_table[next].id != 0 ) + { + next++; + if( next >= _POSIX_TIMER_MAX ) + next = 0; + + if( next == timer_next ) + { + timer_mutex.unlock(); + TIME_RETURN(EAGAIN); + } + } + + timer = &timer_table[next]; + + timer_next = next; + + // Make sure we never allocate a zero timer id. + while( timer->id == 0 ) + { + timer_id_cookie += TIMER_ID_COOKIE_INC; + timer->id = next+timer_id_cookie; + } + + if( evp == NULL ) + { + // If no evp is supplied, set up the timer + // to use a default set. + timer->sigev.sigev_notify = SIGEV_SIGNAL; + timer->sigev.sigev_signo = SIGALRM; + timer->sigev.sigev_value.sival_int = timer->id; + } + else timer->sigev = *evp; + + timer->alarm = new( timer->alarm_obj ) + Cyg_Alarm( Cyg_Clock::real_time_clock, + alarm_action, + (CYG_ADDRWORD)timer ); + + timer->armed = false; + timer->overrun = 0; + + *timer_id = timer->id; + + timer_mutex.unlock(); + + TIME_RETURN(0); +} + +//----------------------------------------------------------------------------- +// Delete the timer + +externC int timer_delete( timer_t timerid ) +{ + int err = EINVAL; + TIME_ENTRY(); + + posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK]; + + timer_mutex.lock(); + + if( timer->id == timerid ) + { + // This is a valid timer, disable the kernel + // alarm and delete it. + + // disable alarm + timer->alarm->disable(); + + // destroy it + timer->alarm->~Cyg_Alarm(); + + // Mark POSIX timer free + timer->id = 0; + + err = 0; + } + + timer_mutex.unlock(); + + TIME_RETURN( err ); +} + +//----------------------------------------------------------------------------- +// Set the expiration time of the timer. + +externC int timer_settime( timer_t timerid, int flags, + const struct itimerspec *value, + struct itimerspec *ovalue ) +{ + int err = EINVAL; + TIME_ENTRY(); + + if( value == NULL ) + TIME_RETURN(EINVAL); + + // convert trigger and interval values to ticks. + cyg_tick_count trigger = cyg_timespec_to_ticks( &value->it_value, true ); + cyg_tick_count interval = cyg_timespec_to_ticks( &value->it_interval, true ); + + posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK]; + + timer_mutex.lock(); + + if( timer->id == timerid ) + { + // disable the timer + timer->alarm->disable(); + + if( ovalue != NULL ) + { + cyg_tick_count otrigger, ointerval; + + timer->alarm->get_times( &otrigger, &ointerval ); + + if( timer->armed ) + { + // convert absolute trigger time to interval until next trigger + otrigger -= Cyg_Clock::real_time_clock->current_value(); + } + else otrigger = 0; + + // convert ticks to timespecs + cyg_ticks_to_timespec( otrigger, &ovalue->it_value ); + cyg_ticks_to_timespec( ointerval, &ovalue->it_interval ); + } + + if( trigger == 0 ) + { + // Mark timer disarmed + timer->armed = false; + } + else + { + // If the ABSTIME flag is not set, add the current time + if( (flags & TIMER_ABSTIME) == 0 ) + trigger += Cyg_Clock::real_time_clock->current_value(); + + // Set the alarm running. + timer->alarm->initialize( trigger, interval ); + + // Mark timer armed + timer->armed = true; + + } + + err = 0; + } + + timer_mutex.unlock(); + + TIME_RETURN(err); +} + +//----------------------------------------------------------------------------- +// Get current timer values + +externC int timer_gettime( timer_t timerid, struct itimerspec *value ) +{ + int err = EINVAL; + + TIME_ENTRY(); + + if( value == NULL ) + TIME_RETURN(EINVAL); + + posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK]; + + timer_mutex.lock(); + + if( timer->id == timerid ) + { + cyg_tick_count trigger, interval; + + timer->alarm->get_times( &trigger, &interval ); + + if( timer->armed ) + { + // convert absolute trigger time to interval until next trigger + trigger -= Cyg_Clock::real_time_clock->current_value(); + } + else trigger = 0; + + // convert ticks to timespecs + cyg_ticks_to_timespec( trigger, &value->it_value ); + cyg_ticks_to_timespec( interval, &value->it_interval ); + err = 0; + } + + timer_mutex.unlock(); + + TIME_RETURN(err); +} + +//----------------------------------------------------------------------------- +// Get number of missed triggers + +externC int timer_getoverrun( timer_t timerid ) +{ + int overrun = 0; + + TIME_ENTRY(); + + posix_timer *timer = &timer_table[timerid & TIMER_ID_COOKIE_MASK]; + + timer_mutex.lock(); + + if( timer->id == timerid ) + { + overrun = timer->overrun; + } + + timer_mutex.unlock(); + + CYG_REPORT_RETVAL(overrun); + return overrun; +} + +//========================================================================== +// Nanosleep +// Sleep for the given time. + +externC int nanosleep( const struct timespec *rqtp, + struct timespec *rmtp) +{ + cyg_tick_count ticks, now, then; + + TIME_ENTRY(); + + // Fail an invalid timespec + if( !valid_timespec( rqtp ) ) + TIME_RETURN(EINVAL); + + // Return immediately for a zero delay. + if( rqtp->tv_sec == 0 && rqtp->tv_nsec == 0 ) + TIME_RETURN(0); + + // Convert timespec to ticks + ticks = cyg_timespec_to_ticks( rqtp, true ); + + CYG_ASSERT( ticks != 0, "Zero tick count"); + + pthread_info *self = pthread_self_info(); + + + // Do the delay, keeping track of how long we actually slept for. + then = Cyg_Clock::real_time_clock->current_value(); + + self->thread->delay( ticks ); + + now = Cyg_Clock::real_time_clock->current_value(); + + + if( rmtp != NULL && (then+ticks) > now ) + { + // We woke up early, return the time left. + // FIXME: strictly we only need to do this if we were woken + // by a signal. + + // Calculate remaining number of ticks. + ticks -= (now-then); + + cyg_ticks_to_timespec( ticks, rmtp ); + } + + TIME_RETURN(0); +} + +// ------------------------------------------------------------------------- +// EOF time.cxx
