1 /*
2  * Copyright (c) 1999, 2008, 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 #ifndef GDIHASHTABLE_H
27 #define GDIHASHTABLE_H
28 
29 #include "Hashtable.h"
30 
31 /*
32  * This class has been created to fix bug #4191297.
33  */
34 
35 /**
36  * GDIHashtable class. Subclasses Hashtable to provide
37  * capability of batch destruction of freed GDI resources.
38  * Assumes that values are only of AwtGDIObject type.
39  */
40 class GDIHashtable : public Hashtable {
41     struct ListEntry {
42         GDIHashtable* table;
43         ListEntry*      next;
44     };
45 
46     /**
47      * GDIHashtable::List class. Designed to store pointers
48      * to all existing GDIHashtables. This is required
49      * to flush all GDIHashtables at once.
50      */
51     class List {
52     public:
List()53         List() : m_pHead(NULL) {}
~List()54         ~List() { clear(); }
55 
56         void add(GDIHashtable*);
57         void remove(GDIHashtable*);
58         void flushAll();
59 
60     private:
61         void clear();
62 
63         ListEntry* m_pHead;
64 
65         CriticalSection m_listLock;
66     };
67 
68     friend class List;
69 
70     /**
71      * GDIHashtable::BatchDestructionManager class.
72      * Tracks the amount of remaining space in the GDI
73      * and flushes GDIHashtables when needed.
74      */
75     class BatchDestructionManager {
76     private:
77         int               m_nCounter;
78         UINT              m_nFirstThreshold;
79         UINT              m_nSecondThreshold;
80         UINT              m_nDestroyPeriod;
81         BOOL              m_bBatchingEnabled;
82 
83         List              m_list;
84 
85         CriticalSection   m_managerLock;
86 
87     public:
88         /**
89          * Constructs a new BatchDestructionManager with the specified parameters.
90          * The care should be taken when non-default values are used, since it
91          * affects performance. They always should satisfy the inequality
92          * 10 < nSecondThreshold < nFirstThreshold.
93          *
94          * @param nFirstThreshold if less than <code>nFirstThreshold</code> percents
95          *        of space in GDI heaps is free all existing GDIHashtables will be
96          *        flushed on the next call of <code>update</code>.
97          * @param nSecondThreshold if less than <code>nSecondThreshold</code>
98          *        percents of space in GDI heaps is free after the flush
99          *        <code>update</code> will return <code>TRUE</code>.
100          * @param nDestroyPeriod specifies how often free space in GDI heaps
101          *        will be rechecked in low-resource situation.
102          *        In detailss: after <code>update</code> prohibit batching by
103          *        setting <code>m_bBatchingEnabled</code> to <code>FALSE</code>
104          *        it won't recheck free GDI space for the next
105          *        <code>nDestroyPeriod<code> calls. So during this time
106          *        <code>shouldDestroy</code> will return <code>TRUE</code>.
107          *        This is done to reduce performance impact
108          *        caused by calls to <code>GetFreeSystemResourses</code>.
109          */
110         BatchDestructionManager(UINT nFirstThreshold = 50,
111                                 UINT nSecondThreshold = 15,
112                                 UINT nDestroyPeriod = 200);
113 
114         /**
115          * Adds the specified GDIHashtable to the internal list.
116          * <code>flushAll</code> flushes all GDIHashtables from this list.
117          * @param table pointer to the GDIHashtable to be added.
118          */
add(GDIHashtable * table)119         INLINE void add(GDIHashtable* table) { m_list.add(table); }
120 
121         /**
122          * Removes the specified GDIHashtable to the internal list.
123          * Does nothing if the specified table doesn't exist.
124          * @param table pointer to the GDIHashtable to be removed.
125          */
remove(GDIHashtable * table)126         INLINE void remove(GDIHashtable* table) { m_list.remove(table); }
127 
128         /**
129          * @return <code>TRUE</code> if unreferenced AwtGDIObjects shouldn't
130          *         be destroyed immediatelly. They will be deleted in
131          *         a batch when needed.
132          *         <code>FALSE</code> if unreferenced AwtGDIObjects should
133          *         be destroyed as soon as freed.
134          */
isBatchingEnabled()135         INLINE BOOL isBatchingEnabled() { return m_bBatchingEnabled; }
136 
137         /**
138          * Flushes all the GDIHashtables from the internal list.
139          */
flushAll()140         INLINE void flushAll() { m_list.flushAll(); }
141 
142         /**
143          * Decrements the internal counter. The initial value
144          * is assigned by <code>update</code> according to
145          * the BatchDestructionManager parameters. When the
146          * counter hits zero the BatchDestructionManager will
147          * recheck the amount of free space in GDI heaps.
148          * This is done to reduce the performance impact caused
149          * by calls to GetFreeSystemResources. Currently this
150          * method is called when a new GDI resource is created.
151          */
decrementCounter()152         INLINE void decrementCounter() { m_nCounter--; }
153 
getLock()154         INLINE CriticalSection& getLock() { return m_managerLock; }
155     };
156 
157  public:
158     /**
159      * Constructs a new, empty GDIHashtable with the specified initial
160      * capacity and the specified load factor.
161      */
162     GDIHashtable(const char* name, void (*deleteProc)(void*) = NULL,
163                    int initialCapacity = 29, float loadFactor = 0.75) :
Hashtable(name,deleteProc,initialCapacity,loadFactor)164         Hashtable(name, deleteProc, initialCapacity, loadFactor) {
165         manager.add(this);
166     }
167 
~GDIHashtable()168     ~GDIHashtable() {
169         manager.remove(this);
170     }
171 
172     /**
173      * Puts the specified element into the hashtable, using the specified
174      * key.  The element may be retrieved by doing a get() with the same key.
175      * The key and the element cannot be null.
176      */
177     void* put(void* key, void* value);
178 
179     /**
180      * Depending on the amount of free space in GDI heads destroys
181      * as unreferenced the element corresponding to the key or keeps
182      * it for destruction in batch.
183      * Does nothing if the key is not present.
184      */
185     void release(void* key);
186 
187     /**
188      * Removes all unreferenced elements from the hastable.
189      */
190     void flush();
191 
192     /**
193      * Flushes all existing GDIHashtable instances.
194      */
flushAll()195     INLINE static void flushAll() { manager.flushAll(); }
196 
getManagerLock()197     INLINE CriticalSection& getManagerLock() { return manager.getLock(); }
198 
199  private:
200 
201     static BatchDestructionManager manager;
202 
203 };
204 
205 #endif // GDIHASHTABLE_H
206