Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/nsprpub/pr/src/pthreads/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 30 kB image not shown  

Quelle  ptsynch.c   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/*
** File:            ptsynch.c
** Descritpion:        Implemenation for thread synchronization using pthreads
** Exports:            prlock.h, prcvar.h, prmon.h, prcmon.h
*/


#if defined(_PR_PTHREADS)

#  include "primpl.h"
#  include "obsolete/prsem.h"

#  include <string.h>
#  include <pthread.h>
#  include <sys/time.h>

static pthread_mutexattr_t _pt_mattr;
static pthread_condattr_t _pt_cvar_attr;

#  if defined(DEBUG)
extern PTDebug pt_debug; /* this is shared between several modules */
#  endif                 /* defined(DEBUG) */

#  if defined(FREEBSD)
/*
 * On older versions of FreeBSD, pthread_mutex_trylock returns EDEADLK.
 * Newer versions return EBUSY.  We still need to support both.
 */

static int pt_pthread_mutex_is_locked(pthread_mutex_t* m) {
  int rv = pthread_mutex_trylock(m);
  return (EBUSY == rv || EDEADLK == rv);
}
#  endif

/**************************************************************/
/**************************************************************/
/*****************************LOCKS****************************/
/**************************************************************/
/**************************************************************/

void _PR_InitLocks(void) {
  int rv;
  rv = _PT_PTHREAD_MUTEXATTR_INIT(&_pt_mattr);
  PR_ASSERT(0 == rv);

#  if (defined(LINUX) &&                                               \
       (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))) || \
      (defined(FREEBSD) && __FreeBSD_version > 700055)
  rv = pthread_mutexattr_settype(&_pt_mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
  PR_ASSERT(0 == rv);
#  endif

  rv = _PT_PTHREAD_CONDATTR_INIT(&_pt_cvar_attr);
  PR_ASSERT(0 == rv);
}

static void pt_PostNotifies(PRLock* lock, PRBool unlock) {
  PRIntn index, rv;
  _PT_Notified post;
  _PT_Notified *notified, *prev = NULL;
  /*
   * Time to actually notify any conditions that were affected
   * while the lock was held. Get a copy of the list that's in
   * the lock structure and then zero the original. If it's
   * linked to other such structures, we own that storage.
   */

  post = lock->notified; /* a safe copy; we own the lock */

#  if defined(DEBUG)
  memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */
#  else
  lock->notified.length = 0; /* these are really sufficient */
  lock->notified.link = NULL;
#  endif

  /* should (may) we release lock before notifying? */
  if (unlock) {
    rv = pthread_mutex_unlock(&lock->mutex);
    PR_ASSERT(0 == rv);
  }

  notified = &post; /* this is where we start */
  do {
    for (index = 0; index < notified->length; ++index) {
      PRCondVar* cv = notified->cv[index].cv;
      PR_ASSERT(NULL != cv);
      PR_ASSERT(0 != notified->cv[index].times);
      if (-1 == notified->cv[index].times) {
        rv = pthread_cond_broadcast(&cv->cv);
        PR_ASSERT(0 == rv);
      } else {
        while (notified->cv[index].times-- > 0) {
          rv = pthread_cond_signal(&cv->cv);
          PR_ASSERT(0 == rv);
        }
      }
#  if defined(DEBUG)
      pt_debug.cvars_notified += 1;
      if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) {
        pt_debug.delayed_cv_deletes += 1;
        PR_DestroyCondVar(cv);
      }
#  else  /* defined(DEBUG) */
      if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) {
        PR_DestroyCondVar(cv);
      }
#  endif /* defined(DEBUG) */
    }
    prev = notified;
    notified = notified->link;
    if (&post != prev) {
      PR_DELETE(prev);
    }
  } while (NULL != notified);
/* pt_PostNotifies */

