1/* 2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26#include "awt_GDIObject.h" 27 28/** 29 * These methods work around a bug in Windows where allocating 30 * the max number of GDI Objects (HDC, Pen, Brush, etc.) will cause the 31 * application and desktop to become unusable. The workaround 32 * ensures we never reach this maximum, by refcounting 33 * HDC, Pen, and Brush objects that are active. We increment the refcount 34 * when we create these objects and decrement the 35 * refcount when we release them, so that our numCurrentObjects 36 * counter should always equal the number of unreleased objects. 37 * We only do this for HDC, Pen, and Brush because these are the only GDI 38 * objects that may grow without bound in our implementation (we cache 39 * these objects per thread, so a growing number of threads may have 40 * unique HDC/Pen/Brush objects per thread and might approach the maximum). 41 * Also, we do not count objects allocated on a temporary basis (such as 42 * the many calls to GetDC() in our code, followed quickly by ReleaseDC()); 43 * we only care about long-lived GDI objects that might bloat our total 44 * object usage. 45 */ 46 47/** 48 * Default GDI Object limit for win2k and XP is 10,000 49 * Set our limit much lower than that to allow a buffer for objects 50 * created beyond the per-thread HDC/Brush/Pen objects we are 51 * counting here, including objects created by the overall process 52 * (which could include the browser, in the case of applets) 53 */ 54#define MAX_GDI_OBJECTS 9000 55 56// Static initialization of these globals used in AwtGDIObject 57int AwtGDIObject::numCurrentObjects = 0; 58// this variable will never be deleted. initialized below with SafeCreate. 59CriticalSection* AwtGDIObject::objectCounterLock = NULL; 60int AwtGDIObject::maxGDIObjects = GetMaxGDILimit(); 61 62/** 63 * Sets up max GDI limit; we query the registry key that 64 * defines this value on WindowsXP and Windows2000. 65 * If we fail here, we will use the default value 66 * MAX_GDI_OBJECTS as a fallback value. This is not unreasonable - 67 * it seems unlikely that many people would change this 68 * registry key setting. 69 * NOTE: This function is called automatically at startup to 70 * set the value of maxGDIObjects; it should not be necessary to 71 * call this function from anywhere else. Think of it like a static 72 * block in Java. 73 */ 74int AwtGDIObject::GetMaxGDILimit() { 75 int limit = MAX_GDI_OBJECTS; 76 HKEY hKey = NULL; 77 DWORD ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 78 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, 79 KEY_QUERY_VALUE, &hKey); 80 if (ret == ERROR_SUCCESS) { 81 DWORD valueLength = 4; 82 DWORD regValue; 83 ret = RegQueryValueEx(hKey, L"GDIProcessHandleQuota", NULL, NULL, 84 (LPBYTE)®Value, &valueLength); 85 if (ret == ERROR_SUCCESS) { 86 // Set limit to 90% of the actual limit to account for other 87 // GDI objects that the process might need 88 limit = (int)(regValue * .9); 89 } else { 90 J2dTraceLn(J2D_TRACE_WARNING, 91 "Problem with RegQueryValueEx in GetMaxGDILimit"); 92 } 93 RegCloseKey(hKey); 94 } else { 95 J2dTraceLn(J2D_TRACE_WARNING, 96 "Problem with RegOpenKeyEx in GetMaxGDILimit"); 97 } 98 return limit; 99} 100 101/** 102 * Increment the object counter to indicate that we are about to 103 * create a new GDI object. If the limit has been reached, skip the 104 * increment and return FALSE to indicate that an object should 105 * not be allocated. 106 */ 107BOOL AwtGDIObject::IncrementIfAvailable() { 108 BOOL available; 109 CriticalSection* pLock = SafeCreate(objectCounterLock); 110 pLock->Enter(); 111 if (numCurrentObjects < maxGDIObjects) { 112 available = TRUE; 113 ++numCurrentObjects; 114 } else { 115 // First, flush the cache; we may have run out simply because 116 // we have unused colors still reserved in the cache 117 GDIHashtable::flushAll(); 118 // Now check again to see if flushing helped. If not, we really 119 // have run out. 120 if (numCurrentObjects < maxGDIObjects) { 121 available = TRUE; 122 ++numCurrentObjects; 123 } else { 124 available = FALSE; 125 } 126 } 127 pLock->Leave(); 128 return available; 129} 130 131/** 132 * Decrement the counter after releasing a GDI Object 133 */ 134void AwtGDIObject::Decrement() { 135 CriticalSection* pLock = SafeCreate(objectCounterLock); 136 pLock->Enter(); 137 --numCurrentObjects; 138 pLock->Leave(); 139} 140 141/** 142 * This utility method is called by subclasses of AwtGDIObject 143 * to ensure capacity for an additional GDI object. Failure 144 * results in throwing an AWTException. 145 */ 146BOOL AwtGDIObject::EnsureGDIObjectAvailability() 147{ 148 if (!IncrementIfAvailable()) { 149 // IncrementIfAvailable flushed the cache but still failed; must 150 // have hit the limit. Throw an exception to indicate the problem. 151 if (jvm != NULL) { 152 JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); 153 if (env != NULL && !safe_ExceptionOccurred(env)) { 154 JNU_ThrowByName(env, "java/awt/AWTError", 155 "Pen/Brush creation failure - " \ 156 "exceeded maximum GDI resources"); 157 } 158 } 159 return FALSE; 160 } 161 return TRUE; 162} 163