/* * Licensed to ElasticSearch and Shay Banon under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. ElasticSearch licenses this * file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.fastcatsearch.common.io; import java.lang.ref.SoftReference; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import org.fastcatsearch.common.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ public class BlockingCachedStreamOutput { protected static Logger logger = LoggerFactory.getLogger(BlockingCachedStreamOutput.class); private final int defaultBufferSize; private final int maxSize; private final SoftWrapper<BlockingQueue<Entry>> cache; private final AtomicInteger totalCount = new AtomicInteger(); private final AtomicInteger inPoolCount = new AtomicInteger(); private final AtomicInteger outPoolCount = new AtomicInteger(); //100m, 3m+4k public BlockingCachedStreamOutput(int maxSize, int defaultBufferSize){ this.maxSize = maxSize; this.defaultBufferSize = defaultBufferSize; cache = new SoftWrapper<BlockingQueue<Entry>>(); initCache(); } private BlockingQueue<Entry> initCache(){ logger.debug("initCache maxSize={}", maxSize); BlockingQueue<Entry> ref = new LinkedBlockingQueue<Entry>(maxSize); cache.set(ref); inPoolCount.set(0); //발급해준 버퍼는 아직 유효하므로 총 갯수는 발급한 버퍼갯수이다. totalCount.set(outPoolCount.get()); logger.debug("{}", toString()); return ref; } public int totalCount(){ return totalCount.get(); } @Override public String toString(){ return "[Cache]mem="+Strings.getHumanReadableByteSize(defaultBufferSize * totalCount.get())+", totalCount="+totalCount.get()+", inPoolCount="+inPoolCount.get()+", outPoolCount="+outPoolCount.get(); } private synchronized Entry newEntry() { if(totalCount.get() < maxSize){ logger.debug("make newEntry totalCount={}", totalCount.get()); totalCount.incrementAndGet(); BytesStreamOutput bytes = new BytesStreamOutput(defaultBufferSize); return new Entry(bytes); } return null; } public void clear() { cache.clear(); } // static AtomicInteger c = new AtomicInteger(); // static AtomicInteger r = new AtomicInteger(); public Entry popEntry() { // logger.debug("{}", toString()); BlockingQueue<Entry> ref = cache.get(); if (ref == null) { ref = initCache(); } Entry entry = null; while(entry == null){ try { entry = ref.poll(); //가용한 버퍼는 없으나, max갯수까지 여유가 있을 경우는 생성. if(entry == null && totalCount.get() < maxSize){ entry = newEntry(); } //생성을 못했을 경우 take. if(entry == null){ // logger.debug("# Take wait!!"); entry = ref.take(); // logger.debug("# Take Done!!"); entry.reset(); inPoolCount.decrementAndGet(); } if(entry != null){ outPoolCount.incrementAndGet(); } } catch (InterruptedException e) { logger.debug("interrupt while take"); } } return entry; } public void pushEntry(Entry entry) { entry.reset(); BlockingQueue<Entry> ref = cache.get(); if (ref == null) { ref = initCache(); } //동기화에 문제가 없다면 항상 offer에 성공해야한다. if(ref.offer(entry)){ inPoolCount.incrementAndGet(); } outPoolCount.decrementAndGet(); } public static class Entry { private final BytesStreamOutput bytes; Entry(BytesStreamOutput bytes) { this.bytes = bytes; } public void reset() { bytes.reset(); } public BytesStreamOutput bytes() { return bytes; } } static class SoftWrapper<T> { private SoftReference<T> ref; public SoftWrapper() { } public void set(T ref) { this.ref = new SoftReference<T>(ref); } public T get() { return ref == null ? null : ref.get(); } public void clear() { ref = null; } } }