blob: 8bbb8d560bf5ce3cdd4904bf94c83bfdf0f1fd05 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @ingroup Reference
* @brief Reference implementation of VM-local storage.
*
* @detail VM-local storage (VMLS) is similar to thread-local storage (TLS) in that it provides
* key-value storage slots that can hold arbitrary data. While TLS provides per-thread
* slots associated with a fixed key, VMLS provides per-JavaVM space associated with a fixed key.
*
* VMLS is used as replacement for static variables which would otherwise prevent multiple
* JavaVM instances from co-existing in the same process. Instead of storing values (such as
* JNI method/field ID's) in static variables -- allocate a VMLS key and use the VMI functions
* to get/set it's value on a per-vm basis.
*/
#define USING_VMI
#include "jni.h" /* for definitions of JavaVM */
#include "hycomp.h" /* for portable types (UDATA,etc...) */
#include "hythread.h" /* for synchronization */
#include "hyport.h" /* for port library */
#include "vmi.h" /* for VMI functions */
#include <string.h> /* for memset */
/* A constant that bounds the number of keys available. */
#define VMLS_MAX_KEYS 256
/**
* A structure to hold per-vm data.
*/
typedef struct VMLSContainer {
JavaVM* javaVM;
void** vmLocalStorage[VMLS_MAX_KEYS];
struct VMLSContainer* linkNext;
} VMLSContainer;
/**
* A structure used to hold process-level VMLS information.
*/
typedef struct HyVMLSTable {
UDATA initialized;
UDATA head;
UDATA freeKeys;
UDATA keys[VMLS_MAX_KEYS];
VMLSContainer* containerHead;
} HyVMLSTable;
/* An instance of the VMLS data shared by all JavaVMs */
static HyVMLSTable VMLSTable;
/**
* Destroy one or more slots of VM local storage.
*
* @code void JNICALL HyVMLSFreeKeys(JNIEnv * env, UDATA * pInitCount, ...); @endcode
*
* @param[in] env A JNIEnv pointer
* @param[in] pInitCount Pointer to the reference count for these slots
* @param[out] ... Pointers to the allocated keys
*/
void JNICALL HyVMLSFreeKeys(JNIEnv * env, UDATA * pInitCount, ...)
{
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_ENV(env);
#endif /* HY_NO_THR */
va_list args;
HyVMLSTable* vmls = GLOBAL_DATA(VMLSTable);
/* Obtain a process-level lock */
hythread_monitor_t globalMonitor = hythread_global_monitor();
hythread_monitor_enter(globalMonitor);
if (--(*pInitCount) == 0) {
void ** pKey;
va_start(args, pInitCount);
while ((pKey = va_arg(args, void *)) != NULL) {
UDATA key;
key = (UDATA) *pKey;
*pKey = NULL;
if (vmls->keys[key] == (UDATA) -1) {
vmls->keys[key] = vmls->head;
vmls->head = key;
++vmls->freeKeys;
}
}
va_end(args);
}
/* Release the lock */
hythread_monitor_exit(globalMonitor);
}
/**
* Allocate one or more slots of VM local storage.
*
* @code UDATA JNICALL HyVMLSAllocKeys(JNIEnv * env, UDATA * pInitCount, ...); @endcode
*
* @param[in] env A JNIEnv pointer
* @param[in/out] pInitCount Pointer to the reference count for these slots
* @param[out] ... Locations to store the allocated keys
*
* @return 0 on success, 1 on failure.
*
* @note Newly allocated VMLS slots contain NULL in all VMs.
*/
UDATA JNICALL HyVMLSAllocKeys(JNIEnv * env, UDATA * pInitCount, ...)
{
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_ENV(env);
#endif /* HY_NO_THR */
va_list args;
HyVMLSTable *vmls = GLOBAL_DATA(VMLSTable);
/* Obtain a process-level lock */
hythread_monitor_t globalMonitor = hythread_global_monitor();
hythread_monitor_enter(globalMonitor);
if (++(*pInitCount) == 1) {
void **pKey;
UDATA count = 0;
/* Count the number of keys being initialized */
va_start(args, pInitCount);
while ((pKey = va_arg(args, void *)) != NULL) {
++count;
}
va_end(args);
/* Fail if there are not enough free keys left */
if (count > vmls->freeKeys) {
/* Free the lock */
hythread_monitor_exit(globalMonitor);
return 1;
}
/* Allocate the keys */
va_start(args, pInitCount);
while ((pKey = va_arg(args, void *)) != NULL) {
UDATA key;
VMLSContainer* cursor;
key = vmls->head;
vmls->head = vmls->keys[key];
vmls->keys[key] = (UDATA) - 1;
*pKey = (void *) key;
/* Initialize the value of the key to NULL this VM and all other known VMs.
VMs which are being created (i.e. not in the list yet) will have all VMLS
values set to NULL already. The current VM may or may not be in the list,
which means that the list may be empty. The global monitor protects the
VM list as well as the key list. */
/* Iterate through all vms */
cursor = vmls->containerHead;
while (cursor) {
cursor->vmLocalStorage[key - 1] = NULL;
cursor = cursor->linkNext;
}
}
va_end(args);
vmls->freeKeys -= count;
}
/* Release the lock */
hythread_monitor_exit(globalMonitor);
return 0;
}
/**
* Retrieve the value in a VM local storage slot.
*
* @code void* JNICALL HyVMLSGet(JNIEnv * env, void * key); @endcode
*
* @param[in] env JNIEnv pointer
* @param[in] key The VMLS key
*
* @return The contents of the VM local storage slot in the VM that contains the specified env
*/
void * JNICALL HyVMLSGet(JNIEnv * env, void * key)
{
HyVMLSTable* vmls = GLOBAL_DATA(VMLSTable);
JavaVM *javaVM = NULL;
VMLSContainer* cursor = vmls->containerHead;
/* Fetch the javaVM */
(*env)->GetJavaVM(env,&javaVM);
/* Iterate all containers */
while (cursor) {
/* Locate the correct container identified by javaVM */
if (cursor->javaVM == javaVM) {
return cursor->vmLocalStorage[((UDATA) key) - 1];
}
cursor = cursor->linkNext;
}
return NULL;
}
/**
* Store a value into a VM local storage slot. Note the extra level of indirection on
* the key parameter. This indirection enables the use of these functions even when
* multi-vm is not supported. In these cases pKey is a pointer to the raw memory where
* the value will be stored.
*
* @code void* JNICALL HyVMLSSet(JNIEnv * env, void ** pKey, void * value); @endcode
*
* @param[in] env JNIEnv pointer
* @param[in] pKey Pointer to the VM local storage key (NOTE: the extra level of indirection).
* @param[in] value Value to store
*
* @return The value stored
*/
void * JNICALL HyVMLSSet(JNIEnv * env, void ** pKey, void * value)
{
HyVMLSTable* vmls = GLOBAL_DATA(VMLSTable);
JavaVM *javaVM = NULL;
VMLSContainer* cursor = vmls->containerHead;
/* Fetch the javaVM */
(*env)->GetJavaVM(env,&javaVM);
/* Iterate all containers */
while (cursor) {
/* Locate the correct container identified by javaVM */
if (cursor->javaVM == javaVM) {
cursor->vmLocalStorage[((UDATA) *pKey) - 1] = value;
return value;
}
cursor = cursor->linkNext;
}
return value;
}
/**
* This method must be called each time a JavaVM is created. On the
* first invocation this method will allocate the global structures
* needed for book-keeping.
*
* @param[in] vm The JavaVM.
*/
void initializeVMLocalStorage(JavaVM * vm)
{
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_JAVAVM(vm);
#endif /* HY_NO_THR */
HyVMLSTable* vmls = GLOBAL_DATA(VMLSTable);
VMLSContainer* container = NULL;
/* Reach for the VM interface */
PORT_ACCESS_FROM_JAVAVM(vm);
if (!vmls->initialized) {
UDATA i;
/* Obtain the global lock */
hythread_monitor_t globalMonitor = hythread_global_monitor();
hythread_monitor_enter(globalMonitor);
/* Test again now that we own the lock */
if (!vmls->initialized) {
/* Initialize keys */
for (i = 1; i < VMLS_MAX_KEYS-1; ++i) {
vmls->keys[i] = i + 1;
}
vmls->keys[0] = 0;
vmls->keys[VMLS_MAX_KEYS-1] = 0;
vmls->head = 1;
vmls->freeKeys = VMLS_MAX_KEYS-1;
vmls->initialized = TRUE;
vmls->head = 1;
vmls->containerHead = NULL;
}
/* Allocate a new container */
container = hymem_allocate_memory(sizeof(VMLSContainer));
if (!container) return;
memset(container,0,sizeof(VMLSContainer));
container->javaVM = vm;
/* Insert it into the list */
container->linkNext = vmls->containerHead;
vmls->containerHead = container;
/* Release the global lock */
hythread_monitor_exit(globalMonitor);
}
}
/**
* This method must be called each time a JavaVM is destroyed.
*
* @param[in] vm The JavaVM.
*/
void freeVMLocalStorage(JavaVM * vm)
{
HyVMLSTable* vmls = GLOBAL_DATA(VMLSTable);
VMLSContainer* cursor = vmls->containerHead;
VMLSContainer* previous = NULL;
/* Reach for the VM interface */
#if defined(HY_NO_THR)
THREAD_ACCESS_FROM_JAVAVM(vm);
#endif /* HY_NO_THR */
PORT_ACCESS_FROM_JAVAVM(vm);
/* Obtain the global lock */
hythread_monitor_t globalMonitor = hythread_global_monitor();
hythread_monitor_enter(globalMonitor);
/* Iterate all containers */
while (cursor) {
/* Locate the correct container identified by javaVM */
if (cursor->javaVM == vm) {
/* Unlink the container from the list */
if (previous) {
previous->linkNext = cursor->linkNext;
} else {
vmls->containerHead = cursor->linkNext;
}
hymem_free_memory(cursor);
break;
}
/* Move to the next element */
previous = cursor;
cursor = cursor->linkNext;
}
/* Release the global lock */
hythread_monitor_exit(globalMonitor);
}