| /* | |
| * Copyright (c) 2005, 2006, 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 "awt_GDIObject.h" | |
| /** | |
| * These methods work around a bug in Windows where allocating | |
| * the max number of GDI Objects (HDC, Pen, Brush, etc.) will cause the | |
| * application and desktop to become unusable. The workaround | |
| * ensures we never reach this maximum, by refcounting | |
| * HDC, Pen, and Brush objects that are active. We increment the refcount | |
| * when we create these objects and decrement the | |
| * refcount when we release them, so that our numCurrentObjects | |
| * counter should always equal the number of unreleased objects. | |
| * We only do this for HDC, Pen, and Brush because these are the only GDI | |
| * objects that may grow without bound in our implementation (we cache | |
| * these objects per thread, so a growing number of threads may have | |
| * unique HDC/Pen/Brush objects per thread and might approach the maximum). | |
| * Also, we do not count objects allocated on a temporary basis (such as | |
| * the many calls to GetDC() in our code, followed quickly by ReleaseDC()); | |
| * we only care about long-lived GDI objects that might bloat our total | |
| * object usage. | |
| */ | |
| /** | |
| * Default GDI Object limit for win2k and XP is 10,000 | |
| * Set our limit much lower than that to allow a buffer for objects | |
| * created beyond the per-thread HDC/Brush/Pen objects we are | |
| * counting here, including objects created by the overall process | |
| * (which could include the browser, in the case of applets) | |
| */ | |
| #define MAX_GDI_OBJECTS 9000 | |
| // Static initialization of these globals used in AwtGDIObject | |
| int AwtGDIObject::numCurrentObjects = 0; | |
| // this variable will never be deleted. initialized below with SafeCreate. | |
| CriticalSection* AwtGDIObject::objectCounterLock = NULL; | |
| int AwtGDIObject::maxGDIObjects = GetMaxGDILimit(); | |
| /** | |
| * Sets up max GDI limit; we query the registry key that | |
| * defines this value on WindowsXP and Windows2000. | |
| * If we fail here, we will use the default value | |
| * MAX_GDI_OBJECTS as a fallback value. This is not unreasonable - | |
| * it seems unlikely that many people would change this | |
| * registry key setting. | |
| * NOTE: This function is called automatically at startup to | |
| * set the value of maxGDIObjects; it should not be necessary to | |
| * call this function from anywhere else. Think of it like a static | |
| * block in Java. | |
| */ | |
| int AwtGDIObject::GetMaxGDILimit() { | |
| int limit = MAX_GDI_OBJECTS; | |
| HKEY hKey = NULL; | |
| DWORD ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, | |
| L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, | |
| KEY_QUERY_VALUE, &hKey); | |
| if (ret == ERROR_SUCCESS) { | |
| DWORD valueLength = 4; | |
| DWORD regValue; | |
| ret = RegQueryValueEx(hKey, L"GDIProcessHandleQuota", NULL, NULL, | |
| (LPBYTE)®Value, &valueLength); | |
| if (ret == ERROR_SUCCESS) { | |
| // Set limit to 90% of the actual limit to account for other | |
| // GDI objects that the process might need | |
| limit = (int)(regValue * .9); | |
| } else { | |
| J2dTraceLn(J2D_TRACE_WARNING, | |
| "Problem with RegQueryValueEx in GetMaxGDILimit"); | |
| } | |
| RegCloseKey(hKey); | |
| } else { | |
| J2dTraceLn(J2D_TRACE_WARNING, | |
| "Problem with RegOpenKeyEx in GetMaxGDILimit"); | |
| } | |
| return limit; | |
| } | |
| /** | |
| * Increment the object counter to indicate that we are about to | |
| * create a new GDI object. If the limit has been reached, skip the | |
| * increment and return FALSE to indicate that an object should | |
| * not be allocated. | |
| */ | |
| BOOL AwtGDIObject::IncrementIfAvailable() { | |
| BOOL available; | |
| CriticalSection* pLock = SafeCreate(objectCounterLock); | |
| pLock->Enter(); | |
| if (numCurrentObjects < maxGDIObjects) { | |
| available = TRUE; | |
| ++numCurrentObjects; | |
| } else { | |
| // First, flush the cache; we may have run out simply because | |
| // we have unused colors still reserved in the cache | |
| GDIHashtable::flushAll(); | |
| // Now check again to see if flushing helped. If not, we really | |
| // have run out. | |
| if (numCurrentObjects < maxGDIObjects) { | |
| available = TRUE; | |
| ++numCurrentObjects; | |
| } else { | |
| available = FALSE; | |
| } | |
| } | |
| pLock->Leave(); | |
| return available; | |
| } | |
| /** | |
| * Decrement the counter after releasing a GDI Object | |
| */ | |
| void AwtGDIObject::Decrement() { | |
| CriticalSection* pLock = SafeCreate(objectCounterLock); | |
| pLock->Enter(); | |
| --numCurrentObjects; | |
| pLock->Leave(); | |
| } | |
| /** | |
| * This utility method is called by subclasses of AwtGDIObject | |
| * to ensure capacity for an additional GDI object. Failure | |
| * results in throwing an AWTException. | |
| */ | |
| BOOL AwtGDIObject::EnsureGDIObjectAvailability() | |
| { | |
| if (!IncrementIfAvailable()) { | |
| // IncrementIfAvailable flushed the cache but still failed; must | |
| // have hit the limit. Throw an exception to indicate the problem. | |
| if (jvm != NULL) { | |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); | |
| if (env != NULL && !safe_ExceptionOccurred(env)) { | |
| JNU_ThrowByName(env, "java/awt/AWTError", | |
| "Pen/Brush creation failure - " \ | |
| "exceeded maximum GDI resources"); | |
| } | |
| } | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } |