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)&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 */
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