Index: include/semaphore.h =================================================================== --- include/semaphore.h +++ include/semaphore.h @@ -59,6 +59,10 @@ sem_t *sem_open(const char *, int, ...); int sem_post(sem_t *); int sem_timedwait(sem_t * __restrict, const struct timespec * __restrict); +#ifdef _CLOCKID_T_DECLARED +int sem_timedwait5_np(sem_t * __restrict, clockid_t, int, + const struct timespec *, struct timespec *); +#endif int sem_trywait(sem_t *); int sem_unlink(const char *); int sem_wait(sem_t *); Index: lib/libc/gen/Makefile.inc =================================================================== --- lib/libc/gen/Makefile.inc +++ lib/libc/gen/Makefile.inc @@ -463,6 +463,7 @@ MLINKS+=sem_open.3 sem_close.3 \ sem_open.3 sem_unlink.3 MLINKS+=sem_wait.3 sem_trywait.3 +MLINKS+=sem_timedwait.3 sem_timedwait5_np.3 MLINKS+=send.2 sendmmsg.2 MLINKS+=setjmp.3 _longjmp.3 \ setjmp.3 _setjmp.3 \ Index: lib/libc/gen/Symbol.map =================================================================== --- lib/libc/gen/Symbol.map +++ lib/libc/gen/Symbol.map @@ -415,6 +415,7 @@ FBSD_1.5 { basename; dirname; + sem_timedwait5_np; }; FBSDprivate_1.0 { Index: lib/libc/gen/sem_new.c =================================================================== --- lib/libc/gen/sem_new.c +++ lib/libc/gen/sem_new.c @@ -56,6 +56,7 @@ __weak_reference(_sem_open, sem_open); __weak_reference(_sem_post, sem_post); __weak_reference(_sem_timedwait, sem_timedwait); +__weak_reference(_sem_timedwait5_np, sem_timedwait5_np); __weak_reference(_sem_trywait, sem_trywait); __weak_reference(_sem_unlink, sem_unlink); __weak_reference(_sem_wait, sem_wait); @@ -345,23 +346,34 @@ } static __inline int -usem_wait(struct _usem2 *sem, const struct timespec *abstime) +usem_wait(struct _usem2 *sem, clockid_t clock_id, int flags, + const struct timespec *rqtp, struct timespec *rmtp) { - struct _umtx_time *tm_p, timeout; + struct { + struct _umtx_time timeout; + struct timespec remain; + } tms; + void *tm_p; size_t tm_size; + int retval; - if (abstime == NULL) { + if (rqtp == NULL) { tm_p = NULL; tm_size = 0; } else { - timeout._clockid = CLOCK_REALTIME; - timeout._flags = UMTX_ABSTIME; - timeout._timeout = *abstime; - tm_p = &timeout; - tm_size = sizeof(timeout); + tms.timeout._clockid = clock_id; + tms.timeout._flags = (flags & TIMER_ABSTIME) ? UMTX_ABSTIME : 0; + tms.timeout._timeout = *rqtp; + tm_p = &tms; + tm_size = sizeof(tms); } - return _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0, - (void *)tm_size, __DECONST(void*, tm_p)); + retval = _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0, (void *)tm_size, tm_p); + if (retval == -1 && errno == EINTR && (flags & TIMER_ABSTIME) == 0 && + rqtp != NULL && rmtp != NULL) { + *rmtp = tms.remain; + } + + return (retval); } int @@ -381,8 +393,8 @@ } int -_sem_timedwait(sem_t * __restrict sem, - const struct timespec * __restrict abstime) +_sem_timedwait5_np(sem_t * __restrict sem, clockid_t clock_id, int flags, + const struct timespec *rqtp, struct timespec *rmtp) { int val, retval; @@ -393,7 +405,8 @@ _pthread_testcancel(); for (;;) { while (USEM_COUNT(val = sem->_kern._count) > 0) { - if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) + if (atomic_cmpset_acq_int(&sem->_kern._count, val, + val - 1)) return (0); } @@ -406,20 +419,28 @@ * The timeout argument is only supposed to * be checked if the thread would have blocked. */ - if (abstime != NULL) { - if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { + if (rqtp != NULL) { + if (rqtp->tv_nsec >= 1000000000 || rqtp->tv_nsec < 0) { errno = EINVAL; return (-1); } } _pthread_cancel_enter(1); - retval = usem_wait(&sem->_kern, abstime); + retval = usem_wait(&sem->_kern, clock_id, flags, rqtp, rmtp); _pthread_cancel_leave(0); } return (retval); } int +_sem_timedwait(sem_t * __restrict sem, + const struct timespec * __restrict abstime) +{ + return (_sem_timedwait5_np(sem, CLOCK_REALTIME, TIMER_ABSTIME, abstime, + NULL)); +}; + +int _sem_wait(sem_t *sem) { return _sem_timedwait(sem, NULL); Index: lib/libc/gen/sem_timedwait.3 =================================================================== --- lib/libc/gen/sem_timedwait.3 +++ lib/libc/gen/sem_timedwait.3 @@ -34,11 +34,12 @@ .\" .\" $FreeBSD$ .\" -.Dd August 17, 2016 +.Dd February 17, 2017 .Dt SEM_TIMEDWAIT 3 .Os .Sh NAME -.Nm sem_timedwait +.Nm sem_timedwait , +.Nm sem_timedwait5_np .Nd "lock a semaphore" .Sh LIBRARY .Lb libc @@ -46,6 +47,10 @@ .In semaphore.h .Ft int .Fn sem_timedwait "sem_t * restrict sem" "const struct timespec * restrict abs_timeout" +.In time.h /* before... */ +.In semaphore.h +.Ft int +.Fn sem_timedwait5_np "sem_t * restrict sem" "clockid_t clock_id" "int flags" "const struct timespec * rqtp" "struct timespec * rmtp" .Sh DESCRIPTION The .Fn sem_timedwait @@ -77,10 +82,40 @@ The validity of the .Fa abs_timeout is not checked if the semaphore can be locked immediately. -.Sh RETURN VALUES +.Pp The -.Fn sem_timedwait -function returns zero if the calling process successfully performed the +.Fn sem_timedwait5_np +function is a more flexible variant of +.Fn sem_timedwait . +The +.Fa clock_id +parameter specifies the reference clock. +If the +.Fa flags +parameter contains +.Dv TIMER_ABSTIME , +then the requested timeout +.Pq Fa rqtp +is an absolute timeout; otherwise, +the timeout is relative. +If this function fails with +.Er EINTR +and the timeout is relative, +a non-NULL +.Fa rmtp +will be updated to contain the amount of time remaining in the interval +.Po +the requested time minus the time actually slept +.Pc . +An absolute timeout has no effect on +.Fa rmtp . +A single structure can be used for both +.Fa rqtp +and +.Fa rmtp . +.Sh RETURN VALUES +These +functions return zero if the calling process successfully performed the semaphore lock operation on the semaphore designated by .Fa sem . If the call was unsuccessful, the state of the semaphore is unchanged, @@ -88,9 +123,7 @@ .Va errno to indicate the error. .Sh ERRORS -The -.Fn sem_timedwait -function will fail if: +These functions will fail if: .Bl -tag -width Er .It Bq Er EINVAL The @@ -114,6 +147,16 @@ .Fn sem_timedwait function conforms to .St -p1003.1-2004 . +The +.Fn sem_timedwait5_np +function is not specified by any standard; +it exists only on FreeBSD at the time of this writing. .Sh HISTORY -The function first appeared in +The +.Fn sem_timedwait +function first appeared in .Fx 5.0 . +The +.Fn sem_timedwait5_np +function first appeared in +.Fx 12.0 . Index: lib/libc/include/namespace.h =================================================================== --- lib/libc/include/namespace.h +++ lib/libc/include/namespace.h @@ -217,6 +217,7 @@ #define sem_open _sem_open #define sem_post _sem_post #define sem_timedwait _sem_timedwait +#define sem_timedwait5_np _sem_timedwait5_np #define sem_trywait _sem_trywait #define sem_unlink _sem_unlink #define sem_wait _sem_wait Index: lib/libc/include/un-namespace.h =================================================================== --- lib/libc/include/un-namespace.h +++ lib/libc/include/un-namespace.h @@ -198,6 +198,7 @@ #undef sem_open #undef sem_post #undef sem_timedwait +#undef sem_timedwait5_np #undef sem_trywait #undef sem_unlink #undef sem_wait Index: share/man/man3/pthread_testcancel.3 =================================================================== --- share/man/man3/pthread_testcancel.3 +++ share/man/man3/pthread_testcancel.3 @@ -1,5 +1,5 @@ .\" $FreeBSD$ -.Dd August 16, 2016 +.Dd February 17, 2017 .Dt PTHREAD_TESTCANCEL 3 .Os .Sh NAME @@ -120,9 +120,9 @@ The .Fn kevent function is a cancellation point if it is potentially blocking, -i.e. when the +such as when the .Fa nevents -argument is non-zero. +argument is non-zero. .It Fn mq_receive .It Fn mq_send .It Fn mq_timedreceive @@ -146,6 +146,7 @@ .It Fn recvmsg .It Fn select .It Fn sem_timedwait +.It Fn sem_timedwait5_np .It Fn sem_wait .It Fn send .It Fn sendmsg Index: sys/kern/kern_umtx.c =================================================================== --- sys/kern/kern_umtx.c +++ sys/kern/kern_umtx.c @@ -3219,10 +3219,16 @@ error = 0; else { umtxq_remove(uq); - /* A relative timeout cannot be restarted. */ - if (error == ERESTART && timeout != NULL && - (timeout->_flags & UMTX_ABSTIME) == 0) - error = EINTR; + if (timeout != NULL && (timeout->_flags & UMTX_ABSTIME) == 0) { + /* A relative timeout cannot be restarted. */ + if (error == ERESTART) + error = EINTR; + if (error == EINTR) { + abs_timeout_update(&timo); + timeout->_timeout = timo.end; + timespecsub(&timeout->_timeout, &timo.cur); + } + } } umtxq_unlock(&uq->uq_key); umtx_key_release(&uq->uq_key); @@ -3585,19 +3591,33 @@ __umtx_op_sem2_wait(struct thread *td, struct _umtx_op_args *uap) { struct _umtx_time *tm_p, timeout; + size_t uasize; int error; /* Allow a null timespec (wait forever). */ - if (uap->uaddr2 == NULL) + if (uap->uaddr2 == NULL) { + uasize = 0; tm_p = NULL; - else { - error = umtx_copyin_umtx_time( - uap->uaddr2, (size_t)uap->uaddr1, &timeout); + } else { + uasize = (size_t)uap->uaddr1; + error = umtx_copyin_umtx_time(uap->uaddr2, uasize, &timeout); if (error != 0) return (error); tm_p = &timeout; } - return (do_sem2_wait(td, uap->obj, tm_p)); + error = do_sem2_wait(td, uap->obj, tm_p); + if (error == EINTR && uap->uaddr2 != NULL && + (timeout._flags & UMTX_ABSTIME) == 0 && + uasize >= sizeof(struct _umtx_time) + sizeof(struct timespec)) { + error = copyout(&timeout._timeout, + (struct _umtx_time *)uap->uaddr2 + 1, + sizeof(struct timespec)); + if (error == 0) { + error = EINTR; + } + } + + return (error); } static int @@ -4194,19 +4214,37 @@ __umtx_op_sem2_wait_compat32(struct thread *td, struct _umtx_op_args *uap) { struct _umtx_time *tm_p, timeout; + size_t uasize; int error; /* Allow a null timespec (wait forever). */ - if (uap->uaddr2 == NULL) + if (uap->uaddr2 == NULL) { + uasize = 0; tm_p = NULL; - else { - error = umtx_copyin_umtx_time32(uap->uaddr2, - (size_t)uap->uaddr1, &timeout); + } else { + uasize = (size_t)uap->uaddr1; + error = umtx_copyin_umtx_time32(uap->uaddr2, uasize, &timeout); if (error != 0) return (error); tm_p = &timeout; } - return (do_sem2_wait(td, uap->obj, tm_p)); + error = do_sem2_wait(td, uap->obj, tm_p); + if (error == EINTR && uap->uaddr2 != NULL && + (timeout._flags & UMTX_ABSTIME) == 0 && + uasize >= sizeof(struct umtx_time32) + sizeof(struct timespec32)) { + struct timespec32 remain32 = { + .tv_sec = timeout._timeout.tv_sec, + .tv_nsec = timeout._timeout.tv_nsec + }; + error = copyout(&remain32, + (struct umtx_time32 *)uap->uaddr2 + 1, + sizeof(struct timespec32)); + if (error == 0) { + error = EINTR; + } + } + + return (error); } static int