PR_IMPLEMENT(PRLock*) PR_NewLock(void) {
  PRIntn rv;
  PRLock* lock;

  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  lock = PR_NEWZAP(PRLock);
  if (lock != NULL) {
    rv = _PT_PTHREAD_MUTEX_INIT(lock->mutex, _pt_mattr);
    PR_ASSERT(0 == rv);
  }
#  if defined(DEBUG)
  pt_debug.locks_created += 1;
#  endif
  return lock;
/* PR_NewLock */

PR_IMPLEMENT(void) PR_DestroyLock(PRLock* lock) {
  PRIntn rv;
  PR_ASSERT(NULL != lock);
  PR_ASSERT(PR_FALSE == lock->locked);
  PR_ASSERT(0 == lock->notified.length);
  PR_ASSERT(NULL == lock->notified.link);
  rv = pthread_mutex_destroy(&lock->mutex);
  PR_ASSERT(0 == rv);
#  if defined(DEBUG)
  memset(lock, 0xaf, sizeof(PRLock));
  pt_debug.locks_destroyed += 1;
#  endif
  PR_Free(lock);
/* PR_DestroyLock */

PR_IMPLEMENT(void) PR_Lock(PRLock* lock) {
  /* Nb: PR_Lock must not call PR_GetCurrentThread to access the |id| or
   * |tid| field of the current thread's PRThread structure because
   * _pt_root calls PR_Lock before setting thred->id and thred->tid. */

  PRIntn rv;
  PR_ASSERT(lock != NULL);
  rv = pthread_mutex_lock(&lock->mutex);
  PR_ASSERT(0 == rv);
  PR_ASSERT(0 == lock->notified.length);
  PR_ASSERT(NULL == lock->notified.link);
  PR_ASSERT(PR_FALSE == lock->locked);
  /* Nb: the order of the next two statements is not critical to
   * the correctness of PR_AssertCurrentThreadOwnsLock(), but
   * this particular order makes the assertion more likely to
   * catch errors. */

  lock->owner = pthread_self();
  lock->locked = PR_TRUE;
#  if defined(DEBUG)
  pt_debug.locks_acquired += 1;
#  endif
/* PR_Lock */

PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock* lock) {
  pthread_t self = pthread_self();
  PRIntn rv;

  PR_ASSERT(lock != NULL);
  PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(lock->mutex));
  PR_ASSERT(PR_TRUE == lock->locked);
  PR_ASSERT(pthread_equal(lock->owner, self));

  if (!lock->locked || !pthread_equal(lock->owner, self)) {
    return PR_FAILURE;
  }

  lock->locked = PR_FALSE;
  if (0 == lock->notified.length) /* shortcut */
  {
    rv = pthread_mutex_unlock(&lock->mutex);
    PR_ASSERT(0 == rv);
  } else {
    pt_PostNotifies(lock, PR_TRUE);
  }

#  if defined(DEBUG)
  pt_debug.locks_released += 1;
#  endif
  return PR_SUCCESS;
/* PR_Unlock */

PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock* lock) {
  /* Nb: the order of the |locked| and |owner==me| checks is not critical
   * to the correctness of PR_AssertCurrentThreadOwnsLock(), but
   * this particular order makes the assertion more likely to
   * catch errors. */

  PR_ASSERT(lock->locked && pthread_equal(lock->owner, pthread_self()));
}

/**************************************************************/
/**************************************************************/
/***************************CONDITIONS*************************/
/**************************************************************/
/**************************************************************/

/*
 * This code is used to compute the absolute time for the wakeup.
 * It's moderately ugly, so it's defined here and called in a
 * couple of places.
 */

#  define PT_NANOPERMICRO 1000UL
#  define PT_BILLION 1000000000UL

static PRIntn pt_TimedWait(pthread_cond_t* cv, pthread_mutex_t* ml,
                           PRIntervalTime timeout) {
  int rv;
  struct timeval now;
  struct timespec tmo;
  PRUint32 ticks = PR_TicksPerSecond();

  tmo.tv_sec = (PRInt32)(timeout / ticks);
  tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks));
  tmo.tv_nsec =
      (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec);

  /* pthreads wants this in absolute time, off we go ... */
  (void)GETTIMEOFDAY(&now);
  /* that one's usecs, this one's nsecs - grrrr! */
  tmo.tv_sec += now.tv_sec;
  tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec);
  tmo.tv_sec += tmo.tv_nsec / PT_BILLION;
  tmo.tv_nsec %= PT_BILLION;

  rv = pthread_cond_timedwait(cv, ml, &tmo);

  /* NSPR doesn't report timeouts */
  return (rv == ETIMEDOUT) ? 0 : rv;
/* pt_TimedWait */

/*
 * Notifies just get posted to the protecting mutex. The
 * actual notification is done when the lock is released so that
 * MP systems don't contend for a lock that they can't have.
 */

