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

Quelle  w95cv.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/. */


/*
 *  w95cv.c -- Windows 95 Machine-Dependent Code for Condition Variables
 *
 *  We implement our own condition variable wait queue.  Each thread
 *  has a semaphore object (thread->md.blocked_sema) to block on while
 *  waiting on a condition variable.
 *
 *  We use a deferred condition notify algorithm.  When PR_NotifyCondVar
 *  or PR_NotifyAllCondVar is called, the condition notifies are simply
 *  recorded in the _MDLock structure.  We defer the condition notifies
 *  until right after we unlock the lock.  This way the awakened threads
 *  have a better chance to reaquire the lock.
 */


#include "primpl.h"

/*
 * AddThreadToCVWaitQueueInternal --
 *
 * Add the thread to the end of the condition variable's wait queue.
 * The CV's lock must be locked when this function is called.
 */


static void AddThreadToCVWaitQueueInternal(PRThread* thred,
                                           struct _MDCVar* cv) {
  PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) ||
            (cv->waitTail == NULL && cv->waitHead == NULL));
  cv->nwait += 1;
  thred->md.inCVWaitQueue = PR_TRUE;
  thred->md.next = NULL;
  thred->md.prev = cv->waitTail;
  if (cv->waitHead == NULL) {
    cv->waitHead = thred;
  } else {
    cv->waitTail->md.next = thred;
  }
  cv->waitTail = thred;
}

/*
 * md_UnlockAndPostNotifies --
 *
 * Unlock the lock, and then do the deferred condition notifies.
 * If waitThred and waitCV are not NULL, waitThred is added to
 * the wait queue of waitCV before the lock is unlocked.
 *
 * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
 * the two places where a lock is unlocked.
 */

static void md_UnlockAndPostNotifies(_MDLock* lock, PRThread* waitThred,
                                     _MDCVar* waitCV) {
  PRIntn index;
  _MDNotified post;
  _MDNotified *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)
  ZeroMemory(&lock->notified, sizeof(_MDNotified)); /* reset */
#else
  lock->notified.length = 0; /* these are really sufficient */
  lock->notified.link = NULL;
#endif

  /*
   * Figure out how many threads we need to wake up.
   */

  notified = &post; /* this is where we start */
  do {
    for (index = 0; index < notified->length; ++index) {
      _MDCVar* cv = notified->cv[index].cv;
      PRThread* thred;
      int i;

      /* Fast special case: no waiting threads */
      if (cv->waitHead == NULL) {
        notified->cv[index].notifyHead = NULL;
        continue;
      }

      /* General case */
      if (-1 == notified->cv[index].times) {
        /* broadcast */
        thred = cv->waitHead;
        while (thred != NULL) {
          thred->md.inCVWaitQueue = PR_FALSE;
          thred = thred->md.next;
        }
        notified->cv[index].notifyHead = cv->waitHead;
        cv->waitHead = cv->waitTail = NULL;
        cv->nwait = 0;
      } else {
        thred = cv->waitHead;
        i = notified->cv[index].times;
        while (thred != NULL && i > 0) {
          thred->md.inCVWaitQueue = PR_FALSE;
          thred = thred->md.next;
          i--;
        }
        notified->cv[index].notifyHead = cv->waitHead;
        cv->waitHead = thred;
        if (cv->waitHead == NULL) {
          cv->waitTail = NULL;
        } else {
          if (cv->waitHead->md.prev != NULL) {
            cv->waitHead->md.prev->md.next = NULL;
            cv->waitHead->md.prev = NULL;
          }
        }
        cv->nwait -= notified->cv[index].times - i;
      }
    }
    notified = notified->link;
  } while (NULL != notified);

  if (waitThred) {
    AddThreadToCVWaitQueueInternal(waitThred, waitCV);
  }

  /* Release the lock before notifying */
  LeaveCriticalSection(&lock->mutex);

  notified = &post; /* this is where we start */
  do {
    for (index = 0; index < notified->length; ++index) {
      PRThread* thred;
      PRThread* next;

      thred = notified->cv[index].notifyHead;
      while (thred != NULL) {
        BOOL rv;

        next = thred->md.next;
        thred->md.prev = thred->md.next = NULL;

        rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
        PR_ASSERT(rv != 0);
        thred = next;
      }
    }
    prev = notified;
    notified = notified->link;
    if (&post != prev) {
      PR_DELETE(prev);
    }
  } while (NULL != notified);
}

/*
 * 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 md_PostNotifyToCvar(_MDCVar* cvar, _MDLock* lock,
                                PRBool broadcast) {
  PRIntn index = 0;
  _MDNotified* notified = &lock->notified;

  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;
      }
    }
    /* if not full, enter new CV in this array */
    if (notified->length < _MD_CV_NOTIFIED_LENGTH) {
      break;
    }

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

    notified = notified->link;
  }

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

/*
 * _PR_MD_NEW_CV() -- Creating new condition variable
 * ... Solaris uses cond_init() in similar function.
 *
 * returns: -1 on failure
 *          0 when it succeeds.
 *
 */

PRInt32 _PR_MD_NEW_CV(_MDCVar* cv) {
  cv->magic = _MD_MAGIC_CV;
  /*
   * The waitHead, waitTail, and nwait fields are zeroed
   * when the PRCondVar structure is created.
   */

  return 0;
}

void _PR_MD_FREE_CV(_MDCVar* cv) {
  cv->magic = (PRUint32)-1;
  return;
}

/*
 *  _PR_MD_WAIT_CV() -- Wait on condition variable
 */

void _PR_MD_WAIT_CV(_MDCVar* cv, _MDLock* lock, PRIntervalTime timeout) {
  PRThread* thred = _PR_MD_CURRENT_THREAD();
  DWORD rv;
  DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT)
                    ? INFINITE
                    : PR_IntervalToMilliseconds(timeout);

  /*
   * If we have pending notifies, post them now.
   */

  if (0 != lock->notified.length) {
    md_UnlockAndPostNotifies(lock, thred, cv);
  } else {
    AddThreadToCVWaitQueueInternal(thred, cv);
    LeaveCriticalSection(&lock->mutex);
  }

  /* Wait for notification or timeout; don't really care which */
  rv = WaitForSingleObject(thred->md.blocked_sema, msecs);

  EnterCriticalSection(&(lock->mutex));

  PR_ASSERT(rv != WAIT_ABANDONED);
  PR_ASSERT(rv != WAIT_FAILED);
  PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);

  if (rv == WAIT_TIMEOUT) {
    if (thred->md.inCVWaitQueue) {
      PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) ||
                (cv->waitTail == NULL && cv->waitHead == NULL));
      cv->nwait -= 1;
      thred->md.inCVWaitQueue = PR_FALSE;
      if (cv->waitHead == thred) {
        cv->waitHead = thred->md.next;
        if (cv->waitHead == NULL) {
          cv->waitTail = NULL;
        } else {
          cv->waitHead->md.prev = NULL;
        }
      } else {
        PR_ASSERT(thred->md.prev != NULL);
        thred->md.prev->md.next = thred->md.next;
        if (thred->md.next != NULL) {
          thred->md.next->md.prev = thred->md.prev;
        } else {
          PR_ASSERT(cv->waitTail == thred);
          cv->waitTail = thred->md.prev;
        }
      }
      thred->md.next = thred->md.prev = NULL;
    } else {
      /*
       * This thread must have been notified, but the
       * ReleaseSemaphore call happens after WaitForSingleObject
       * times out.  Wait on the semaphore again to make it
       * non-signaled.  We assume this wait won't take long.
       */

      rv = WaitForSingleObject(thred->md.blocked_sema, INFINITE);
      PR_ASSERT(rv == WAIT_OBJECT_0);
    }
  }
  PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
  return;
/* --- end _PR_MD_WAIT_CV() --- */

void _PR_MD_NOTIFY_CV(_MDCVar* cv, _MDLock* lock) {
  md_PostNotifyToCvar(cv, lock, PR_FALSE);
  return;
}

void _PR_MD_NOTIFYALL_CV(_MDCVar* cv, _MDLock* lock) {
  md_PostNotifyToCvar(cv, lock, PR_TRUE);
  return;
}

typedef BOOL(WINAPI* INITIALIZECRITICALSECTIONEX)(
    CRITICAL_SECTION* lpCriticalSection, DWORD dwSpinCount, DWORD Flags);

static INITIALIZECRITICALSECTIONEX sInitializeCriticalSectionEx;

void _PR_MD_INIT_LOCKS(void) {
  /*
   * Starting with Windows Vista, every CRITICAL_SECTION allocates an extra
   * RTL_CRITICAL_SECTION_DEBUG object. Unfortunately, this debug object is
   * not reclaimed by DeleteCriticalSection(), causing an apparent memory
   * leak. This is a debugging "feature", not a bug. If we are running on
   * Vista or later, use InitializeCriticalSectionEx() to allocate
   * CRITICAL_SECTIONs without debug objects.
   */

  HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
  PR_ASSERT(hKernel32);
  PR_ASSERT(!sInitializeCriticalSectionEx);
  sInitializeCriticalSectionEx = (INITIALIZECRITICALSECTIONEX)GetProcAddress(
      hKernel32, "InitializeCriticalSectionEx");
}

/*
 * By default, CRITICAL_SECTIONs are initialized with a spin count of 0.
 * Joe Duffy's "Concurrent Programming on Windows" book suggests 1500 is
 * a "reasonable starting point". On single-processor systems, the spin
 * count is ignored and the critical section spin count is set to 0.
 */

#define LOCK_SPIN_COUNT 1500

PRStatus _PR_MD_NEW_LOCK(_MDLock* lock) {
  CRITICAL_SECTION* cs = &lock->mutex;
  BOOL ok;

  if (sInitializeCriticalSectionEx) {
    ok = sInitializeCriticalSectionEx(cs, LOCK_SPIN_COUNT,
                                      CRITICAL_SECTION_NO_DEBUG_INFO);
  } else {
    ok = InitializeCriticalSectionAndSpinCount(cs, LOCK_SPIN_COUNT);
  }
  if (!ok) {
    _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    return PR_FAILURE;
  }

  lock->notified.length = 0;
  lock->notified.link = NULL;
  return PR_SUCCESS;
}

void _PR_MD_UNLOCK(_MDLock* lock) {
  if (0 != lock->notified.length) {
    md_UnlockAndPostNotifies(lock, NULL, NULL);
  } else {
    LeaveCriticalSection(&lock->mutex);
  }
}

Messung V0.5
C=96 H=84 G=90

¤ Dauer der Verarbeitung: 0.12 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.