001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.nio;
019
020import com.google.errorprone.annotations.RestrictedApi;
021import org.apache.hadoop.hbase.io.ByteBuffAllocator;
022import org.apache.hadoop.hbase.io.ByteBuffAllocator.Recycler;
023import org.apache.yetus.audience.InterfaceAudience;
024
025import org.apache.hbase.thirdparty.io.netty.util.AbstractReferenceCounted;
026import org.apache.hbase.thirdparty.io.netty.util.ReferenceCounted;
027import org.apache.hbase.thirdparty.io.netty.util.ResourceLeakDetector;
028import org.apache.hbase.thirdparty.io.netty.util.ResourceLeakDetectorFactory;
029import org.apache.hbase.thirdparty.io.netty.util.ResourceLeakTracker;
030
031/**
032 * Maintain an reference count integer inside to track life cycle of {@link ByteBuff}, if the
033 * reference count become 0, it'll call {@link Recycler#free()} exactly once.
034 */
035@InterfaceAudience.Private
036public class RefCnt extends AbstractReferenceCounted {
037
038  private static final ResourceLeakDetector<RefCnt> detector =
039    ResourceLeakDetectorFactory.instance().newResourceLeakDetector(RefCnt.class);
040  private final Recycler recycler;
041  private final ResourceLeakTracker<RefCnt> leak;
042
043  /**
044   * Create an {@link RefCnt} with an initial reference count = 1. If the reference count become
045   * zero, the recycler will do nothing. Usually, an Heap {@link ByteBuff} will use this kind of
046   * refCnt to track its life cycle, it help to abstract the code path although it's not really
047   * needed to track on heap ByteBuff.
048   */
049  public static RefCnt create() {
050    return new RefCnt(ByteBuffAllocator.NONE);
051  }
052
053  public static RefCnt create(Recycler recycler) {
054    return new RefCnt(recycler);
055  }
056
057  public RefCnt(Recycler recycler) {
058    this.recycler = recycler;
059    this.leak = recycler == ByteBuffAllocator.NONE ? null : detector.track(this);
060  }
061
062  /**
063   * Returns true if this refCnt has a recycler.
064   */
065  public boolean hasRecycler() {
066    return recycler != ByteBuffAllocator.NONE;
067  }
068
069  @Override
070  public ReferenceCounted retain() {
071    maybeRecord();
072    return super.retain();
073  }
074
075  @Override
076  public ReferenceCounted retain(int increment) {
077    maybeRecord();
078    return super.retain(increment);
079  }
080
081  @Override
082  public boolean release() {
083    maybeRecord();
084    return super.release();
085  }
086
087  @Override
088  public boolean release(int decrement) {
089    maybeRecord();
090    return super.release(decrement);
091  }
092
093  @Override
094  protected final void deallocate() {
095    this.recycler.free();
096    if (leak != null) {
097      this.leak.close(this);
098    }
099  }
100
101  @Override
102  public RefCnt touch() {
103    maybeRecord();
104    return this;
105  }
106
107  @Override
108  public final ReferenceCounted touch(Object hint) {
109    maybeRecord(hint);
110    return this;
111  }
112
113  @RestrictedApi(explanation = "Should only be called in tests", link = "",
114      allowedOnPath = ".*/src/test/.*")
115  public Recycler getRecycler() {
116    return recycler;
117  }
118
119  private void maybeRecord() {
120    maybeRecord(null);
121  }
122
123  private void maybeRecord(Object hint) {
124    if (leak != null) {
125      leak.record(hint);
126    }
127  }
128}