static void pt_PostNotifyToCvar(PRCondVar* cvar, PRBool broadcast) {
  PRIntn index = 0;
  _PT_Notified* notified = &cvar->lock->notified;

  PR_ASSERT(PR_TRUE == cvar->lock->locked);
  PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));
  PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));

  while (1) {
    for (index = 0; index < notified->length; ++index) {
      if (notified->cv[index].cv == cvar) {
        if (broadcast) {
          notified->cv[index].times = -1;
        } else if (-1 != notified->cv[index].times) {
          notified->cv[index].times += 1;
        }
        return/* we're finished */
      }
    }
    /* if not full, enter new CV in this array */
    if (notified->length < PT_CV_NOTIFIED_LENGTH) {
      break;
    }

    /* if there's no link, create an empty array and link it */
    if (NULL == notified->link) {
      notified->link = PR_NEWZAP(_PT_Notified);
    }
    notified = notified->link;
  }

  /* A brand new entry in the array */
  (void)PR_ATOMIC_INCREMENT(&cvar->notify_pending);
  notified->cv[index].times = (broadcast) ? -1 : 1;
  notified->cv[index].cv = cvar;
  notified->length += 1;
/* pt_PostNotifyToCvar */

PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock* lock) {
  PRCondVar* cv = PR_NEW(PRCondVar);
  PR_ASSERT(lock != NULL);
  if (cv != NULL) {
    int rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
    PR_ASSERT(0 == rv);
    if (0 == rv) {
      cv->lock = lock;
      cv->notify_pending = 0;
#  if defined(DEBUG)
      pt_debug.cvars_created += 1;
#  endif
    } else {
      PR_DELETE(cv);
      cv = NULL;
    }
  }
  return cv;
/* PR_NewCondVar */

PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar* cvar) {
  if (0 > PR_ATOMIC_DECREMENT(&cvar->notify_pending)) {
    PRIntn rv = pthread_cond_destroy(&cvar->cv);
#  if defined(DEBUG)
    PR_ASSERT(0 == rv);
    memset(cvar, 0xaf, sizeof(PRCondVar));
    pt_debug.cvars_destroyed += 1;
#  else
    (void)rv;
#  endif
    PR_Free(cvar);
  }
/* PR_DestroyCondVar */

PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar* cvar, PRIntervalTime timeout) {
  PRIntn rv;
  PRThread* thred = PR_GetCurrentThread();

  PR_ASSERT(cvar != NULL);
  /* We'd better be locked */
  PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex));
  PR_ASSERT(PR_TRUE == cvar->lock->locked);
  /* and it better be by us */
  PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self()));

  if (_PT_THREAD_INTERRUPTED(thred)) {
    goto aborted;
  }

  /*
   * The thread waiting is used for PR_Interrupt
   */

  thred->waiting = cvar; /* this is where we're waiting */

  /*
   * If we have pending notifies, post them now.
   *
   * This is not optimal. We're going to post these notifies
   * while we're holding the lock. That means on MP systems
   * that they are going to collide for the lock that we will
   * hold until we actually wait.
   */

  if (0 != cvar->lock->notified.length) {
    pt_PostNotifies(cvar->lock, PR_FALSE);
  }

  /*
   * We're surrendering the lock, so clear out the locked field.
   */

  cvar->lock->locked = PR_FALSE;

  if (timeout == PR_INTERVAL_NO_TIMEOUT) {
    rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex);
  } else {
    rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout);
  }

  /* We just got the lock back - this better be empty */
  PR_ASSERT(PR_FALSE == cvar->lock->locked);
  cvar->lock->locked = PR_TRUE;
  cvar->lock->owner = pthread_self();

  PR_ASSERT(0 == cvar->lock->notified.length);
  thred->waiting = NULL; /* and now we're not */
  if (_PT_THREAD_INTERRUPTED(thred)) {
    goto aborted;
  }
  if (rv != 0) {
    _PR_MD_MAP_DEFAULT_ERROR(rv);
    return PR_FAILURE;
  }
  return PR_SUCCESS;

aborted:
  PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  thred->state &= ~PT_THREAD_ABORTED;
  return PR_FAILURE;
/* PR_WaitCondVar */

PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar* cvar) {
  PR_ASSERT(cvar != NULL);
  pt_PostNotifyToCvar(cvar, PR_FALSE);
  return PR_SUCCESS;
/* PR_NotifyCondVar */

PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar* cvar) {
  PR_ASSERT(cvar != NULL);
  pt_PostNotifyToCvar(cvar, PR_TRUE);
  return PR_SUCCESS;
/* PR_NotifyAllCondVar */

/**************************************************************/
/**************************************************************/
/***************************MONITORS***************************/
/**************************************************************/
/**************************************************************/

/*
 * Notifies just get posted to the monitor. The actual notification is done
 * when the monitor is fully exited so that MP systems don't contend for a
 * monitor that they can't enter.
 */

static void pt_PostNotifyToMonitor(PRMonitor* mon, PRBool broadcast) {
  PR_ASSERT(NULL != mon);
  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon);

  /* mon->notifyTimes is protected by the monitor, so we don't need to
   * acquire mon->lock.
   */

  if (broadcast) {
    mon->notifyTimes = -1;
  } else if (-1 != mon->notifyTimes) {
    mon->notifyTimes += 1;
  }
/* pt_PostNotifyToMonitor */

static void pt_PostNotifiesFromMonitor(pthread_cond_t* cv, PRIntn times) {
  PRIntn rv;

  /*
   * Time to actually notify any waits that were affected while the monitor
   * was entered.
   */

  PR_ASSERT(NULL != cv);
  PR_ASSERT(0 != times);
  if (-1 == times) {
    rv = pthread_cond_broadcast(cv);
    PR_ASSERT(0 == rv);
  } else {
    while (times-- > 0) {
      rv = pthread_cond_signal(cv);
      PR_ASSERT(0 == rv);
    }
  }
/* pt_PostNotifiesFromMonitor */

PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void) {
  PRMonitor* mon;
  int rv;

  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  mon = PR_NEWZAP(PRMonitor);
  if (mon == NULL) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return NULL;
  }

  rv = _PT_PTHREAD_MUTEX_INIT(mon->lock, _pt_mattr);
  PR_ASSERT(0 == rv);
  if (0 != rv) {
    goto error1;
  }

  _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);

  rv = _PT_PTHREAD_COND_INIT(mon->entryCV, _pt_cvar_attr);
  PR_ASSERT(0 == rv);
  if (0 != rv) {
    goto error2;
  }

  rv = _PT_PTHREAD_COND_INIT(mon->waitCV, _pt_cvar_attr);
  PR_ASSERT(0 == rv);
  if (0 != rv) {
    goto error3;
  }

  mon->notifyTimes = 0;
  mon->entryCount = 0;
  mon->refCount = 1;
  mon->name = NULL;
  return mon;

error3:
  pthread_cond_destroy(&mon->entryCV);
error2:
  pthread_mutex_destroy(&mon->lock);
error1:
  PR_Free(mon);
  _PR_MD_MAP_DEFAULT_ERROR(rv);
  return NULL;
/* PR_NewMonitor */

PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) {
  PRMonitor* mon = PR_NewMonitor();
  if (mon) {
    mon->name = name;
  }
  return mon;
}

PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor* mon) {
  int rv;

  PR_ASSERT(mon != NULL);
  if (PR_ATOMIC_DECREMENT(&mon->refCount) == 0) {
    rv = pthread_cond_destroy(&mon->waitCV);
    PR_ASSERT(0 == rv);
    rv = pthread_cond_destroy(&mon->entryCV);
    PR_ASSERT(0 == rv);
    rv = pthread_mutex_destroy(&mon->lock);
    PR_ASSERT(0 == rv);
#  if defined(DEBUG)
    memset(mon, 0xaf, sizeof(PRMonitor));
#  endif
    PR_Free(mon);
  }
/* PR_DestroyMonitor */

/* The GC uses this; it is quite arguably a bad interface.  I'm just
 * duplicating it for now - XXXMB
 */

PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor* mon) {
  pthread_t self = pthread_self();
  PRIntn rv;
  PRIntn count = 0;

  rv = pthread_mutex_lock(&mon->lock);
  PR_ASSERT(0 == rv);
  if (pthread_equal(mon->owner, self)) {
    count = mon->entryCount;
  }
  rv = pthread_mutex_unlock(&mon->lock);
  PR_ASSERT(0 == rv);
  return count;
}

PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor* mon) {
#  if defined(DEBUG) || defined(FORCE_PR_ASSERT)
  PRIntn rv;

  rv = pthread_mutex_lock(&mon->lock);
  PR_ASSERT(0 == rv);
  PR_ASSERT(mon->entryCount != 0 && pthread_equal(mon->owner, pthread_self()));
  rv = pthread_mutex_unlock(&mon->lock);
  PR_ASSERT(0 == rv);
#  endif
}

PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor* mon) {
  pthread_t self = pthread_self();
  PRIntn rv;

  PR_ASSERT(mon != NULL);
  rv = pthread_mutex_lock(&mon->lock);
  PR_ASSERT(0 == rv);
  if (mon->entryCount != 0) {
    if (pthread_equal(mon->owner, self)) {
      goto done;
    }
    while (mon->entryCount != 0) {
      rv = pthread_cond_wait(&mon->entryCV, &mon->lock);
      PR_ASSERT(0 == rv);
    }
  }
  /* and now I have the monitor */
  PR_ASSERT(0 == mon->notifyTimes);
  PR_ASSERT(_PT_PTHREAD_THR_HANDLE_IS_INVALID(mon->owner));
  _PT_PTHREAD_COPY_THR_HANDLE(self, mon->owner);

done:
  mon->entryCount += 1;
  rv = pthread_mutex_unlock(&mon->lock);
  PR_ASSERT(0 == rv);
/* PR_EnterMonitor */

PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor* mon) {
  pthread_t self = pthread_self();
  PRIntn rv;
  PRBool notifyEntryWaiter = PR_FALSE;
  PRIntn notifyTimes = 0;

  PR_ASSERT(mon != NULL);
  rv = pthread_mutex_lock(&mon->lock);
  PR_ASSERT(0 == rv);
  /* the entries should be > 0 and we'd better be the owner */
  PR_ASSERT(mon->entryCount > 0);
  PR_ASSERT(pthread_equal(mon->owner, self));
  if (mon->entryCount == 0 || !pthread_equal(mon->owner, self)) {
    rv = pthread_mutex_unlock(&mon->lock);
    PR_ASSERT(0 == rv);
    return PR_FAILURE;
  }

  mon->entryCount -= 1; /* reduce by one */
  if (mon->entryCount == 0) {
    /* and if it transitioned to zero - notify an entry waiter */
    /* make the owner unknown */
    _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
    notifyEntryWaiter = PR_TRUE;
    notifyTimes = mon->notifyTimes;
    mon->notifyTimes = 0;
    /* We will access the members of 'mon' after unlocking mon->lock.
     * Add a reference. */

    PR_ATOMIC_INCREMENT(&mon->refCount);
  }
  rv = pthread_mutex_unlock(&mon->lock);
  PR_ASSERT(0 == rv);
  if (notifyEntryWaiter) {
    if (notifyTimes) {
      pt_PostNotifiesFromMonitor(&mon->waitCV, notifyTimes);
    }
    rv = pthread_cond_signal(&mon->entryCV);
    PR_ASSERT(0 == rv);
    /* We are done accessing the members of 'mon'. Release the
     * reference. */

    PR_DestroyMonitor(mon);
  }
  return PR_SUCCESS;
/* PR_ExitMonitor */

PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor* mon, PRIntervalTime timeout) {
  PRStatus rv;
  PRUint32 saved_entries;
  pthread_t saved_owner;

  PR_ASSERT(mon != NULL);
  rv = pthread_mutex_lock(&mon->lock);
  PR_ASSERT(0 == rv);
  /* the entries better be positive */
  PR_ASSERT(mon->entryCount > 0);
  /* and it better be owned by us */
  PR_ASSERT(pthread_equal(mon->owner, pthread_self()));

  /* tuck these away 'till later */
  saved_entries = mon->entryCount;
  mon->entryCount = 0;
  _PT_PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner);
  _PT_PTHREAD_INVALIDATE_THR_HANDLE(mon->owner);
  /*
   * If we have pending notifies, post them now.
   *
   * This is not optimal. We're going to post these notifies
   * while we're holding the lock. That means on MP systems
   * that they are going to collide for the lock that we will
   * hold until we actually wait.
   */

  if (0 != mon->notifyTimes) {
    pt_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
    mon->notifyTimes = 0;
  }
  rv = pthread_cond_signal(&mon->entryCV);
  PR_ASSERT(0 == rv);

  if (timeout == PR_INTERVAL_NO_TIMEOUT) {
    rv = pthread_cond_wait(&mon->waitCV, &mon->lock);
  } else {
    rv = pt_TimedWait(&mon->waitCV, &mon->lock, timeout);
  }
  PR_ASSERT(0 == rv);

  while (mon->entryCount != 0) {
    rv = pthread_cond_wait(&mon->entryCV, &mon->lock);
    PR_ASSERT(0 == rv);
  }
  PR_ASSERT(0 == mon->notifyTimes);
  /* reinstate the interesting information */
  mon->entryCount = saved_entries;
  _PT_PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner);

  rv = pthread_mutex_unlock(&mon->lock);
  PR_ASSERT(0 == rv);
  return rv;
