/* * Copyright 2016-present Facebook, Inc. * * Licensed 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 com.facebook.buck.log; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public class ReferenceCountedWriter extends Writer { private final AtomicBoolean hasBeenClosed; private final AtomicInteger counter; private final OutputStreamWriter innerWriter; public ReferenceCountedWriter(OutputStreamWriter innerWriter) { this(new AtomicInteger(1), innerWriter); } private ReferenceCountedWriter(AtomicInteger counter, OutputStreamWriter innerWriter) { this.hasBeenClosed = new AtomicBoolean(false); this.counter = counter; this.innerWriter = innerWriter; } public ReferenceCountedWriter newReference() { if (getAndIncrementIfNotZero(counter) == 0) { throw new RuntimeException("ReferenceCountedWriter is closed!"); } return new ReferenceCountedWriter(counter, innerWriter); } @Override public void write(char[] cbuf, int off, int len) throws IOException { innerWriter.write(cbuf, off, len); } @Override public void flush() throws IOException { innerWriter.flush(); } @Override public void write(int c) throws IOException { innerWriter.write(c); } @Override public void write(char[] cbuf) throws IOException { innerWriter.write(cbuf); } @Override public void write(String str) throws IOException { innerWriter.write(str); } @Override public void write(String str, int off, int len) throws IOException { innerWriter.write(str, off, len); } @Override public Writer append(CharSequence csq) throws IOException { return innerWriter.append(csq); } @Override public Writer append(CharSequence csq, int start, int end) throws IOException { return innerWriter.append(csq, start, end); } @Override public Writer append(char c) throws IOException { return innerWriter.append(c); } @Override public void close() throws IOException { // Avoid decrementing more than once from the same ReferenceCounted instance. if (hasBeenClosed.getAndSet(true)) { return; } int currentCount = counter.decrementAndGet(); if (currentCount == 0) { innerWriter.close(); } else { // Close implies flush, so if we're not actually closing we should at least flush innerWriter.flush(); } } private static int getAndIncrementIfNotZero(AtomicInteger counter) { for (; ; ) { int current = counter.get(); if (current == 0) { return 0; } int next = current + 1; if (counter.compareAndSet(current, next)) { return current; } } } }