View Javadoc
1   /*
2    * Copyright 2015 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.netty.channel.epoll;
17  
18  import io.netty.channel.unix.Buffer;
19  import io.netty.util.internal.CleanableDirectBuffer;
20  import io.netty.util.internal.PlatformDependent;
21  import io.netty.util.internal.UnstableApi;
22  
23  import java.nio.ByteBuffer;
24  
25  /**
26   * This is an internal datastructure which can be directly passed to epoll_wait to reduce the overhead.
27   *
28   * typedef union epoll_data {
29   *     void        *ptr;
30   *     int          fd;
31   *     uint32_t     u32;
32   *     uint64_t     u64;
33   * } epoll_data_t;
34   *
35   * struct epoll_event {
36   *     uint32_t     events;      // Epoll events
37   *     epoll_data_t data;        // User data variable
38   * };
39   *
40   * We use {@code fd} if the {@code epoll_data union} to store the actual file descriptor of an
41   * {@link AbstractEpollChannel} and so be able to map it later.
42   */
43  @UnstableApi
44  public final class EpollEventArray {
45      // Size of the epoll_event struct
46      private static final int EPOLL_EVENT_SIZE = Native.sizeofEpollEvent();
47      // The offset of the data union in the epoll_event struct
48      private static final int EPOLL_DATA_OFFSET = Native.offsetofEpollData();
49  
50      private CleanableDirectBuffer cleanable;
51      private ByteBuffer memory;
52      private long memoryAddress;
53      private int length;
54  
55      EpollEventArray(int length) {
56          if (length < 1) {
57              throw new IllegalArgumentException("length must be >= 1 but was " + length);
58          }
59          this.length = length;
60          cleanable = Buffer.allocateDirectBufferWithNativeOrder(calculateBufferCapacity(length));
61          memory = cleanable.buffer();
62          memoryAddress = Buffer.memoryAddress(memory);
63      }
64  
65      /**
66       * Return the {@code memoryAddress} which points to the start of this {@link EpollEventArray}.
67       */
68      long memoryAddress() {
69          return memoryAddress;
70      }
71  
72      /**
73       * Return the length of the {@link EpollEventArray} which represent the maximum number of {@code epoll_events}
74       * that can be stored in it.
75       */
76      int length() {
77          return length;
78      }
79  
80      /**
81       * Increase the storage of this {@link EpollEventArray}.
82       */
83      void increase() {
84          // double the size
85          length <<= 1;
86          // There is no need to preserve what was in the memory before.
87          CleanableDirectBuffer buffer = Buffer.allocateDirectBufferWithNativeOrder(calculateBufferCapacity(length));
88          cleanable.clean();
89          cleanable = buffer;
90          memory = buffer.buffer();
91          memoryAddress = Buffer.memoryAddress(buffer.buffer());
92      }
93  
94      /**
95       * Free this {@link EpollEventArray}. Any usage after calling this method may segfault the JVM!
96       */
97      void free() {
98          cleanable.clean();
99          memoryAddress = 0;
100     }
101 
102     /**
103      * Return the events for the {@code epoll_event} on this index.
104      */
105     int events(int index) {
106         return getInt(index, 0);
107     }
108 
109     /**
110      * Return the file descriptor for the {@code epoll_event} on this index.
111      */
112     int fd(int index) {
113         return getInt(index, EPOLL_DATA_OFFSET);
114     }
115 
116     private int getInt(int index, int offset) {
117         if (PlatformDependent.hasUnsafe()) {
118             long n = (long) index * EPOLL_EVENT_SIZE;
119             return PlatformDependent.getInt(memoryAddress + n + offset);
120         }
121         return memory.getInt(index * EPOLL_EVENT_SIZE + offset);
122     }
123 
124     private static int calculateBufferCapacity(int capacity) {
125         return capacity * EPOLL_EVENT_SIZE;
126     }
127 }