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 }