Skip to content
Snippets Groups Projects
Commit 4513537e authored by Nadav Har'El's avatar Nadav Har'El Committed by Pekka Enberg
Browse files

clock: Support different clocks in pthread_cond_wait


Fix pthread_cond_timedwait to set the absolute timer using a timepoint,
instead of the old s64.

Moreover, now that we have both a wall-clock and monotonic clock, we
can support pthread_condattr_setclock, so this patch also adds this
support. OpenJDK 8, for example, cannot run without this support (it
assumes that if the OS supports CLOCK_MONOTONIC, it can also configure
condition variables to use it).

Unfortunately supporting pthread_condattr_setclock - the only condition-
variable attribute that really exists - grows the pthread condition
variable structure :(

Fixes #168.

Reviewed-by: default avatarGlauber Costa <glommer@cloudius-systems.com>
Signed-off-by: default avatarNadav Har'El <nyh@cloudius-systems.com>
Signed-off-by: default avatarPekka Enberg <penberg@cloudius-systems.com>
parent 0f5992ca
No related branches found
No related tags found
No related merge requests found
......@@ -281,13 +281,23 @@ int pthread_sigmask(int how, const sigset_t* set, sigset_t* oldset)
}
#ifdef LOCKFREE_MUTEX
typedef lazy_indirect<condvar> indirect_condvar;
struct pthread_condvar {
condvar cond;
clockid_t clock {CLOCK_REALTIME};
};
typedef lazy_indirect<pthread_condvar> indirect_condvar;
static_assert(sizeof(indirect_condvar) < sizeof(pthread_cond_t), "condvar overflow");
int pthread_cond_init(pthread_cond_t* __restrict c,
const pthread_condattr_t* __restrict attr)
{
// FIXME: respect attr
new (c) indirect_condvar;
// There's not much that attr can say. OSv doesn't have processes so the
// pshared attribute is irrelevant. All that remains is the clock, and
// since this can only specify CLOCK_REALTIME or CLOCK_MONOTONIC, a
// single byte in pthread_condattr_t is enough for us.
if (attr && *reinterpret_cast<const char*>(attr)) {
reinterpret_cast<indirect_condvar*>(c)->get()->clock = CLOCK_MONOTONIC;
}
return 0;
}
int pthread_cond_destroy(pthread_cond_t *c)
......@@ -297,7 +307,7 @@ int pthread_cond_destroy(pthread_cond_t *c)
}
condvar* from_libc(pthread_cond_t* c)
{
return reinterpret_cast<indirect_condvar*>(c)->get();
return &(reinterpret_cast<indirect_condvar*>(c)->get()->cond);
}
#else
......@@ -343,7 +353,20 @@ int pthread_cond_timedwait(pthread_cond_t *__restrict cond,
const struct timespec* __restrict ts)
{
sched::timer tmr(*sched::thread::current());
tmr.set(u64(ts->tv_sec) * 1000000000 + ts->tv_nsec);
switch(reinterpret_cast<indirect_condvar*>(cond)->get()->clock) {
case CLOCK_REALTIME:
tmr.set(osv::clock::wall::time_point(
std::chrono::seconds(ts->tv_sec) +
std::chrono::nanoseconds(ts->tv_nsec)));
break;
case CLOCK_MONOTONIC:
tmr.set(osv::clock::uptime::time_point(
std::chrono::seconds(ts->tv_sec) +
std::chrono::nanoseconds(ts->tv_nsec)));
break;
default:
assert(0); // pthread_cond_init() will never allow this case
}
return from_libc(cond)->wait(from_libc(mutex), &tmr);
}
......@@ -524,36 +547,55 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
return EINVAL;
}
int pthread_condattr_init(pthread_condattr_t *)
int pthread_condattr_init(pthread_condattr_t *attr)
{
WARN_STUBBED();
// We assume there's room for at least one byte in pthread_condattr_t
// and use this single byte to specify the clockid. The default is 0
// (which we take to signify CLOCK_REALTIME).
*reinterpret_cast<char*>(attr) = 0;
return 0;
}
int pthread_condattr_destroy(pthread_condattr_t *)
{
WARN_STUBBED();
return EINVAL;
return 0;
}
int pthread_condattr_setclock(pthread_condattr_t *, clockid_t)
int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clockid)
{
WARN_STUBBED();
return EINVAL;
char byte;
switch (clockid) {
case CLOCK_REALTIME:
byte = 0;
break;
case CLOCK_MONOTONIC:
byte = 1;
break;
default:
return EINVAL;
}
*reinterpret_cast<char*>(attr) = byte;
return 0;
}
int pthread_condattr_setpshared(pthread_condattr_t *, int)
int pthread_condattr_getclock(const pthread_condattr_t *__restrict attr,
clockid_t *__restrict clockid)
{
WARN_STUBBED();
return EINVAL;
if (*reinterpret_cast<const char*>(attr)) {
*clockid = CLOCK_MONOTONIC;
} else {
*clockid = CLOCK_REALTIME;
}
return 0;
}
int pthread_condattr_getclock(const pthread_condattr_t *__restrict, clockid_t *__restrict)
int pthread_condattr_setpshared(pthread_condattr_t *, int)
{
WARN_STUBBED();
return EINVAL;
}
int pthread_condattr_getpshared(const pthread_condattr_t *__restrict, int *__restrict)
{
WARN_STUBBED();
......
......@@ -123,6 +123,41 @@ int main(void)
void* ret;
pthread_join(thread, &ret);
// Test that we can configure pthread_cond_timedwait to use
// CLOCK_MONOTONIC, and that it works as expected - we set a short but
// nonzero timeout, and expect to reach it, but not immediately (since
// the monotonic clock is much lower than the realtime clock, thinking
// it is a realtime clock value will result in an immediate timeout).
int r;
pthread_mutex_t mutex2;
pthread_cond_t cond2;
pthread_condattr_t attr2;
r = pthread_condattr_init(&attr2);
report("pthread_condattr_init", r == 0);
r= pthread_condattr_setclock(&attr2, CLOCK_MONOTONIC);
report("pthread_condattr_setclock", r == 0);
pthread_mutex_init(&mutex2, NULL);
r = pthread_cond_init(&cond2, &attr2);
report("pthread_cond_init", r == 0);
r = pthread_condattr_destroy(&attr2);
report("pthread_condattr_destroy", r == 0);
struct timespec ts;
r = clock_gettime(CLOCK_MONOTONIC, &ts);
report("clock_gettime(CLOCK_MONOTONIC)", r == 0);
struct timespec to = ts;
to.tv_nsec += 500000000;
pthread_mutex_lock(&mutex2);
r = pthread_cond_timedwait(&cond2, &mutex2, &to);
pthread_mutex_unlock(&mutex2);
report("pthread_cond_timedwait (short)", r == ETIMEDOUT);
struct timespec ts2;
clock_gettime(CLOCK_MONOTONIC, &ts2);
long ns = (ts2.tv_sec - ts.tv_sec)*1000000000 + (ts2.tv_nsec - ts.tv_nsec);
report("should have not returned immediately", ns > 400000000);
printf("ts = %ld,%ld\n",ts.tv_sec, ts.tv_nsec);
printf("ts2 = %ld,%ld\n",ts2.tv_sec, ts2.tv_nsec);
printf("ns = %ld\n",ns);
printf("SUMMARY: %u tests / %u failures\n", tests_total, tests_failed);
return tests_failed == 0 ? 0 : 1;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment