/*
 * 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();
    }
    for (int i = 0; i < numRoots; ++i) {
	roots[i]->mark(cx);
    }
    gcHeap.markObj(JS_VAL_TO_OBJECT(NaNobj));
#ifdef FASTER
#ifdef COMPACT_MEM
    organizeGCString();
#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));

    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) {
	    /* AR230188 */
	    memmove(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::organizeGCString()
{
    gcStringHashtable->organize(&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(NULL, 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
}

