149 lines
3.0 KiB
C
149 lines
3.0 KiB
C
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#include "config.h"
|
|
#include "runtime.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <semaphore.h>
|
|
|
|
/* If we don't have sem_timedwait, use pthread_cond_timedwait instead.
|
|
We don't always use condition variables because on some systems
|
|
pthread_mutex_lock and pthread_mutex_unlock must be called by the
|
|
same thread. That is never true of semaphores. */
|
|
|
|
struct go_sem
|
|
{
|
|
sem_t sem;
|
|
|
|
#ifndef HAVE_SEM_TIMEDWAIT
|
|
int timedwait;
|
|
pthread_mutex_t mutex;
|
|
pthread_cond_t cond;
|
|
#endif
|
|
};
|
|
|
|
/* Create a semaphore. */
|
|
|
|
uintptr
|
|
runtime_semacreate(void)
|
|
{
|
|
struct go_sem *p;
|
|
|
|
/* Call malloc rather than runtime_malloc. This will allocate space
|
|
on the C heap. We can't call runtime_malloc here because it
|
|
could cause a deadlock. */
|
|
p = malloc (sizeof (struct go_sem));
|
|
if (sem_init (&p->sem, 0, 0) != 0)
|
|
runtime_throw ("sem_init");
|
|
|
|
#ifndef HAVE_SEM_TIMEDWAIT
|
|
if (pthread_mutex_init (&p->mutex, NULL) != 0)
|
|
runtime_throw ("pthread_mutex_init");
|
|
if (pthread_cond_init (&p->cond, NULL) != 0)
|
|
runtime_throw ("pthread_cond_init");
|
|
#endif
|
|
|
|
return (uintptr) p;
|
|
}
|
|
|
|
/* Acquire m->waitsema. */
|
|
|
|
int32
|
|
runtime_semasleep (int64 ns)
|
|
{
|
|
M *m;
|
|
struct go_sem *sem;
|
|
int r;
|
|
|
|
m = runtime_m ();
|
|
sem = (struct go_sem *) m->waitsema;
|
|
if (ns >= 0)
|
|
{
|
|
int64 abs;
|
|
struct timespec ts;
|
|
int err;
|
|
|
|
abs = ns + runtime_nanotime ();
|
|
ts.tv_sec = abs / 1000000000LL;
|
|
ts.tv_nsec = abs % 1000000000LL;
|
|
|
|
err = 0;
|
|
|
|
#ifdef HAVE_SEM_TIMEDWAIT
|
|
r = sem_timedwait (&sem->sem, &ts);
|
|
if (r != 0)
|
|
err = errno;
|
|
#else
|
|
if (pthread_mutex_lock (&sem->mutex) != 0)
|
|
runtime_throw ("pthread_mutex_lock");
|
|
|
|
while ((r = sem_trywait (&sem->sem)) != 0)
|
|
{
|
|
r = pthread_cond_timedwait (&sem->cond, &sem->mutex, &ts);
|
|
if (r != 0)
|
|
{
|
|
err = r;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pthread_mutex_unlock (&sem->mutex) != 0)
|
|
runtime_throw ("pthread_mutex_unlock");
|
|
#endif
|
|
|
|
if (err != 0)
|
|
{
|
|
if (err == ETIMEDOUT || err == EAGAIN || err == EINTR)
|
|
return -1;
|
|
runtime_throw ("sema_timedwait");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
while (sem_wait (&sem->sem) != 0)
|
|
{
|
|
if (errno == EINTR)
|
|
continue;
|
|
runtime_throw ("sem_wait");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Wake up mp->waitsema. */
|
|
|
|
void
|
|
runtime_semawakeup (M *mp)
|
|
{
|
|
struct go_sem *sem;
|
|
|
|
sem = (struct go_sem *) mp->waitsema;
|
|
if (sem_post (&sem->sem) != 0)
|
|
runtime_throw ("sem_post");
|
|
|
|
#ifndef HAVE_SEM_TIMEDWAIT
|
|
if (pthread_mutex_lock (&sem->mutex) != 0)
|
|
runtime_throw ("pthread_mutex_lock");
|
|
if (pthread_cond_broadcast (&sem->cond) != 0)
|
|
runtime_throw ("pthread_cond_broadcast");
|
|
if (pthread_mutex_unlock (&sem->mutex) != 0)
|
|
runtime_throw ("pthread_mutex_unlock");
|
|
#endif
|
|
}
|
|
|
|
void
|
|
runtime_osinit (void)
|
|
{
|
|
runtime_ncpu = getproccount();
|
|
}
|
|
|
|
void
|
|
runtime_goenvs (void)
|
|
{
|
|
runtime_goenvs_unix ();
|
|
}
|