Cygwin: posix timers: reimplement using OS timer
- Rename files timer.* to posix_timer.*. - Reimplement using an OS timer rather than a handcrafted wait loop. - Use a Slim R/W Lock for synchronization. - Drop timer chaining. It doesn't server a purpose since all timers are local only. - Rename ttstart to itimer_tracker to better reflect its purpose. It's not the anchor for a timer chain anymore anyway. - Drop fixup_timers_after_fork. Everything is process-local, nothing gets inherited. - Rename timer_tracker::disarm_event to disarm_overrun_event for better readability. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
		
							
								
								
									
										644
									
								
								winsup/cygwin/posix_timer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										644
									
								
								winsup/cygwin/posix_timer.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,644 @@ | ||||
| /* timer.cc: posix timers | ||||
|  | ||||
| This file is part of Cygwin. | ||||
|  | ||||
| This software is a copyrighted work licensed under the terms of the | ||||
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for | ||||
| details. */ | ||||
|  | ||||
| #include "winsup.h" | ||||
| #include "thread.h" | ||||
| #include "cygtls.h" | ||||
| #include "sigproc.h" | ||||
| #include "path.h" | ||||
| #include "fhandler.h" | ||||
| #include "dtable.h" | ||||
| #include "cygheap.h" | ||||
| #include "posix_timer.h" | ||||
| #include <sys/param.h> | ||||
|  | ||||
| #define OVR_EVENT_DISARMED	0 | ||||
| #define OVR_EVENT_ARMED		1 | ||||
|  | ||||
| timer_tracker NO_COPY itimer_tracker (CLOCK_REALTIME, NULL); | ||||
|  | ||||
| bool | ||||
| timer_tracker::cancel () | ||||
| { | ||||
|   DWORD res; | ||||
|  | ||||
|   if (!cancel_evt) | ||||
|     return false; | ||||
|   SetEvent (cancel_evt); | ||||
|   if (sync_thr) | ||||
|     { | ||||
|       res = WaitForSingleObject (sync_thr, INFINITE); | ||||
|       if (res != WAIT_OBJECT_0) | ||||
| 	system_printf ("WFSO returned unexpected value %u, %E", res); | ||||
|     } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| timer_tracker::timer_tracker (clockid_t c, const sigevent *e) | ||||
| : magic (TT_MAGIC), clock_id (c), timer (NULL), cancel_evt (NULL), | ||||
|   sync_thr (NULL), interval (0), exp_ts (0), overrun_event_running (0), | ||||
|   overrun_count_curr (0), overrun_count (0) | ||||
| { | ||||
|   srwlock = SRWLOCK_INIT; | ||||
|   if (e != NULL) | ||||
|     evp = *e; | ||||
|   else | ||||
|     { | ||||
|       evp.sigev_notify = SIGEV_SIGNAL; | ||||
|       evp.sigev_signo = SIGALRM; | ||||
|       evp.sigev_value.sival_ptr = this; | ||||
|     } | ||||
| } | ||||
|  | ||||
| timer_tracker::~timer_tracker () | ||||
| { | ||||
|   AcquireSRWLockExclusive (&srwlock); | ||||
|   cancel (); | ||||
|   NtClose (cancel_evt); | ||||
|   NtClose (sync_thr); | ||||
|   NtClose (timer); | ||||
|   magic = 0; | ||||
|   ReleaseSRWLockExclusive (&srwlock); | ||||
| } | ||||
|  | ||||
| static inline int64_t | ||||
| timespec_to_us (const timespec& ts) | ||||
| { | ||||
|   int64_t res = ts.tv_sec; | ||||
|   res *= USPERSEC; | ||||
|   res += (ts.tv_nsec + (NSPERSEC/USPERSEC) - 1) / (NSPERSEC/USPERSEC); | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| /* Returns 0 if arming successful, -1 if a signal is already queued. | ||||
|    If so, it also increments overrun_count.  Only call under lock! */ | ||||
| LONG | ||||
| timer_tracker::arm_overrun_event () | ||||
| { | ||||
|   LONG ret; | ||||
|  | ||||
|   ret = InterlockedExchange (&overrun_event_running, OVR_EVENT_ARMED); | ||||
|   if (ret == OVR_EVENT_ARMED) | ||||
|     ret = InterlockedIncrement64 (&overrun_count); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| LONG | ||||
| timer_tracker::disarm_overrun_event () | ||||
| { | ||||
|   LONG ret; | ||||
|  | ||||
|   AcquireSRWLockExclusive (&srwlock); | ||||
|   ret = InterlockedExchange (&overrun_event_running, OVR_EVENT_DISARMED); | ||||
|   if (ret == OVR_EVENT_ARMED) | ||||
|     { | ||||
|       LONG64 ov_cnt; | ||||
|  | ||||
|       InterlockedExchange64 (&ov_cnt, overrun_count); | ||||
|       if (ov_cnt > DELAYTIMER_MAX || ov_cnt < 0) | ||||
| 	overrun_count_curr = DELAYTIMER_MAX; | ||||
|       else | ||||
| 	overrun_count_curr = ov_cnt; | ||||
|       ret = overrun_count_curr; | ||||
|       InterlockedExchange64 (&overrun_count, 0); | ||||
|     } | ||||
|   ReleaseSRWLockExclusive (&srwlock); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static void * | ||||
| notify_thread_wrapper (void *arg) | ||||
| { | ||||
|   timer_tracker *tt = (timer_tracker *) arg; | ||||
|   sigevent_t *evt = tt->sigevt (); | ||||
|   void * (*notify_func) (void *) = (void * (*) (void *)) | ||||
| 				   evt->sigev_notify_function; | ||||
|  | ||||
|   tt->disarm_overrun_event (); | ||||
|   return notify_func (evt->sigev_value.sival_ptr); | ||||
| } | ||||
|  | ||||
| DWORD | ||||
| timer_tracker::thread_func () | ||||
| { | ||||
|   HANDLE w4[2] = { timer, cancel_evt }; | ||||
|  | ||||
|   debug_printf ("%p timer armed", this); | ||||
|   while (1) | ||||
|     { | ||||
|       switch (WaitForMultipleObjects (2, w4, FALSE, INFINITE)) | ||||
| 	{ | ||||
| 	case WAIT_OBJECT_0: | ||||
| 	  debug_printf ("%p timer expired", this); | ||||
| 	  break; | ||||
| 	case WAIT_OBJECT_0 + 1: | ||||
| 	  debug_printf ("%p timer disarmed, %E", this); | ||||
| 	  goto out; | ||||
| 	default: | ||||
| 	  debug_printf ("%p wait failed, %E", this); | ||||
| 	  continue; | ||||
| 	} | ||||
|       AcquireSRWLockExclusive (&srwlock); | ||||
|       /* Make sure we haven't been abandoned and/or disarmed in the meantime */ | ||||
|       if (exp_ts == 0 && interval == 0) | ||||
| 	{ | ||||
| 	  ReleaseSRWLockExclusive (&srwlock); | ||||
| 	  goto out; | ||||
| 	} | ||||
|       if (interval) | ||||
| 	{ | ||||
| 	  /* Compute expiration count. */ | ||||
| 	  LONG64 now = get_clock_now (); | ||||
| 	  LONG64 ts = get_exp_ts (); | ||||
| 	  LONG64 exp_cnt; | ||||
|  | ||||
| 	  /* Make concessions for unexact realtime clock */ | ||||
| 	  if (ts > now) | ||||
| 	    ts = now - 1; | ||||
| 	  exp_cnt = (now - ts) / interval; | ||||
| 	  InterlockedAdd64 (&overrun_count, exp_cnt); | ||||
| 	  ts += interval * exp_cnt; | ||||
| 	  set_exp_ts (ts); | ||||
| 	  /* NtSetTimer allows periods of up to 24 days only.  If the time | ||||
| 	     is longer, we set the timer up as one-shot timer for each | ||||
| 	     interval.  Restart timer here with new due time. */ | ||||
| 	  if (interval > INT_MAX * (NS100PERSEC / MSPERSEC)) | ||||
| 	    { | ||||
| 	      /* TODO: CLOCK_REALTIME_ALARM / CLOCK_BOOTTIME_ALARM | ||||
| 		 See comment in arm_timer */ | ||||
| 	      BOOL Resume = FALSE; | ||||
| 	      LARGE_INTEGER DueTime = { QuadPart: -interval }; | ||||
|  | ||||
| 	      NtSetTimer (timer, &DueTime, NULL, NULL, Resume, 0, NULL); | ||||
| 	    } | ||||
| 	} | ||||
|       switch (evp.sigev_notify) | ||||
| 	{ | ||||
| 	case SIGEV_SIGNAL: | ||||
| 	  { | ||||
| 	    if (arm_overrun_event ()) | ||||
| 	      { | ||||
| 		debug_printf ("%p timer signal already queued", this); | ||||
| 		break; | ||||
| 	      } | ||||
| 	    siginfo_t si = {0}; | ||||
| 	    si.si_signo = evp.sigev_signo; | ||||
| 	    si.si_code = SI_TIMER; | ||||
| 	    si.si_tid = (timer_t) this; | ||||
| 	    si.si_sigval.sival_ptr = evp.sigev_value.sival_ptr; | ||||
| 	    debug_printf ("%p sending signal %d", this, evp.sigev_signo); | ||||
| 	    sig_send (myself_nowait, si); | ||||
| 	    break; | ||||
| 	  } | ||||
| 	case SIGEV_THREAD: | ||||
| 	  { | ||||
| 	    if (arm_overrun_event ()) | ||||
| 	      { | ||||
| 		debug_printf ("%p timer thread already queued", this); | ||||
| 		break; | ||||
| 	      } | ||||
| 	    pthread_t notify_thread; | ||||
| 	    debug_printf ("%p starting thread", this); | ||||
| 	    pthread_attr_t *attr; | ||||
| 	    pthread_attr_t default_attr; | ||||
| 	    if (evp.sigev_notify_attributes) | ||||
| 	      attr = evp.sigev_notify_attributes; | ||||
| 	    else | ||||
| 	      { | ||||
| 		pthread_attr_init(attr = &default_attr); | ||||
| 		pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED); | ||||
| 	      } | ||||
| 	    int rc = pthread_create (¬ify_thread, attr, | ||||
| 				     notify_thread_wrapper, this); | ||||
| 	    if (rc) | ||||
| 	      { | ||||
| 		debug_printf ("thread creation failed, %E"); | ||||
| 		return 0; | ||||
| 	      } | ||||
| 	    break; | ||||
| 	  } | ||||
| 	} | ||||
|       /* one-shot timer? */ | ||||
|       if (!interval) | ||||
| 	{ | ||||
| 	  memset (&time_spec, 0, sizeof time_spec); | ||||
| 	  exp_ts = 0; | ||||
| 	  overrun_event_running = OVR_EVENT_DISARMED; | ||||
| 	  ReleaseSRWLockExclusive (&srwlock); | ||||
| 	  goto out; | ||||
| 	} | ||||
|       ReleaseSRWLockExclusive (&srwlock); | ||||
|       debug_printf ("looping"); | ||||
|     } | ||||
|  | ||||
| out: | ||||
|   _my_tls._ctinfo->auto_release ();     /* automatically return the cygthread to the cygthread pool */ | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static DWORD WINAPI | ||||
| timer_thread (VOID *x) | ||||
| { | ||||
|   timer_tracker *tt = ((timer_tracker *) x); | ||||
|   return tt->thread_func (); | ||||
| } | ||||
|  | ||||
| int | ||||
| timer_tracker::gettime (itimerspec *curr_value, bool lock) | ||||
| { | ||||
|   if (lock) | ||||
|     { | ||||
|       AcquireSRWLockExclusive (&srwlock); | ||||
|       if (!is_timer_tracker ()) | ||||
| 	{ | ||||
| 	  ReleaseSRWLockExclusive (&srwlock); | ||||
| 	  return -EINVAL; | ||||
| 	} | ||||
|     } | ||||
|   if (!cancel_evt) | ||||
|     memset (curr_value, 0, sizeof (*curr_value)); | ||||
|   else | ||||
|     { | ||||
|       LONG64 next_relative_exp = get_exp_ts () - get_clock_now (); | ||||
|       curr_value->it_value.tv_sec = next_relative_exp / NS100PERSEC; | ||||
|       next_relative_exp -= curr_value->it_value.tv_sec * NS100PERSEC; | ||||
|       curr_value->it_value.tv_nsec = next_relative_exp | ||||
| 				     * (NSPERSEC / NS100PERSEC); | ||||
|       curr_value->it_interval = time_spec.it_interval; | ||||
|     } | ||||
|   if (lock) | ||||
|     ReleaseSRWLockExclusive (&srwlock); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| timer_tracker::settime (int flags, const itimerspec *new_value, | ||||
| 			itimerspec *old_value) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   __try | ||||
|     { | ||||
|       if (!new_value || !valid_timespec (new_value->it_value) | ||||
| 	  || !valid_timespec (new_value->it_interval)) | ||||
| 	{ | ||||
| 	  ret = -EINVAL; | ||||
| 	  __leave; | ||||
| 	} | ||||
|  | ||||
|       AcquireSRWLockExclusive (&srwlock); | ||||
|       if (!is_timer_tracker ()) | ||||
| 	{ | ||||
| 	  ReleaseSRWLockExclusive (&srwlock); | ||||
| 	  ret = -EINVAL; | ||||
| 	  __leave; | ||||
| 	} | ||||
|  | ||||
|       if (old_value) | ||||
| 	gettime (old_value, false); | ||||
|  | ||||
|       if (!new_value->it_value.tv_sec && !new_value->it_value.tv_nsec) | ||||
| 	{ | ||||
| 	  cancel (); | ||||
| 	  memset (&time_spec, 0, sizeof time_spec); | ||||
| 	  interval = 0; | ||||
| 	  exp_ts = 0; | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  LONG64 ts; | ||||
| 	  LARGE_INTEGER DueTime; | ||||
| 	  BOOLEAN Resume; | ||||
| 	  LONG Period; | ||||
| 	  NTSTATUS status; | ||||
|  | ||||
| 	  if (!timer) | ||||
| 	    { | ||||
| 	      OBJECT_ATTRIBUTES attr; | ||||
|  | ||||
| 	      InitializeObjectAttributes (&attr, NULL, 0, NULL, | ||||
| 					  sec_none_nih.lpSecurityDescriptor); | ||||
| 	      status = NtCreateEvent (&cancel_evt, EVENT_ALL_ACCESS, &attr, | ||||
| 				      NotificationEvent, FALSE); | ||||
| 	      if (!NT_SUCCESS (status)) | ||||
| 		{ | ||||
| 		  ret = -geterrno_from_nt_status (status); | ||||
| 		  __leave; | ||||
| 		} | ||||
| 	      status = NtCreateEvent (&sync_thr, EVENT_ALL_ACCESS, &attr, | ||||
| 				      NotificationEvent, FALSE); | ||||
| 	      if (!NT_SUCCESS (status)) | ||||
| 		{ | ||||
| 		  NtClose (cancel_evt); | ||||
| 		  cancel_evt = NULL; | ||||
| 		  ret = -geterrno_from_nt_status (status); | ||||
| 		  __leave; | ||||
| 		} | ||||
| 	      status = NtCreateTimer (&timer, TIMER_ALL_ACCESS, &attr, | ||||
| 				      SynchronizationTimer); | ||||
| 	      if (!NT_SUCCESS (status)) | ||||
| 		{ | ||||
| 		  NtClose (cancel_evt); | ||||
| 		  NtClose (sync_thr); | ||||
| 		  cancel_evt = sync_thr = NULL; | ||||
| 		  ret = -geterrno_from_nt_status (status); | ||||
| 		  __leave; | ||||
| 		} | ||||
| 	    } | ||||
| 	  ResetEvent (cancel_evt); | ||||
| 	  ResetEvent (sync_thr); | ||||
| 	  NtCancelTimer (timer, NULL); | ||||
| 	  /* Convert incoming itimerspec into 100ns interval and timestamp */ | ||||
| 	  interval = new_value->it_interval.tv_sec * NS100PERSEC | ||||
| 		      + (new_value->it_interval.tv_nsec | ||||
| 			 + (NSPERSEC / NS100PERSEC) - 1) | ||||
| 			/ (NSPERSEC / NS100PERSEC); | ||||
| 	  ts = new_value->it_value.tv_sec * NS100PERSEC | ||||
| 	       + (new_value->it_value.tv_nsec + (NSPERSEC / NS100PERSEC) - 1) | ||||
| 		 / (NSPERSEC / NS100PERSEC); | ||||
| 	  if (flags & TIMER_ABSTIME) | ||||
| 	    { | ||||
| 	      if (clock_id == CLOCK_REALTIME) | ||||
| 		DueTime.QuadPart = ts + FACTOR; | ||||
| 	      else /* non-REALTIME clocks require relative DueTime. */ | ||||
| 		{ | ||||
| 		  DueTime.QuadPart = get_clock_now () - ts; | ||||
| 		  /* If the timestamp was earlier than now, compute number | ||||
| 		     of expirations and offset DueTime to expire immediately. */ | ||||
| 		  if (DueTime.QuadPart >= 0) | ||||
| 		    DueTime.QuadPart = -1LL; | ||||
| 		} | ||||
| 	    } | ||||
| 	  else | ||||
| 	    { | ||||
| 	      /* Keep relative timestamps relative for the timer, but store the | ||||
| 		 expiry timestamp absolute for the timer thread. */ | ||||
| 	      DueTime.QuadPart = -ts; | ||||
| 	      ts += get_clock_now (); | ||||
| 	    } | ||||
| 	  set_exp_ts (ts); | ||||
| 	  time_spec = *new_value; | ||||
| 	  overrun_count_curr = 0; | ||||
| 	  overrun_count = 0; | ||||
| 	  overrun_event_running = OVR_EVENT_DISARMED; | ||||
| 	  /* TODO: CLOCK_REALTIME_ALARM / CLOCK_BOOTTIME_ALARM | ||||
| 	     Note: Advanced Power Settings -> Sleep -> Allow Wake Timers | ||||
| 	     since W10 1709 */ | ||||
| 	  Resume = FALSE; | ||||
| 	  if (interval > INT_MAX * (NS100PERSEC / MSPERSEC)) | ||||
| 	    Period = 0; | ||||
| 	  else | ||||
| 	    Period = (interval + (NS100PERSEC / MSPERSEC) - 1) | ||||
| 		     / (NS100PERSEC / MSPERSEC); | ||||
| 	  status = NtSetTimer (timer, &DueTime, NULL, NULL, Resume, Period, | ||||
| 			       NULL); | ||||
| 	  if (!NT_SUCCESS (status)) | ||||
| 	    { | ||||
| 	      memset (&time_spec, 0, sizeof time_spec); | ||||
| 	      interval = 0; | ||||
| 	      exp_ts = 0; | ||||
| 	      ret = -geterrno_from_nt_status (status); | ||||
| 	      __leave; | ||||
| 	    } | ||||
| 	  new cygthread (timer_thread, this, "itimer", sync_thr); | ||||
| 	} | ||||
|       ReleaseSRWLockExclusive (&srwlock); | ||||
|       ret = 0; | ||||
|     } | ||||
|   __except (EFAULT) {} | ||||
|   __endtry | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| /* The timers are stored on the cygheap. */ | ||||
| #define cnew(name, ...) \ | ||||
|   ({ \ | ||||
|     void* ptr = (void*) ccalloc (HEAP_3_TIMER, 1, sizeof (name)); \ | ||||
|     ptr ? new (ptr) name (__VA_ARGS__) : NULL; \ | ||||
|   }) | ||||
|  | ||||
| extern "C" int | ||||
| timer_create (clockid_t clock_id, struct sigevent *__restrict evp, | ||||
| 	      timer_t *__restrict timerid) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   __try | ||||
|     { | ||||
|       if (CLOCKID_IS_PROCESS (clock_id) || CLOCKID_IS_THREAD (clock_id)) | ||||
| 	{ | ||||
| 	  set_errno (ENOTSUP); | ||||
| 	  return -1; | ||||
| 	} | ||||
|  | ||||
|       if (clock_id >= MAX_CLOCKS) | ||||
| 	{ | ||||
| 	  set_errno (EINVAL); | ||||
| 	  return -1; | ||||
| 	} | ||||
|  | ||||
|       *timerid = (timer_t) cnew (timer_tracker, clock_id, evp); | ||||
|       ret = 0; | ||||
|     } | ||||
|   __except (EFAULT) {} | ||||
|   __endtry | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| extern "C" int | ||||
| timer_gettime (timer_t timerid, struct itimerspec *ovalue) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   __try | ||||
|     { | ||||
|       timer_tracker *tt = (timer_tracker *) timerid; | ||||
|       if (!tt->is_timer_tracker ()) | ||||
| 	{ | ||||
| 	  set_errno (EINVAL); | ||||
| 	  return -1; | ||||
| 	} | ||||
|  | ||||
|       ret = tt->gettime (ovalue, true); | ||||
|       if (ret < 0) | ||||
| 	set_errno (-ret); | ||||
|     } | ||||
|   __except (EFAULT) {} | ||||
|   __endtry | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| extern "C" int | ||||
| timer_settime (timer_t timerid, int flags, | ||||
| 	       const struct itimerspec *__restrict value, | ||||
| 	       struct itimerspec *__restrict ovalue) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   __try | ||||
|     { | ||||
|       timer_tracker *tt = (timer_tracker *) timerid; | ||||
|       if (!tt->is_timer_tracker ()) | ||||
| 	{ | ||||
| 	  set_errno (EINVAL); | ||||
| 	  __leave; | ||||
| 	} | ||||
|       ret = tt->settime (flags, value, ovalue); | ||||
|       if (ret < 0) | ||||
| 	set_errno (-ret); | ||||
|     } | ||||
|   __except (EFAULT) {} | ||||
|   __endtry | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| extern "C" int | ||||
| timer_getoverrun (timer_t timerid) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   __try | ||||
|     { | ||||
|       timer_tracker *tt = (timer_tracker *) timerid; | ||||
|       if (!tt->is_timer_tracker ()) | ||||
| 	{ | ||||
| 	  set_errno (EINVAL); | ||||
| 	  __leave; | ||||
| 	} | ||||
|       LONG64 ov_cnt = tt->getoverrun (); | ||||
|       if (ov_cnt > DELAYTIMER_MAX || ov_cnt < 0) | ||||
| 	ret = DELAYTIMER_MAX; | ||||
|       else | ||||
| 	ret = ov_cnt; | ||||
|     } | ||||
|   __except (EFAULT) {} | ||||
|   __endtry | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| extern "C" int | ||||
| timer_delete (timer_t timerid) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   __try | ||||
|     { | ||||
|       timer_tracker *in_tt = (timer_tracker *) timerid; | ||||
|       if (!in_tt->is_timer_tracker () || in_tt == &itimer_tracker) | ||||
| 	{ | ||||
| 	  set_errno (EINVAL); | ||||
| 	  __leave; | ||||
| 	} | ||||
|       delete in_tt; | ||||
|     } | ||||
|   __except (EFAULT) {} | ||||
|   __endtry | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| extern "C" int | ||||
| setitimer (int which, const struct itimerval *__restrict value, | ||||
| 	   struct itimerval *__restrict ovalue) | ||||
| { | ||||
|   int ret; | ||||
|   if (which != ITIMER_REAL) | ||||
|     { | ||||
|       set_errno (EINVAL); | ||||
|       ret = -1; | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       struct itimerspec spec_value, spec_ovalue; | ||||
|       spec_value.it_interval.tv_sec = value->it_interval.tv_sec; | ||||
|       spec_value.it_interval.tv_nsec = value->it_interval.tv_usec * 1000; | ||||
|       spec_value.it_value.tv_sec = value->it_value.tv_sec; | ||||
|       spec_value.it_value.tv_nsec = value->it_value.tv_usec * 1000; | ||||
|       ret = timer_settime ((timer_t) &itimer_tracker, 0, | ||||
| 			   &spec_value, &spec_ovalue); | ||||
|       if (ret) | ||||
| 	ret = -1; | ||||
|       else if (ovalue) | ||||
| 	{ | ||||
| 	  ovalue->it_interval.tv_sec = spec_ovalue.it_interval.tv_sec; | ||||
| 	  ovalue->it_interval.tv_usec = spec_ovalue.it_interval.tv_nsec / 1000; | ||||
| 	  ovalue->it_value.tv_sec = spec_ovalue.it_value.tv_sec; | ||||
| 	  ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000; | ||||
| 	} | ||||
|     } | ||||
|   syscall_printf ("%R = setitimer()", ret); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| extern "C" int | ||||
| getitimer (int which, struct itimerval *ovalue) | ||||
| { | ||||
|   int ret = -1; | ||||
|  | ||||
|   if (which != ITIMER_REAL) | ||||
|     set_errno (EINVAL); | ||||
|   else | ||||
|     { | ||||
|       __try | ||||
| 	{ | ||||
| 	  struct itimerspec spec_ovalue; | ||||
| 	  ret = timer_gettime ((timer_t) &itimer_tracker, &spec_ovalue); | ||||
| 	  if (!ret) | ||||
| 	    { | ||||
| 	      ovalue->it_interval.tv_sec = spec_ovalue.it_interval.tv_sec; | ||||
| 	      ovalue->it_interval.tv_usec = spec_ovalue.it_interval.tv_nsec / 1000; | ||||
| 	      ovalue->it_value.tv_sec = spec_ovalue.it_value.tv_sec; | ||||
| 	      ovalue->it_value.tv_usec = spec_ovalue.it_value.tv_nsec / 1000; | ||||
| 	    } | ||||
| 	} | ||||
|       __except (EFAULT) {} | ||||
|       __endtry | ||||
|     } | ||||
|   syscall_printf ("%R = getitimer()", ret); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| /* FIXME: POSIX - alarm survives exec */ | ||||
| extern "C" unsigned int | ||||
| alarm (unsigned int seconds) | ||||
| { | ||||
|  struct itimerspec newt = {}, oldt; | ||||
|  /* alarm cannot fail, but only needs not be | ||||
|     correct for arguments < 64k. Truncate */ | ||||
|  if (seconds > (CLOCK_DELAY_MAX / 1000 - 1)) | ||||
|    seconds = (CLOCK_DELAY_MAX / 1000 - 1); | ||||
|  newt.it_value.tv_sec = seconds; | ||||
|  timer_settime ((timer_t) &itimer_tracker, 0, &newt, &oldt); | ||||
|  int ret = oldt.it_value.tv_sec + (oldt.it_value.tv_nsec > 0); | ||||
|  syscall_printf ("%d = alarm(%u)", ret, seconds); | ||||
|  return ret; | ||||
| } | ||||
|  | ||||
| extern "C" useconds_t | ||||
| ualarm (useconds_t value, useconds_t interval) | ||||
| { | ||||
|  struct itimerspec timer = {}, otimer; | ||||
|  /* ualarm cannot fail. | ||||
|     Interpret negative arguments as zero */ | ||||
|  if (value > 0) | ||||
|    { | ||||
|      timer.it_value.tv_sec = value / USPERSEC; | ||||
|      timer.it_value.tv_nsec = (value % USPERSEC) * (NSPERSEC/USPERSEC); | ||||
|    } | ||||
|  if (interval > 0) | ||||
|    { | ||||
|      timer.it_interval.tv_sec = interval / USPERSEC; | ||||
|      timer.it_interval.tv_nsec = (interval % USPERSEC) * (NSPERSEC/USPERSEC); | ||||
|    } | ||||
|  timer_settime ((timer_t) &itimer_tracker, 0, &timer, &otimer); | ||||
|  useconds_t ret = otimer.it_value.tv_sec * USPERSEC | ||||
| 		  + (otimer.it_value.tv_nsec + (NSPERSEC/USPERSEC) - 1) | ||||
| 		    / (NSPERSEC/USPERSEC); | ||||
|  syscall_printf ("%d = ualarm(%ld , %ld)", ret, value, interval); | ||||
|  return ret; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user