blob: e22aa3b6458c88cee22d942d93aa1771e27dd6db [file] [log] [blame]
/*
* Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <new.h>
#include <stdio.h>
#include "awt_new.h"
#include "awt_Toolkit.h"
#include "Hashtable.h"
// Don't want to pull in the redefined allocation functions
#undef malloc
#undef calloc
#undef realloc
#undef ExceptionOccurred
#ifdef OUTOFMEM_TEST
#undef safe_Malloc
#undef safe_Calloc
#undef safe_Realloc
#undef new
static CriticalSection *alloc_lock;
static FILE *logfile;
static DWORD thread_seeded = TLS_OUT_OF_INDEXES;
#endif
void
NewHandler::init() {
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
#ifdef OUTOFMEM_TEST
alloc_lock = new CriticalSection();
logfile = fopen("java.awt.outofmem.txt", "w");
DASSERT(logfile);
thread_seeded = TlsAlloc();
DASSERT(thread_seeded != TLS_OUT_OF_INDEXES);
#endif
// use new handler for operator new and malloc
_set_new_mode(1);
// set the function which will be called when operator new or
// malloc runs out of memory
_set_new_handler((_PNH)NewHandler::handler);
}
// Called when malloc or operator new runs out of memory. We try to
// compact the heap by initiating a Java GC. If the amount of free
// memory available after this operation increases, then we return
// (1) to indicate that malloc or operator new should retry the
// allocation. Returning (0) indicates that the allocation should fail.
int
NewHandler::handler(size_t) {
fprintf(stderr, "java.lang.OutOfMemoryError\n");
return FALSE;
}
// These three functions throw std::bad_alloc in an out of memory condition
// instead of returning 0. safe_Realloc will return 0 if memblock is not
// NULL and size is 0. safe_Malloc and safe_Calloc will never return 0.
void *safe_Malloc(size_t size) throw (std::bad_alloc) {
register void *ret_val = malloc(size);
if (ret_val == NULL) {
throw std::bad_alloc();
}
return ret_val;
}
void *safe_Calloc(size_t num, size_t size) throw (std::bad_alloc) {
register void *ret_val = calloc(num, size);
if (ret_val == NULL) {
throw std::bad_alloc();
}
return ret_val;
}
void *safe_Realloc(void *memblock, size_t size) throw (std::bad_alloc) {
register void *ret_val = realloc(memblock, size);
// Special case for realloc.
if (memblock != NULL && size == 0) {
return ret_val; // even if it's NULL
}
if (ret_val == NULL) {
throw std::bad_alloc();
}
return ret_val;
}
#if !defined(DEBUG)
// This function exists because VC++ 5.0 currently does not conform to the
// Standard C++ specification which requires that operator new throw
// std::bad_alloc in an out of memory situation. Instead, VC++ 5.0 returns 0.
//
// This function can be safely removed when the problem is corrected.
void * CDECL operator new(size_t size) throw (std::bad_alloc) {
return safe_Malloc(size);
}
#endif
// This function is called at the beginning of an entry point.
// Entry points are functions which are declared:
// 1. CALLBACK,
// 2. JNIEXPORT,
// 3. __declspec(dllexport), or
// 4. extern "C"
// A function which returns an HRESULT (an OLE function) is also an entry
// point.
void
entry_point(void) {
if (jvm != NULL) {
JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (env != NULL) {
env->ExceptionClear();
}
}
}
// This function is called when a std::bad_alloc exception is caught.
void
handle_bad_alloc(void) {
if (jvm != NULL) {
JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (env != NULL) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
}
}
}
// This function is called instead of ExceptionOccurred. It throws
// std::bad_alloc if a java.lang.OutOfMemoryError is currently pending
// on the calling thread.
jthrowable
safe_ExceptionOccurred(JNIEnv *env) throw (std::bad_alloc) {
jthrowable xcp = env->ExceptionOccurred();
if (xcp != NULL) {
env->ExceptionClear(); // if we don't do this, FindClass will fail
jclass outofmem = env->FindClass("java/lang/OutOfMemoryError");
DASSERT(outofmem != NULL);
jboolean isOutofmem = env->IsInstanceOf(xcp, outofmem);
env->DeleteLocalRef(outofmem);
if (isOutofmem) {
env->DeleteLocalRef(xcp);
throw std::bad_alloc();
} else {
// rethrow exception
env->Throw(xcp);
return xcp;
}
}
return NULL;
}
#ifdef OUTOFMEM_TEST
#include <time.h>
#include <limits.h>
static void
rand_alloc_fail(const char *file, int line) throw (std::bad_alloc)
{
if (alloc_lock == NULL) { // Not yet initialized
return;
}
CriticalSection::Lock l(*alloc_lock);
// Each thread must be seeded individually
if (!TlsGetValue(thread_seeded)) {
TlsSetValue(thread_seeded, (LPVOID)1);
srand((unsigned int)time(NULL));
}
if (rand() > (int)(RAND_MAX * .999)) { // .1% chance of alloc failure
fprintf(stderr, "failing allocation at %s, %d\n", file, line);
fprintf(logfile, "%s, %d\n", file, line);
fflush(logfile);
VERIFY(malloc(INT_MAX) == 0); // should fail
throw std::bad_alloc();
}
}
void *safe_Malloc_outofmem(size_t size, const char *file, int line)
throw (std::bad_alloc)
{
rand_alloc_fail(file, line);
return safe_Malloc(size);
}
void *safe_Calloc_outofmem(size_t num, size_t size, const char *file, int line)
throw (std::bad_alloc)
{
rand_alloc_fail(file, line);
return safe_Calloc(num, size);
}
void *safe_Realloc_outofmem(void *memblock, size_t size, const char *file,
int line)
throw (std::bad_alloc)
{
rand_alloc_fail(file, line);
return safe_Realloc(memblock, size);
}
void * CDECL operator new(size_t size, const char *file, int line)
throw (std::bad_alloc)
{
rand_alloc_fail(file, line);
return operator new(size);
}
#endif