/* PR_Wait */

PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor* mon) {
  pt_PostNotifyToMonitor(mon, PR_FALSE);
  return PR_SUCCESS;
/* PR_Notify */

PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor* mon) {
  pt_PostNotifyToMonitor(mon, PR_TRUE);
  return PR_SUCCESS;
/* PR_NotifyAll */

/**************************************************************/
/**************************************************************/
/**************************SEMAPHORES**************************/
/**************************************************************/
/**************************************************************/
PR_IMPLEMENT(void) PR_PostSem(PRSemaphore* semaphore) {
  static PRBool unwarned = PR_TRUE;
  if (unwarned)
    unwarned = _PR_Obsolete("PR_PostSem""locks & condition variables");
  PR_Lock(semaphore->cvar->lock);
  PR_NotifyCondVar(semaphore->cvar);
  semaphore->count += 1;
  PR_Unlock(semaphore->cvar->lock);
/* PR_PostSem */

PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore* semaphore) {
  PRStatus status = PR_SUCCESS;
  static PRBool unwarned = PR_TRUE;
  if (unwarned)
    unwarned = _PR_Obsolete("PR_WaitSem""locks & condition variables");
  PR_Lock(semaphore->cvar->lock);
  while ((semaphore->count == 0) && (PR_SUCCESS == status)) {
    status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT);
  }
  if (PR_SUCCESS == status) {
    semaphore->count -= 1;
  }
  PR_Unlock(semaphore->cvar->lock);
  return status;
/* PR_WaitSem */

PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore* semaphore) {
  static PRBool unwarned = PR_TRUE;
  if (unwarned)
    unwarned = _PR_Obsolete("PR_DestroySem""locks & condition variables");
  PR_DestroyLock(semaphore->cvar->lock);
  PR_DestroyCondVar(semaphore->cvar);
  PR_Free(semaphore);
/* PR_DestroySem */

PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) {
  PRSemaphore* semaphore;
  static PRBool unwarned = PR_TRUE;
  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  if (unwarned)
    unwarned = _PR_Obsolete("PR_NewSem""locks & condition variables");

  semaphore = PR_NEWZAP(PRSemaphore);
  if (NULL != semaphore) {
    PRLock* lock = PR_NewLock();
    if (NULL != lock) {
      semaphore->cvar = PR_NewCondVar(lock);
      if (NULL != semaphore->cvar) {
        semaphore->count = value;
        return semaphore;
      }
      PR_DestroyLock(lock);
    }
    PR_Free(semaphore);
  }
  return NULL;
}

/*
 * Define the interprocess named semaphore functions.
 * There are three implementations:
 * 1. POSIX semaphore based;
 * 2. System V semaphore based;
 * 3. unsupported (fails with PR_NOT_IMPLEMENTED_ERROR).
 */


#  ifdef _PR_HAVE_POSIX_SEMAPHORES
#    include <fcntl.h>

PR_IMPLEMENT(PRSem*)
PR_OpenSemaphore(const char* name, PRIntn flags, PRIntn mode, PRUintn value) {
  PRSem* sem;
  char osname[PR_IPC_NAME_SIZE];

  if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) ==
      PR_FAILURE) {
    return NULL;
  }

  sem = PR_NEW(PRSem);
  if (NULL == sem) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return NULL;
  }

  if (flags & PR_SEM_CREATE) {
    int oflag = O_CREAT;

    if (flags & PR_SEM_EXCL) {
      oflag |= O_EXCL;
    }
    sem->sem = sem_open(osname, oflag, mode, value);
  } else {
#    ifdef HPUX
    /* Pass 0 as the mode and value arguments to work around a bug. */
    sem->sem = sem_open(osname, 0, 0, 0);
#    else
    sem->sem = sem_open(osname, 0);
#    endif
  }
  if ((sem_t*)-1 == sem->sem) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    PR_Free(sem);
    return NULL;
  }
  return sem;
}

PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem* sem) {
  int rv;
  rv = sem_wait(sem->sem);
  if (0 != rv) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem* sem) {
  int rv;
  rv = sem_post(sem->sem);
  if (0 != rv) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem* sem) {
  int rv;
  rv = sem_close(sem->sem);
  if (0 != rv) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  PR_Free(sem);
  return PR_SUCCESS;
}

PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char* name) {
  int rv;
  char osname[PR_IPC_NAME_SIZE];

  if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) ==
      PR_FAILURE) {
    return PR_FAILURE;
  }
  rv = sem_unlink(osname);
  if (0 != rv) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

#  elif defined(_PR_HAVE_SYSV_SEMAPHORES)

#    include <fcntl.h>
#    include <sys/sem.h>

/*
 * From the semctl(2) man page in glibc 2.0
 */

#    if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)) || \
        (defined(FREEBSD) && __FreeBSD_version < 1200059) ||            \
        defined(OPENBSD) || defined(DARWIN)
/* union semun is defined by including <sys/sem.h> */
#    else
/* according to X/OPEN we have to define it ourselves */
union semun {
  int val;
  struct semid_ds* buf;
  unsigned short* array;
};
#    endif

/*
 * 'a' (97) is the final closing price of NSCP stock.
 */

#    define NSPR_IPC_KEY_ID 'a' /* the id argument for ftok() */

#    define NSPR_SEM_MODE 0666

PR_IMPLEMENT(PRSem*)
PR_OpenSemaphore(const char* name, PRIntn flags, PRIntn mode, PRUintn value) {
  PRSem* sem;
  key_t key;
  union semun arg;
  struct sembuf sop;
  struct semid_ds seminfo;
#    define MAX_TRIES 60
  PRIntn i;
  char osname[PR_IPC_NAME_SIZE];

  if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) ==
      PR_FAILURE) {
    return NULL;
  }

  /* Make sure the file exists before calling ftok. */
  if (flags & PR_SEM_CREATE) {
    int osfd = open(osname, O_RDWR | O_CREAT, mode);
    if (-1 == osfd) {
      _PR_MD_MAP_OPEN_ERROR(errno);
      return NULL;
    }
    if (close(osfd) == -1) {
      _PR_MD_MAP_CLOSE_ERROR(errno);
      return NULL;
    }
  }
  key = ftok(osname, NSPR_IPC_KEY_ID);
  if ((key_t)-1 == key) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return NULL;
  }

  sem = PR_NEW(PRSem);
  if (NULL == sem) {
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    return NULL;
  }

  if (flags & PR_SEM_CREATE) {
    sem->semid = semget(key, 1, mode | IPC_CREAT | IPC_EXCL);
    if (sem->semid >= 0) {
      /* creator of a semaphore is responsible for initializing it */
      arg.val = 0;
      if (semctl(sem->semid, 0, SETVAL, arg) == -1) {
        _PR_MD_MAP_DEFAULT_ERROR(errno);
        PR_Free(sem);
        return NULL;
      }
      /* call semop to set sem_otime to nonzero */
      sop.sem_num = 0;
      sop.sem_op = value;
      sop.sem_flg = 0;
      if (semop(sem->semid, &sop, 1) == -1) {
        _PR_MD_MAP_DEFAULT_ERROR(errno);
        PR_Free(sem);
        return NULL;
      }
      return sem;
    }

    if (errno != EEXIST || flags & PR_SEM_EXCL) {
      _PR_MD_MAP_DEFAULT_ERROR(errno);
      PR_Free(sem);
      return NULL;
    }
  }

  sem->semid = semget(key, 1, NSPR_SEM_MODE);
  if (sem->semid == -1) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    PR_Free(sem);
    return NULL;
  }
  for (i = 0; i < MAX_TRIES; i++) {
    arg.buf = &seminfo;
    semctl(sem->semid, 0, IPC_STAT, arg);
    if (seminfo.sem_otime != 0) {
      break;
    }
    sleep(1);
  }
  if (i == MAX_TRIES) {
    PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
    PR_Free(sem);
    return NULL;
  }
  return sem;
}

PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem* sem) {
  struct sembuf sop;

  sop.sem_num = 0;
  sop.sem_op = -1;
  sop.sem_flg = 0;
  if (semop(sem->semid, &sop, 1) == -1) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem* sem) {
  struct sembuf sop;

  sop.sem_num = 0;
  sop.sem_op = 1;
  sop.sem_flg = 0;
  if (semop(sem->semid, &sop, 1) == -1) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem* sem) {
  PR_Free(sem);
  return PR_SUCCESS;
}

PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char* name) {
  key_t key;
  int semid;
  /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */
  union semun unused;
  char osname[PR_IPC_NAME_SIZE];

  if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) ==
      PR_FAILURE) {
    return PR_FAILURE;
  }
  key = ftok(osname, NSPR_IPC_KEY_ID);
  if ((key_t)-1 == key) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  if (unlink(osname) == -1) {
    _PR_MD_MAP_UNLINK_ERROR(errno);
    return PR_FAILURE;
  }
  semid = semget(key, 1, NSPR_SEM_MODE);
  if (-1 == semid) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  unused.val = 0;
  if (semctl(semid, 0, IPC_RMID, unused) == -1) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

#  else /* neither POSIX nor System V semaphores are available */

PR_IMPLEMENT(PRSem*)
PR_OpenSemaphore(const char* name, PRIntn flags, PRIntn mode, PRUintn value) {
  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  return NULL;
}

PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem* sem) {
  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  return PR_FAILURE;
}

PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem* sem) {
  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  return PR_FAILURE;
}

PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem* sem) {
  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  return PR_FAILURE;
}

PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char* name) {
  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  return PR_FAILURE;
}

#  endif /* end of interprocess named semaphore functions */

/**************************************************************/
/**************************************************************/
/******************ROUTINES FOR DCE EMULATION******************/
/**************************************************************/
/**************************************************************/

#  include "prpdce.h"

PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock* lock) {
  PRIntn rv = pthread_mutex_trylock(&lock->mutex);
  if (rv == 0) {
    PR_ASSERT(PR_FALSE == lock->locked);
    lock->locked = PR_TRUE;
    lock->owner = pthread_self();
  }
  /* XXX set error code? */
  return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
/* PRP_TryLock */

PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void) {
  PRCondVar* cv;

  if (!_pr_initialized) {
    _PR_ImplicitInitialization();
  }

  cv = PR_NEW(PRCondVar);
  if (cv != NULL) {
    int rv;
    rv = _PT_PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr);
    PR_ASSERT(0 == rv);
    if (0 == rv) {
      cv->lock = _PR_NAKED_CV_LOCK;
    } else {
      PR_DELETE(cv);
      cv = NULL;
    }
  }
  return cv;
/* PRP_NewNakedCondVar */

PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar* cvar) {
  int rv;
  rv = pthread_cond_destroy(&cvar->cv);
  PR_ASSERT(0 == rv);
#  if defined(DEBUG)
  memset(cvar, 0xaf, sizeof(PRCondVar));
#  endif
  PR_Free(cvar);
/* PRP_DestroyNakedCondVar */

PR_IMPLEMENT(PRStatus)
PRP_NakedWait(PRCondVar* cvar, PRLock* ml, PRIntervalTime timeout) {
  PRIntn rv;
  PR_ASSERT(cvar != NULL);
  /* XXX do we really want to assert this in a naked wait? */
  PR_ASSERT(_PT_PTHREAD_MUTEX_IS_LOCKED(ml->mutex));
  if (timeout == PR_INTERVAL_NO_TIMEOUT) {
    rv = pthread_cond_wait(&cvar->cv, &ml->mutex);
  } else {
    rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout);
  }
  if (rv != 0) {
    _PR_MD_MAP_DEFAULT_ERROR(rv);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
/* PRP_NakedWait */

PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar* cvar) {
  int rv;
  PR_ASSERT(cvar != NULL);
  rv = pthread_cond_signal(&cvar->cv);
  PR_ASSERT(0 == rv);
  return PR_SUCCESS;
/* PRP_NakedNotify */

PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar* cvar) {
  int rv;
  PR_ASSERT(cvar != NULL);
  rv = pthread_cond_broadcast(&cvar->cv);
  PR_ASSERT(0 == rv);
  return PR_SUCCESS;
/* PRP_NakedBroadcast */

#endif /* defined(_PR_PTHREADS) */

/* ptsynch.c */

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.4 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.