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)&regValue, &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