Re: [PATCH] Futexes IV (Fast Lightweight Userspace Semaphores)

Rusty Russell
2002-03-19T03:26:55Z

On 17 Mar 2002 16:52:00 -0800
Ulrich Drepper <drepper@redhat.com> wrote:

> On Sat, 2002-03-16 at 22:50, Rusty Russell wrote:
>
> > Only vs. pthread_cond_broadcast.
>
> No. pthread_barrier_wait has the same problem. It has to wake up lots
> of thread.

Hmmm....

What do you WANT in a kernel primitive then? Given that we now have mutexes,
what else do we need to make pthreads relatively painless?

> > And if you're using that you probably
> > have some other performance issues anyway?
>
> Why? Conditional variables are of use in situations with loosely
> coupled threads.

I meant vs. pthread_cond_signal.

Look, here is an example implementation. Please suggest:
1) Where this is flawed,
2) Where this is suboptimal,
3) What kernel primitive would help to resolve these?

Thanks,
Rusty.
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.

/* Assume we have the following semaphore operations:

futex_down(futex);
futex_down_time(futex, relative timeout);
futex_up(futex, count);
*/
typedef struct
{
struct futex futex;
} pthread_mutex_t;

typedef struct
{
int num_waiting;
struct futex wait, ack;
} pthread_cond_t;

typedef struct
{
unsigned int num_left;
struct futex wait;
unsigned int initial_count;
} pthread_barrier_t;

#define PTHREAD_MUTEX_INITIALIZER { { 1 } }
#define PTHREAD_COND_INITIALIZER { 0, { 0 }, { 0 } }

int pthread_barrier_init(struct pthread_barrier_t *barrier,
void *addr,
unsigned int count)
{
barrier->num_left = barrier->initial_count = count;
barrier->wait.count = 0;
}

int pthread_barrier_wait(struct pthread_barrier_t *barrier)
{
if (atomic_dec_and_test(&barrier->num_left)) {
/* Restore barrier. */
barrier->num_left = barrier->initial_count;
/* Wake the other threads */
futex_up(&barrier->wait, barrier->initial_count-1);
return 0; /* PTHREAD_BARRIER_SERIAL_THREAD */
}
while (futex_down(&barrier->wait) == 0 || errno == EINTR);
return 1;
}

int pthread_cond_signal(pthread_cond_t *cond)
{
if (cond->num_waiters)
return futex_up(&cond->futex, 1);
return 0;
}

int pthread_cond_broadcast(pthread_cond_t *cond)
{
unsigned int waiters = cond->num_waiting;

if (waiters) {
/* Re-initialize ACK. Could have been upped by
pthread_cond_signal and pthread_cond_wait. */
cond->ack.count = 0;
futex_up(&cond->futex, waiters);
/* Wait for ack before returning. */
futex_down(&cond->ack);
}
return 0;
}

static int __pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *reltime)
{
int ret;

/* Increment first so broadcaster knows we are waiting. */
atomic_inc(cond->num_waiting);
futex_up(&mutex, 1);
do {
ret = futex_down_time(&cond, reltime);
} while (ret < 0 && errno == EINTR);
if (atomic_dec_and_test(cond->num_waiting))
futex_up(&cond->ack);
futex_down(&mutex->futex);
return ret;
}

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
return __pthread_cond_wait(cond, mutex, NULL);
}

int pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *abstime)
{
struct timeval _now;
struct timespec now, rel;

/* Absolute to relative */
gettimeofday(&_now, NULL);
TIMEVAL_TO_TIMESPEC(&_now, &now);
if (now.tv_sec > abstime->tv_sec
|| (now.tv_sec == abstime->tv_sec
&& now.tv_nsec > abstime->tv_nsec))
return ETIMEDOUT;

rel.tv_sec = now.tv_sec - abstime->tv_sec;
rel.tv_nsec = now.tv_usec - abstime->tv_usec;
if (rel.tv_nsec < 0) {
--rel.tv_sec;
rel.tv_nsec += 1000000000;
}
return __pthread_cond_wait(cond, mutex, &rel);
}