/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// handleobject - give an object a process-global unique handle
//
#include <security_cdsa_utilities/handleobject.h>


//
// Static members of HandleObject
//
ModuleNexus<HandleObject::State> HandleObject::state;


//
// Bring the State constructor out of line
//
HandleObject::State::State()
{ }


//
// HandleObject destructor (virtual)
//
HandleObject::~HandleObject()
{
	State &st = state();
	StLock<Mutex> _(st);
	st.erase(this);
}


//
// Assign a HandleObject's (new) Handle.
//
void HandleObject::State::make(HandleObject *obj)
{
    StLock<Mutex> _(*this);
	for (;;) {
		Handle handle = reinterpret_cast<uint32>(obj) ^ (++sequence << 19);
		if (handleMap[handle] == NULL) {
			secdebug("handleobj", "create 0x%lx for %p", handle, obj);
			obj->setHandle(handle);
			handleMap[handle] = obj;
			return;
		}
	}
}


//
// Clean up a HandleObject that dies.
// Note that an object MAY clear its handle before (in which case we do nothing).
// In particular, killHandle will do this.
//
void HandleObject::State::erase(HandleObject *obj)
{
    if (obj->validHandle())
        handleMap.erase(obj->handle());
}

void HandleObject::State::erase(HandleMap::iterator &it)
{
    if (it->second->validHandle())
        handleMap.erase(it);
}


//
// Observing proper map locking, locate a handle in the global handle map
// and return a pointer to its object. Throw CssmError(error) if it cannot
// be found, or it is corrupt.
//
HandleObject *HandleObject::State::find(CSSM_HANDLE h, CSSM_RETURN error)
{
	StLock<Mutex> _(*this);
	HandleMap::const_iterator it = handleMap.find(h);
	if (it == handleMap.end())
		CssmError::throwMe(error);
	HandleObject *obj = it->second;
	if (obj == NULL || obj->handle() != h)
		CssmError::throwMe(error);
	return obj;
}


//
// Look up the handle given in the global handle map.
// If not found, or if the object is corrupt, throw an exception.
// Otherwise, hold the State lock and return an iterator to the map entry.
// Caller must release the State lock in a timely manner.
//
HandleObject::HandleMap::iterator HandleObject::State::locate(CSSM_HANDLE h, CSSM_RETURN error)
{
	StLock<Mutex> locker(*this);
	HandleMap::iterator it = handleMap.find(h);
	if (it == handleMap.end())
		CssmError::throwMe(error);
	HandleObject *obj = it->second;
	if (obj == NULL || obj->handle() != h)
		CssmError::throwMe(error);
	locker.release();
	return it;
}


//
// The default locking virtual methods do nothing and succeed.
//
void HandleObject::lock() { }

bool HandleObject::tryLock() { return true; }
