/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
/* Header file */
#ifdef VX
#include <taskLib.h>
#endif
#include "js-context.h"
#include "js-scriptruntime.h"
#ifdef FASTER
#include "js-runtimep.h"
#else
#include "js-runtime.h"
#endif

/* Class data */
JSRuntime *JSRuntime::keepedRuntime;

/* Procedures */
void JSRuntime::enter()
{
#ifdef THREADS
	enterCount += 1;
#endif
}

void JSRuntime::exit()
{
#ifdef THREADS
	enterCount -= 1;
#ifdef VX
	semGive(condWorld);
#else
	pthread_cond_signal(&condWorld);
#endif
#endif
}

void JSRuntime::lock()
{
#ifdef THREADS
#ifdef VX
	semTake(mutex, WAIT_FOREVER);
	while (holder != (int)NULL) {
		if (holder == taskIdSelf())
			break;
		stopCount += 1;
		semGive(mutex);
		semGive(condWorld);
		semTake(cond, WAIT_FOREVER);
		semTake(mutex, WAIT_FOREVER);
		stopCount -= 1;
	}
	if (holder == (int)NULL) {
		holder = taskIdSelf();
	}
	level += 1;
	semGive(mutex);
#else /* !VX */
	pthread_t self = pthread_self();

	pthread_mutex_lock(&mutex);
	while (holder != NULL) {
		if (holder == self)
			break;
		stopCount += 1;
		pthread_cond_signal(&condWorld);
		pthread_cond_wait(&cond, &mutex);
		stopCount -= 1;
	}
	if (holder == (int)NULL) {
		holder = self;
	}
	level += 1;
	pthread_mutex_unlock(&mutex);
#endif /* VX */
#endif
}

void JSRuntime::unlock()
{
#ifdef THREADS
#ifdef VX
	semTake(mutex, WAIT_FOREVER);
	level -= 1;
	if (level == 0) {
		holder = (int)NULL;
		semGive(cond);
	}
	semGive(mutex);
#else /* !VX */
	pthread_mutex_lock(&mutex);

	level -= 1;
	if (level == 0) {
		holder = NULL;
		pthread_cond_signal(&cond);
	}
	pthread_mutex_unlock(&mutex);
#endif /* VX */
#endif
}

void JSRuntime::addContext(JSContext * cx)
{
	lstAdd(&contexts, cx);
}

void JSRuntime::delContext(JSContext * cx)
{
	lstDelete(&contexts, cx);
}

void JSRuntime::markAll()
{
	JSContext *cx;

	for (NODE * node = lstFirst(&contexts); node != NULL; node = lstNext(node)) {
		cx = (JSContext *) node;

		cx->mark();
	}
	cx = JSContext::getCurrentContext();

	for (int i = 0; i < numRoots; ++i) {
		roots[i]->mark(cx);
	}
	gcHeap.markObj(JS_VAL_TO_OBJECT(NaNobj));
#ifdef FASTER
#ifdef COMPACT_MEM
	cleanupGCString();
#endif
#endif
}

void JSRuntime::stopWorld()
{
#ifdef THREADS
#ifdef VX
	semTake(mutex, WAIT_FOREVER);
	stopCount += 1;
	while (stopCount != enterCount) {
		semGive(mutex);
		semTake(condWorld, WAIT_FOREVER);
		semTake(mutex, WAIT_FOREVER);
	}
	semGive(mutex);
#else /* !VX */
	pthread_mutex_lock(&mutex);
	stopCount += 1;
	while (stopCount < enterCount) {
		pthread_cond_wait(&condWorld, &mutex);
	}
	pthread_mutex_unlock(&mutex);
#endif /* VX */
#endif
}

void JSRuntime::resumeWorld()
{
#ifdef THREADS
	stopCount -= 1;
#endif
}

void JSRuntime::addRoot(JSScriptable * rootObj)
{
	JSScriptable **roots = (JSScriptable **) heap.malloc(sizeof(*roots) * (numRoots + 1));

	if (roots == NULL) return;
	memcpy(roots, this->roots, sizeof(*roots) * numRoots);
	if (this->roots != NULL) {
		heap.free(this->roots);
	}
	roots[numRoots] = rootObj;
	this->roots = roots;
	numRoots += 1;
}

void JSRuntime::removeRoot(JSScriptable * rootObj)
{
	for (int i = 0; i < numRoots; ++i) {
		if (roots[i] == rootObj) {
			memcpy(roots + i, roots + i + 1, sizeof(*roots) * (numRoots - i - 1));
			numRoots -= 1;
			break;
		}
	}
}

#ifdef COMPACT_MEM
jsString_t JSRuntime::hasGCString(jsString_t string)
{
	return gcStringHashtable->get(string);
}

void JSRuntime::setGCString(jsString_t string)
{
	gcStringHashtable->set(string);
}

void JSRuntime::cleanupGCString()
{
	gcStringHashtable->cleanup(&gcHeap);
}
#endif /* COMPACT_MEM */

void JSRuntime::cleanup()
{
	numRoots = 0;
#ifdef FASTER
#ifdef COMPACT_MEM
	gcStringHashtable->cleanup(&gcHeap);
#endif
#endif
}

JSRuntime::JSRuntime(int maxBytes)
:gcHeap(this, maxBytes), allocator(this)
#ifdef COMPACT_MEM
, _allocator(this)
#endif
{
#ifdef THREADS
#ifdef VX
	mutex = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
	cond = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
	condWorld = semBCreate(SEM_Q_PRIORITY, SEM_EMPTY);
	holder = (int)NULL;
#else /* !VX */
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);
	pthread_cond_init(&condWorld, NULL);
	holder = NULL;
#endif /* VX */
	level = 0;
#endif
	lstInit(&contexts);
#ifdef THREADS
	enterCount = 0;
	stopCount = 0;
#endif
	numRoots = 0;
	roots = NULL;

	double *d = (double *)gcHeap.JSGCMemory::malloc(sizeof(*d),
			JSGCMemory::PTRFREE);

	*d = JSScriptRuntime::NaN;
	NaNobj = JS_DOUBLE_TO_VAL(d);
#ifdef COMPACT_MEM
	gcStringHashtable = new(this) JSGCStringHashtable(&_allocator);
#endif
#ifdef STATS
	keepedRuntime = this;
#endif
}

JSRuntime::~JSRuntime()
{
	if (this->roots != NULL) {
		heap.free(this->roots);
	}
#ifdef COMPACT_MEM
	gcStringHashtable->destroy(this);
#endif
}
