+++ /dev/null
-/*
- * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
- *
- * The contents of this file constitute Original Code as defined in and are
- * subject to the Apple Public Source License Version 1.2 (the 'License').
- * You may not use this file except in compliance with the License. Please obtain
- * a copy of the License at http://www.apple.com/publicsource and read it before
- * using this file.
- *
- * This Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
- * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
- * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
- * specific language governing rights and limitations under the License.
- */
-
-
-//
-// threading - generic thread support
-//
-
-
-//
-// Since we are planning to generate "stub" out of line code for threading methods,
-// we must force THREAD_NDEBUG to off while compiling our header. Trust me.
-//
-#include <Security/threading.h>
-#include <Security/globalizer.h>
-#include <Security/memutils.h>
-
-
-//
-// Thread-local storage primitive
-//
-#if _USE_THREADS == _USE_PTHREADS
-
-ThreadStoreSlot::ThreadStoreSlot(Destructor *destructor)
-{
- if (int err = pthread_key_create(&mKey, destructor))
- UnixError::throwMe(err);
-}
-
-ThreadStoreSlot::~ThreadStoreSlot()
-{
- //@@@ if we wanted to dispose of pending task objects, we'd have
- //@@@ to keep a set of them and delete them explicitly here
- pthread_key_delete(mKey);
-}
-
-#endif
-
-
-//
-// Mutex implementation
-//
-#if _USE_THREADS == _USE_PTHREADS
-
-bool Mutex::debugHasInitialized;
-bool Mutex::loggingMutexi;
-
-inline void Mutex::init(Type type, bool log)
-{
-#if !defined(THREAD_NDEBUG)
- // this debug-setup code isn't interlocked, but it's idempotent
- // (don't worry, be happy)
- if (!debugHasInitialized) {
- loggingMutexi = Debug::debugging("mutex") || Debug::debugging("mutex-c");
- debugHasInitialized = true;
- }
- debugLog = log && loggingMutexi;
- useCount = contentionCount = 0;
-#else
- debugLog = false;
-#endif //THREAD_NDEBUG
-}
-
-struct Recursive : public pthread_mutexattr_t {
- Recursive()
- {
- pthread_mutexattr_init(this);
- pthread_mutexattr_settype(this, PTHREAD_MUTEX_RECURSIVE);
- }
-};
-
-
-Mutex::Mutex(bool log)
-{
- init(normal, log);
- check(pthread_mutex_init(&me, NULL));
-}
-
-Mutex::Mutex(Type type, bool log)
-{
- init(type, log);
- switch (type) {
- case normal:
- check(pthread_mutex_init(&me, NULL));
- break;
- case recursive:
- static ModuleNexus<Recursive> recursive;
- check(pthread_mutex_init(&me, &recursive()));
- };
- }
-
-Mutex::~Mutex()
-{
-#if !defined(THREAD_NDEBUG)
- if (debugLog) {
- if (contentionCount > 0)
- secdebug("mutex-c", "%p destroyed after %ld/%ld locks/contentions",
- this, useCount, contentionCount);
- else if (useCount > 100)
- secdebug("mutex", "%p destroyed after %ld locks", this, useCount);
- }
-#endif //THREAD_NDEBUG
- check(pthread_mutex_destroy(&me));
-}
-
-void Mutex::lock()
-{
-#if !defined(THREAD_NDEBUG)
- useCount++;
- if (debugLog) {
- switch (int err = pthread_mutex_trylock(&me)) {
- case 0:
- break;
- case EBUSY:
- if (debugLog)
- secdebug("mutex-c", "%p contended (%ld of %ld)", this, ++contentionCount, useCount);
- check(pthread_mutex_lock(&me));
- break;
- default:
- UnixError::throwMe(err);
- }
- if (useCount % 100 == 0)
- secdebug("mutex", "%p locked %ld", this, useCount);
- else
- secdebug("mutex", "%p locked", this);
- return;
- }
-#endif //THREAD_NDEBUG
- check(pthread_mutex_lock(&me));
-}
-
-bool Mutex::tryLock()
-{
- useCount++;
- if (int err = pthread_mutex_trylock(&me)) {
- if (err != EBUSY)
- UnixError::throwMe(err);
-#if !defined(THREAD_NDEBUG)
- if (debugLog)
- secdebug("mutex-c", "%p trylock contended (%ld of %ld)",
- this, ++contentionCount, useCount);
-#endif //THREAD_NDEBUG
- return false;
- }
-#if !defined(THREAD_NDEBUG)
- if (debugLog)
- if (useCount % 100 == 0)
- secdebug("mutex", "%p locked %ld", this, useCount);
- else
- secdebug("mutex", "%p locked", this);
-#endif //THREAD_NDEBUG
- return true;
-}
-
-void Mutex::unlock()
-{
-#if !defined(MUTEX_NDEBUG)
- if (debugLog)
- secdebug("mutex", "%p unlocked", this);
-#endif //MUTEX_NDEBUG
- check(pthread_mutex_unlock(&me));
-}
-
-#endif //PTHREADS
-
-
-//
-// CountingMutex implementation.
-// Note that this is a generic implementation based on a specific Mutex type.
-// In other words, it should work no matter how Mutex is implemented.
-// Also note that CountingMutex is expected to interlock properly with Mutex,
-// so you canNOT just use an AtomicCounter here.
-//
-void CountingMutex::enter()
-{
- lock();
- mCount++;
- secdebug("mutex", "%p up to %d", this, mCount);
- unlock();
-}
-
-bool CountingMutex::tryEnter()
-{
- if (!tryLock())
- return false;
- mCount++;
- secdebug("mutex", "%p up to %d (was try)", this, mCount);
- unlock();
- return true;
-}
-
-void CountingMutex::exit()
-{
- lock();
- assert(mCount > 0);
- mCount--;
- secdebug("mutex", "%p down to %d", this, mCount);
- unlock();
-}
-
-void CountingMutex::finishEnter()
-{
- mCount++;
- secdebug("mutex", "%p finish up to %d", this, mCount);
- unlock();
-}
-
-void CountingMutex::finishExit()
-{
- assert(mCount > 0);
- mCount--;
- secdebug("mutex", "%p finish down to %d", this, mCount);
- unlock();
-}
-
-
-
-//
-// Threads implementation
-//
-#if _USE_THREADS == _USE_PTHREADS
-
-Thread::~Thread()
-{
-}
-
-void Thread::run()
-{
- if (int err = pthread_create(&self.mIdent, NULL, runner, this))
- UnixError::throwMe(err);
- secdebug("thread", "%p created", self.mIdent);
-}
-
-void *Thread::runner(void *arg)
-{
- Thread *me = static_cast<Thread *>(arg);
- if (int err = pthread_detach(me->self.mIdent))
- UnixError::throwMe(err);
- secdebug("thread", "%p starting", me->self.mIdent);
- me->action();
- secdebug("thread", "%p terminating", me->self.mIdent);
- delete me;
- return NULL;
-}
-
-void Thread::yield()
-{
- sched_yield();
-}
-
-
-//
-// Make a more-or-less unique string representation of a thread id.
-// This is meant FOR DEBUGGING ONLY. Don't use this in production code.
-//
-void Thread::Identity::getIdString(char id[idLength])
-{
- pthread_t current = pthread_self();
- // We're not supposed to know what a pthread_t is. Just print the first few bytes...
- // (On MacOS X, it's a pointer to a pthread_t internal structure, so this works fine.)
- long ids;
- memcpy(&ids, ¤t, sizeof(ids));
- snprintf(id, idLength, "%lx", ids);
-}
-
-
-#endif // PTHREADS
-
-
-//
-// ThreadRunner implementation
-//
-ThreadRunner::ThreadRunner(Action *todo)
-{
- mAction = todo;
- run();
-}
-
-void ThreadRunner::action()
-{
- mAction();
-}
-
-
-//
-// Nesting Mutexi.
-// This implementation uses mWait as a "sloppy" wait blocker (only).
-// It should be a semaphore of course, but we don't have a semaphore
-// abstraction right now. The authoritative locking protocol is based on mLock.
-//
-NestingMutex::NestingMutex() : mCount(0)
-{ }
-
-void NestingMutex::lock()
-{
- while (!tryLock()) {
- mWait.lock();
- mWait.unlock();
- }
-}
-
-bool NestingMutex::tryLock()
-{
- StLock<Mutex> _(mLock);
- if (mCount == 0) { // initial lock
- mCount = 1;
- mIdent = Thread::Identity::current();
- mWait.lock();
- return true;
- } else if (mIdent == Thread::Identity::current()) { // recursive lock
- mCount++;
- return true;
- } else { // locked by another thread
- return false;
- }
-}
-
-void NestingMutex::unlock()
-{
- StLock<Mutex> _(mLock);
- assert(mCount > 0 && mIdent == Thread::Identity::current());
- if (--mCount == 0) // last recursive unlock
- mWait.unlock();
-}