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
57 int AwtGDIObject::numCurrentObjects = 0;
58 // this variable will never be deleted. initialized below with SafeCreate.
59 CriticalSection* AwtGDIObject::objectCounterLock = NULL;
60 int 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 */
GetMaxGDILimit()74 int 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 */
IncrementIfAvailable()107 BOOL 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 */
Decrement()134 void 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 */
EnsureGDIObjectAvailability()146 BOOL 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