View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.util;
17  
18  /**
19   * A resource that has a life-time, and can be {@linkplain #close() closed}.
20   * Resources are initially {@linkplain #isAccessible() accessible}, but closing them makes them inaccessible.
21   */
22  public interface Resource<T extends Resource<T>> extends AutoCloseable {
23      /**
24       * Send this object instance to another Thread, transferring the ownership to the recipient.
25       * <p>
26       * The object must be in a state where it can be sent, which includes at least being
27       * {@linkplain #isAccessible() accessible}.
28       * <p>
29       * When sent, this instance will immediately become inaccessible, as if by {@linkplain #close() closing} it.
30       * All attempts at accessing an object that has been sent, even if that object has not yet been received, should
31       * cause an exception to be thrown.
32       * <p>
33       * Calling {@link #close()} on an object that has been sent will have no effect, so this method is safe to call
34       * within a try-with-resources statement.
35       */
36      Send<T> send();
37  
38      /**
39       * Close the resource, making it inaccessible.
40       * <p>
41       * Note, this method is not thread-safe unless otherwise specified.
42       *
43       * @throws IllegalStateException If this {@code Resource} has already been closed.
44       */
45      @Override
46      void close();
47  
48      /**
49       * Check if this object is accessible.
50       *
51       * @return {@code true} if this object is still valid and can be accessed,
52       * otherwise {@code false} if, for instance, this object has been dropped/deallocated,
53       * or been {@linkplain #send() sent} elsewhere.
54       */
55      boolean isAccessible();
56  
57      /**
58       * Record the current access location for debugging purposes.
59       * This information may be included if the resource throws a life-cycle related exception, or if it leaks.
60       * If this resource has already been closed, then this method has no effect.
61       *
62       * @param hint An optional hint about this access and its context. May be {@code null}.
63       * @return This resource instance.
64       */
65      @SuppressWarnings("unchecked")
66      default T touch(Object hint) {
67          return (T) this;
68      }
69  
70      /**
71       * Attempt to dispose of whatever the given object is.
72       * <p>
73       * If the object is {@link AutoCloseable}, such as anything that implements {@link Resource},
74       * then it will be closed.
75       * If the object is {@link ReferenceCounted}, then it will be released once.
76       * <p>
77       * Any exceptions caused by this will be left to bubble up, and checked exceptions will be wrapped in a
78       * {@link RuntimeException}.
79       *
80       * @param obj The object to dispose of.
81       */
82      static void dispose(Object obj) {
83          if (obj instanceof AutoCloseable) {
84              try {
85                  ((AutoCloseable) obj).close();
86              } catch (RuntimeException re) {
87                  throw re;
88              } catch (Exception e) {
89                  throw new RuntimeException("Exception from closing object", e);
90              }
91          } else if (ReferenceCountUtil.isReferenceCounted(obj)) {
92              ReferenceCountUtil.release(obj);
93          }
94      }
95  
96      /**
97       * Check if an object is accessible. This returns {@code true} if the object is a {@linkplain Resource resource}
98       * that {@linkplain Resource#isAccessible() is accessible}, or if the object is
99       * {@linkplain ReferenceCounted reference counted} and has a positive
100      * {@linkplain ReferenceCounted#refCnt() reference count}.
101      * <p>
102      * If the object is neither of these types, then the expected default value is returned.
103      *
104      * @param obj The object to check.
105      * @param expectedDefault The value to return if the object is neither a resource, nor reference counted.
106      * @return If the object is accessible.
107      */
108     static boolean isAccessible(Object obj, boolean expectedDefault) {
109         if (obj instanceof Resource) {
110             return ((Resource<?>) obj).isAccessible();
111         }
112         if (obj instanceof ReferenceCounted) {
113             return ((ReferenceCounted) obj).refCnt() > 0;
114         }
115         return expectedDefault;
116     }
117 
118     /**
119      * Record the current access location for debugging purposes.
120      * This information may be included if the resource throws a life-cycle related exception, or if it leaks.
121      * If this resource has already been closed, then this method has no effect.
122      * <p>
123      * If the given object is not a resource, or not reference counted, then this method has no effect.
124      *
125      * @param obj The object to annotate.
126      * @param hint An optional hint about this access and its context. May be {@code null}.
127      */
128     static void touch(Object obj, Object hint) {
129         if (obj instanceof Resource) {
130             ((Resource<?>) obj).touch(hint);
131         } else if (obj instanceof ReferenceCounted) {
132             ((ReferenceCounted) obj).touch(hint);
133         }
134     }
135 }