1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.kernel.util;
16  
17  import java.lang.ref.ReferenceQueue;
18  import java.lang.ref.SoftReference;
19  
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.List;
23  import java.util.concurrent.locks.Lock;
24  import java.util.concurrent.locks.ReentrantLock;
25  
26  /**
27   * <a href="CharBufferPool.java.html"><b><i>View Source</i></b></a>
28   *
29   * @author Shuyang Zhou
30   */
31  public class CharBufferPool {
32  
33      public static char[] borrow(int size) {
34          if (!isEnabled()) {
35              return new char[size];
36          }
37  
38          _cleanUpDeadBuffers();
39  
40          int poolSize = -1;
41  
42          _modifyLock.lock();
43  
44          try {
45              int index = Collections.binarySearch(
46                  _charBufferHoldersPool, new CharBufferHolder(size));
47  
48              if (index < 0) {
49                  index = -(index + 1);
50              }
51  
52              poolSize = _charBufferHoldersPool.size();
53  
54              while (index < poolSize) {
55                  CharBufferHolder charBufferHolder = _charBufferHoldersPool.get(
56                      index);
57  
58                  if (charBufferHolder._borrowed) {
59                      index++;
60                  }
61                  else {
62                      char[] charBuffer = charBufferHolder.get();
63  
64                      if (charBuffer != null) {
65                          charBufferHolder._borrowed = true;
66  
67                          List<CharBufferHolder> borrowedCharBufferHolders =
68                              _borrowedCharBufferHoldersThreadLocal.get();
69  
70                          borrowedCharBufferHolders.add(charBufferHolder);
71  
72                          return charBuffer;
73                      }
74  
75                      _charBufferHoldersPool.remove(index);
76                  }
77              }
78          }
79          finally {
80              _modifyLock.unlock();
81          }
82  
83          char[] charBuffer = new char[size + (size >> 9)];
84  
85          if (poolSize < _MAX_POOL_SIZE) {
86              CharBufferHolder charBufferHolder = new CharBufferHolder(
87                  charBuffer);
88  
89              List<CharBufferHolder> borrowedCharBufferHolders =
90                  _borrowedCharBufferHoldersThreadLocal.get();
91  
92              borrowedCharBufferHolders.add(charBufferHolder);
93          }
94  
95          return charBuffer;
96      }
97  
98      public static void cleanUp() {
99          List<CharBufferHolder> charBufferHolders =
100             _borrowedCharBufferHoldersThreadLocal.get();
101 
102         _modifyLock.lock();
103 
104         try {
105             for (CharBufferHolder charBufferHolder :
106                     charBufferHolders) {
107 
108                 if (charBufferHolder._borrowed) {
109                     charBufferHolder._borrowed = false;
110                 }
111                 else {
112                     int index = Collections.binarySearch(
113                         _charBufferHoldersPool, charBufferHolder);
114 
115                     if (index < 0) {
116                         index = -(index + 1);
117                     }
118 
119                     _charBufferHoldersPool.add(index, charBufferHolder);
120                 }
121             }
122         }
123         finally {
124             _modifyLock.unlock();
125         }
126 
127         charBufferHolders.clear();
128 
129         _cleanUpDeadBuffers();
130     }
131 
132     public static boolean isEnabled() {
133         return _enabledThreadLocal.get();
134     }
135 
136     public static void setEnabled(boolean enabled) {
137         _enabledThreadLocal.set(enabled);
138     }
139 
140     private static void _cleanUpDeadBuffers() {
141 
142         // Peek before acquiring a Lock. This is crucial for concurrency since
143         // SoftReferences will only be freed when there is a full GC or CMS
144         // rescan. This means that the ReferenceQueue will be empty most of time
145         // and should return immediately without touching the Lock. But when the
146         // ReferenceQueue is not empty because a SoftReference has been freed by
147         // the GC, it is more efficient to hold the Lock outside the while loop
148         // rather than acquiring many dead CharBufferHolders.
149 
150         CharBufferHolder charBufferHolder =
151             (CharBufferHolder)_referenceQueue.poll();
152 
153         if (charBufferHolder == null) {
154             return;
155         }
156 
157         _modifyLock.lock();
158 
159         try {
160             do {
161                 _charBufferHoldersPool.remove(charBufferHolder);
162             }
163             while ((charBufferHolder =
164                         (CharBufferHolder)_referenceQueue.poll()) != null);
165         }
166         finally {
167             _modifyLock.unlock();
168         }
169     }
170 
171     /**
172      * The initial pool size should be set slightly higher than the maximum real
173      * world concurrent processing request number. This value should not be
174      * tuned because it is based on extensive performance tests that tie to the
175      * overall system's inherent nature.
176      */
177     private static final int _INITIAL_POOL_SIZE = 50;
178 
179     /**
180      * Make the actual maximum pool size twice the initial pool size to prevent
181      * random peaks that may cause an unnecessarily high use of old generation
182      * memory.
183      */
184     private static final int _MAX_POOL_SIZE = _INITIAL_POOL_SIZE * 2;
185 
186     private static ThreadLocal<List<CharBufferHolder>>
187         _borrowedCharBufferHoldersThreadLocal =
188             new AutoResetThreadLocal<List<CharBufferHolder>>(
189                 new ArrayList<CharBufferHolder>());
190     private static List<CharBufferHolder> _charBufferHoldersPool =
191         new ArrayList<CharBufferHolder>(_INITIAL_POOL_SIZE);
192     private static ThreadLocal<Boolean> _enabledThreadLocal =
193         new AutoResetThreadLocal<Boolean>(false);
194     private static Lock _modifyLock = new ReentrantLock();
195     private static ReferenceQueue<Object> _referenceQueue =
196         new ReferenceQueue<Object>();
197 
198     private static class CharBufferHolder
199         extends SoftReference<char[]> implements Comparable<CharBufferHolder> {
200 
201         public CharBufferHolder(char[] charBuffer) {
202             super(charBuffer, _referenceQueue);
203 
204             _length = charBuffer.length;
205         }
206 
207         public CharBufferHolder(int length) {
208             super(null);
209 
210             _length = length;
211         }
212 
213         public int compareTo(CharBufferHolder charBufferHolder) {
214             return _length - charBufferHolder._length;
215         }
216 
217         private boolean _borrowed;
218         private int _length;
219 
220     }
221 
222 }