globalizer.cpp   [plain text]


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


//
// globalizer - multiscope globalization services.
//
// This is a tentative, partial implementation.
// Status:
//	module scope: constructs, optional cleanup
//	thread scope: constructs, optional cleanup
//	process scope: not implemented (obsolete implementation, unused)
//	system scope: not implemented (probably never will)
//
// @@@ Assumption: {bool,T*} atomic unless PTHREAD_STRICT
//
#include <Security/globalizer.h>
#include <Security/debugging.h>
#include <cstdlib>


//
// The Error class thrown if Nexus operations fail
//
GlobalNexus::Error::~Error() throw()
{
}


//
// The long (and possibly contentious) path of ModuleNexus()
//
// Briefly, the trick here is to go through a three-stage sequence
// to lazily construct a unique singleton object, no matter how many
// threads all of a sudden decide they need it.
// State sequence:
// State 0: pointer == 0, not initialized, idle
// State 1: pointer == mutexp | 0x1, where mutexp points to a Mutex
//  used to serialize construction of the singleton object
// State 2: pointer == &singleton, and we're done
//
// TAKE NOTE:
// This code is optimized with a particular issue in mind: when placed
// into static storage (as ModuleNexi are wont to), it should not require
// dynamic initialization. This is important because our code is, in effect,
// linked into just about every program in the system. The price we pay
// for this coolness is
//  (a) This won't work *except* in static storage (not on stack or heap)
//  (b) We slightly fracture portability (see below)
// This has been considered Worth It, at least for now. Before you throw
// up and throw this code out, please try to figure out whether you know
// the Whole Story. Thank you.
//
// WARNING:
// This code makes the following non-portable assumptions:
//  (a) NULL == 0 (binary representation of NULL pointer is zero value)
//	(b) Pointers acquired from new have at least their LSB zero (are at
//      least two-byte aligned).
// It seems like it's been a while since anyone made a machine/runtime that
// violated either of those. But you have been warned.
//
#if defined(_HAVE_ATOMIC_OPERATIONS)

AtomicWord ModuleNexusCommon::create(void *(*make)())
{
    sync++;		// keep mutex alive if needed
  retry:
    AtomicWord initialPointer = pointer;	// latch pointer
    if (!initialPointer || (initialPointer & 0x1)) {
        Mutex *mutex;
        if (initialPointer == 0) {
            mutex = new Mutex(false);	// don't bother debugging this one
            mutex->lock();
            if (atomicStore(pointer, AtomicWord(mutex) | 0x1, 0) != 0) {
                // somebody beat us to the lead - back off
                mutex->unlock();
                delete mutex;
                goto retry;
            }
            // we have the ball
            try {
                void *singleton = make();
                pointer = AtomicWord(singleton);
                // we need a write barrier here, but the mutex->unlock below provides it for free
            } catch (...) {
				secdebug("nexus", "ModuleNexus %p construction failed", this);
                mutex->unlock();
                if (--sync == 0) {
                    delete mutex;
                    pointer = 0;
                }
                throw;
            }
        } else {
            mutex = reinterpret_cast<Mutex *>(initialPointer & ~0x1);
            mutex->lock();	// we'll wait here
        }
        mutex->unlock();
        //@@@ retry if not resolved -- or fail here (with "object can't be built")
        if (--sync == 0)
            delete mutex;
    }
    return pointer;
}

#endif //_HAVE_ATOMIC_OPERATIONS


//
// Process nexus operation
//
ProcessNexusBase::ProcessNexusBase(const char *identifier)
{
	const char *env = getenv(identifier);
	if (env == NULL) {	// perhaps we're first...
		auto_ptr<Store> store(new Store);
		char form[2*sizeof(Store *) + 2];
		sprintf(form, "*%p", &store);
		setenv(identifier, form, 0);	// do NOT overwrite...
		env = getenv(identifier);		// ... and refetch to resolve races
		if (sscanf(env, "*%p", &mStore) != 1)
			CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR /*"environment communication failed" */);
		if (mStore == store.get())		// we won the race...
			store.release();			// ... so keep the store
	} else
		if (sscanf(env, "*%p", &mStore) != 1)
			CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR /*"environment communication failed"*/);
}