001: package org.jacorb.orb;
002:
003: /*
004: * JacORB - a free Java ORB
005: *
006: * Copyright (C) 1997-2004 Gerald Brose.
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Library General Public
010: * License as published by the Free Software Foundation; either
011: * version 2 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Library General Public License for more details.
017: *
018: * You should have received a copy of the GNU Library General Public
019: * License along with this library; if not, write to the Free
020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: */
022:
023: import java.util.*;
024:
025: import org.apache.avalon.framework.configuration.*;
026:
027: import org.omg.CORBA.NO_MEMORY;
028: import org.omg.CORBA.BAD_INV_ORDER;
029:
030: /**
031: * A BufferManager is used to share a pool of buffers and to implement
032: * a buffer allocation policy. This reduces the number of memory
033: * allocations and deallocations and the overall memory footprint.
034: * Buffers are generally created on demand.
035: *
036: * The BufferManager uses a singleton pattern, so will only be a single
037: * shared BuffferManager across all ORBs in a process.
038: *
039: * @author Gerald Brose, FU Berlin
040: * @version $Id: BufferManager.java,v 1.26 2006/07/20 13:18:20 alphonse.bendt Exp $
041: */
042:
043: public final class BufferManager {
044: /** the buffer pool */
045: private List[] bufferPool;
046: // The 'extra-large' buffer cache.
047: private byte[] bufferMax = null;
048:
049: /** the maximal buffer size managed since the buffer
050: pool is ordered by buffer size in log2 steps */
051:
052: private static int MAX;
053:
054: /** the buffer at pos n has size 2**(n+MIN_OFFSET)
055: so the smallest available buffer is 2**MIN_OFFSET,
056: the largest buffers managed are 2**(MIN_OFFSET+MAX-1)
057: */
058:
059: private static final int MIN_OFFSET = 5;
060:
061: /** max number of buffers of the same size held in pool */
062:
063: private static final int THRESHOLD = 20;
064: private static final int MEM_BUFSIZE = 256;
065: private static final int MIN_PREFERRED_BUFS = 10;
066:
067: // Purge thread for QoS purging of the bufferMax cache.
068: private Reaper reaper;
069:
070: /**
071: * <code>time</code> denotes whether the maxCache will be active:
072: * -1: Not active
073: * 0 : Active, never flushed
074: * >0: Active with reaper flush thread.
075: */
076: private static int time = 0;
077:
078: /** the singleton instance */
079: private final static BufferManager singleton = new BufferManager();
080:
081: private static boolean configured = false;
082:
083: /**
084: * configures the BufferManager, in turn configures the singleton.
085: * Must be called before getInstance() !
086: */
087: public synchronized static void configure(
088: Configuration configuration) {
089: singleton.singletonConfigure(configuration);
090: configured = true;
091: }
092:
093: private BufferManager() {
094: super ();
095: }
096:
097: /**
098: * configures the singleton
099: */
100:
101: private void singletonConfigure(Configuration configuration) {
102: time = configuration.getAttributeAsInteger(
103: "jacorb.bufferManagerMaxFlush", 0);
104:
105: MAX = configuration.getAttributeAsInteger(
106: "jacorb.maxManagedBufSize", 18);
107:
108: bufferPool = new List[MAX];
109:
110: for (int i = 0; i < MAX; i++) {
111: bufferPool[i] = new ArrayList();
112: }
113:
114: /* create a number of buffers for the preferred memory buffer
115: size */
116:
117: int m_pos = 0;
118: int j = MEM_BUFSIZE;
119:
120: while (j > 1) {
121: j = j >> 1;
122: m_pos++;
123: }
124:
125: for (int min = 0; min < MIN_PREFERRED_BUFS; min++) {
126: bufferPool[m_pos - MIN_OFFSET].add(new byte[MEM_BUFSIZE]);
127: }
128:
129: if (time > 0) {
130: if (reaper != null) {
131: // this is the case when
132: // the BufferManager is re-configured
133: reaper.dispose();
134: }
135:
136: // create new reaper
137: reaper = new Reaper(time);
138: reaper.setName("BufferManager MaxCache Reaper");
139: reaper.setDaemon(true);
140: reaper.start();
141: }
142: }
143:
144: /**
145: * May only be called after configure()
146: * @throws BAD_INV_ORDER if not previously configured
147: */
148:
149: public synchronized static BufferManager getInstance()
150: throws BAD_INV_ORDER {
151: if (!configured) {
152: throw new BAD_INV_ORDER("Buffer Manager not configured");
153: }
154: return singleton;
155: }
156:
157: /**
158: * Log 2, rounded up
159: */
160:
161: private static final int log2up(int n) {
162: int l = 0;
163: int nn = n - 1;
164: while ((nn >> l) != 0) {
165: l++;
166: }
167:
168: return l;
169: }
170:
171: /**
172: * Log 2, rounded down
173: */
174:
175: private static final int log2down(int n) {
176: int l = 0;
177: int nn = n;
178: while ((nn >> l) != 0) {
179: l++;
180: }
181:
182: return l - 1;
183: }
184:
185: public byte[] getPreferredMemoryBuffer() {
186: return getBuffer(MEM_BUFSIZE);
187: }
188:
189: public byte[] getBuffer(int initial) {
190: return getBuffer(initial, false);
191: }
192:
193: /**
194: * <code>getBuffer</code> returns a new buffer.
195: *
196: * @param initial an <code>int</code> value
197: * @param cdrStr a <code>boolean</code> value to denote if CDROuputStream is caller
198: * (may use cache in this situation)
199: * @return a <code>byte[]</code> value
200: */
201:
202: public synchronized byte[] getBuffer(int initial, boolean cdrStr) {
203: byte[] result;
204: List s;
205:
206: int log = log2up(initial);
207:
208: if (log >= MAX) {
209: try {
210: if (!cdrStr || time < 0) {
211: // Defaults to returning asked for size
212: result = new byte[initial];
213: } else {
214: // Using cache so do below determination
215: if (bufferMax == null || bufferMax.length < initial) {
216: // Autocache really large values for speed
217: bufferMax = new byte[initial * 2];
218: }
219: // Else return the cached buffer
220: result = bufferMax;
221: bufferMax = null;
222: }
223: } catch (OutOfMemoryError e) {
224: throw new NO_MEMORY(e.toString());
225: }
226: } else {
227: s = bufferPool[log > MIN_OFFSET ? log - MIN_OFFSET : 0];
228:
229: if (!s.isEmpty()) {
230: // pop least recently added buffer from the list
231: result = (byte[]) s.remove(s.size() - 1);
232: } else {
233: result = new byte[log > MIN_OFFSET ? 1 << log
234: : 1 << MIN_OFFSET];
235: }
236: }
237: return result;
238: }
239:
240: public void returnBuffer(byte[] current) {
241: returnBuffer(current, false);
242: }
243:
244: /**
245: * Describe <code>returnBuffer</code> method here.
246: *
247: * @param current a <code>byte[]</code> value
248: * @param cdrStr a <code>boolean</code> value value to denote if CDROuputStream is
249: * caller (may use cache in this situation)
250: */
251: public synchronized void returnBuffer(byte[] current, boolean cdrStr) {
252: if (current != null) {
253: int log_curr = log2down(current.length);
254:
255: if (log_curr >= MIN_OFFSET) {
256: if (log_curr > MAX) {
257: // Only cache if CDROutputStream is called, cache is enabled &
258: // the new value is > than the cached value.
259: if (cdrStr
260: && (time >= 0 && (bufferMax == null || bufferMax.length < current.length))) {
261: bufferMax = current;
262: }
263: return;
264: }
265:
266: List s = bufferPool[log_curr - MIN_OFFSET];
267: if (s.size() < THRESHOLD) {
268: s.add(current);
269: }
270: }
271: }
272: }
273:
274: public synchronized void release() {
275: // printStatistics();
276: for (int i = MAX; i > 0;) {
277: i--;
278: bufferPool[i].clear();
279: }
280: if (reaper != null) {
281: reaper.dispose();
282: }
283: }
284:
285: private class Reaper extends Thread {
286: private boolean done = false;
287: private int sleepInterval = 0;
288:
289: public Reaper(int sleepInterval) {
290: super ("BufferManagerReaper");
291: // Convert from seconds to milliseconds
292: this .sleepInterval = (sleepInterval * 1000);
293: }
294:
295: public void run() {
296: long time;
297:
298: while (true) {
299: // Sleep (note time check on wake to catch premature awakening bug)
300:
301: try {
302: time = sleepInterval + System.currentTimeMillis();
303: synchronized (this ) {
304: while (!done
305: && System.currentTimeMillis() <= time) {
306: wait(sleepInterval);
307: }
308: }
309: } catch (InterruptedException ex) {
310: // ignored
311: }
312:
313: // Check not shutting down
314:
315: synchronized (this ) {
316: if (done) {
317: break;
318: }
319: }
320:
321: bufferMax = null;
322: }
323: }
324:
325: public synchronized void dispose() {
326: done = true;
327:
328: interrupt();
329:
330: // Only one thread waiting so safe to use notify rather than notifyAll.
331: notify();
332: }
333: }
334: }
|