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.buffer.api;
17
18 import io.netty5.buffer.api.internal.ResourceSupport;
19 import io.netty5.buffer.api.internal.Statics;
20 import io.netty5.util.Resource;
21 import io.netty5.util.Send;
22
23 import java.lang.invoke.VarHandle;
24 import java.util.Objects;
25
26 import static java.lang.invoke.MethodHandles.lookup;
27
28 /**
29 * The {@link BufferHolder} is an abstract class that simplifies the implementation of objects that themselves contain
30 * a {@link Buffer} instance.
31 * <p>
32 * The {@link BufferHolder} can only hold on to a single buffer, so objects and classes that need to hold on to multiple
33 * buffers will have to do their implementation from scratch, though they can use the code of the {@link BufferHolder}
34 * as inspiration. Alternatively, multiple buffers can be
35 * {@linkplain BufferAllocator#compose(Iterable) composed} into a single buffer, which can then be put
36 * in a buffer holder.
37 * <p>
38 * If you just want an object that is a reference to a buffer, then the {@link BufferRef} can be used for that purpose.
39 * If you have an advanced use case where you wish to implement {@link Resource}, and tightly control lifetimes, then
40 * {@link ResourceSupport} can be of help.
41 *
42 * @param <T> The concrete {@link BufferHolder} type.
43 */
44 public abstract class BufferHolder<T extends Resource<T>> implements Resource<T> {
45 private static final VarHandle BUF = Statics.findVarHandle(lookup(), BufferHolder.class, "buf", Buffer.class);
46 private Buffer buf;
47
48 /**
49 * Create a new {@link BufferHolder} to hold the given {@linkplain Buffer buffer}.
50 *
51 * @param buf The {@linkplain Buffer buffer} to be held by this holder.
52 */
53 protected BufferHolder(Buffer buf) {
54 this.buf = Objects.requireNonNull(buf, "The buffer cannot be null.");
55 }
56
57 /**
58 * Create a new {@link BufferHolder} to hold the {@linkplain Buffer buffer} received from the given {@link Send}.
59 * <p>
60 * The {@link BufferHolder} will then be holding exclusive ownership of the buffer.
61 *
62 * @param send The {@linkplain Buffer buffer} to be held by this holder.
63 */
64 protected BufferHolder(Send<Buffer> send) {
65 buf = Objects.requireNonNull(send, "The Send-object cannot be null.").receive();
66 }
67
68 @Override
69 public void close() {
70 buf.close();
71 }
72
73 @SuppressWarnings("unchecked")
74 @Override
75 public Send<T> send() {
76 return buf.send().map((Class<T>) getClass(), this::receive);
77 }
78
79 /**
80 * Called when a {@linkplain #send() sent} {@link BufferHolder} is received by the recipient.
81 * The {@link BufferHolder} should return a new concrete instance, that wraps the given {@link Buffer} object.
82 *
83 * @param buf The {@link Buffer} that is {@linkplain Send#receive() received} by the recipient,
84 * and needs to be wrapped in a new {@link BufferHolder} instance.
85 * @return A new {@linkplain T buffer holder} instance, containing the given {@linkplain Buffer buffer}.
86 */
87 protected abstract T receive(Buffer buf);
88
89 /**
90 * Replace the underlying referenced buffer with the given buffer.
91 * <p>
92 * This method is protected to permit advanced use cases of {@link BufferHolder} sub-class implementations.
93 * <p>
94 * <strong>Note:</strong> This method closes the current buffer,
95 * and takes exclusive ownership of the received buffer.
96 * <p>
97 * The buffer assignment is performed using a plain store.
98 *
99 * @param send The new {@link Buffer} instance that is replacing the currently held buffer.
100 */
101 protected final void replaceBuffer(Send<Buffer> send) {
102 Buffer received = send.receive();
103 buf.close();
104 buf = received;
105 }
106
107 /**
108 * Replace the underlying referenced buffer with the given buffer.
109 * <p>
110 * This method is protected to permit advanced use cases of {@link BufferHolder} sub-class implementations.
111 * <p>
112 * <strong>Note:</strong> this method closes the current buffer,
113 * and takes exclusive ownership of the received buffer.
114 * <p>
115 * The buffer assignment is performed using a volatile store.
116 *
117 * @param send The {@link Send} with the new {@link Buffer} instance that is replacing the currently held buffer.
118 */
119 protected final void replaceBufferVolatile(Send<Buffer> send) {
120 Buffer received = send.receive();
121 var prev = (Buffer) BUF.getAndSet(this, received);
122 prev.close();
123 }
124
125 /**
126 * Access the held {@link Buffer} instance.
127 * <p>
128 * The access is performed using a plain load.
129 *
130 * @return The {@link Buffer} instance being held by this {@linkplain T buffer holder}.
131 */
132 protected final Buffer getBuffer() {
133 return buf;
134 }
135
136 /**
137 * Access the held {@link Buffer} instance.
138 * <p>
139 * The access is performed using a volatile load.
140 *
141 * @return The {@link Buffer} instance being held by this {@linkplain T buffer holder}.
142 */
143 protected final Buffer getBufferVolatile() {
144 return (Buffer) BUF.getVolatile(this);
145 }
146
147 @Override
148 public boolean isAccessible() {
149 return buf.isAccessible();
150 }
151
152 @SuppressWarnings("unchecked")
153 @Override
154 public T touch(Object hint) {
155 buf.touch(hint);
156 return (T) this;
157 }
158
159 /**
160 * This implementation of the {@code equals} operation is restricted to work only with instances of the same class.
161 * The reason for that is that Netty library already has a number of classes that extend {@link BufferHolder} and
162 * override {@code equals} method with an additional comparison logic, and we need the symmetric property of the
163 * {@code equals} operation to be preserved.
164 *
165 * @param other The reference object with which to compare.
166 * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
167 */
168 @Override
169 public boolean equals(Object other) {
170 if (this == other) {
171 return true;
172 }
173 if (other != null && getClass() == other.getClass()) {
174 return buf.equals(((BufferHolder<?>) other).buf);
175 }
176 return false;
177 }
178
179 @Override
180 public int hashCode() {
181 int result = getClass().hashCode();
182 result *= 31 + buf.hashCode();
183 return result;
184 }
185 }