View Javadoc
1   /*
2    * Copyright 2016 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.kqueue;
17  
18  import io.netty.channel.unix.Buffer;
19  import io.netty.util.internal.PlatformDependent;
20  
21  import java.nio.ByteBuffer;
22  
23  /**
24   * Represents an array of kevent structures, backed by offheap memory.
25   *
26   * <pre>
27   * struct kevent {
28   *  uintptr_t ident;
29   *  short     keventFilter;
30   *  u_short   flags;
31   *  u_int     fflags;
32   *  intptr_t  data;
33   *  void      *udata;
34   * };
35   * </pre>
36   */
37  final class KQueueEventArray {
38      private static final int KQUEUE_EVENT_SIZE = Native.sizeofKEvent();
39      private static final int KQUEUE_IDENT_OFFSET = Native.offsetofKEventIdent();
40      private static final int KQUEUE_FILTER_OFFSET = Native.offsetofKEventFilter();
41      private static final int KQUEUE_FFLAGS_OFFSET = Native.offsetofKEventFFlags();
42      private static final int KQUEUE_FLAGS_OFFSET = Native.offsetofKEventFlags();
43      private static final int KQUEUE_DATA_OFFSET = Native.offsetofKeventData();
44      private static final int KQUEUE_UDATA_OFFSET = Native.offsetofKeventUdata();
45  
46      private ByteBuffer memory;
47      private long memoryAddress;
48      private int size;
49      private int capacity;
50  
51      KQueueEventArray(int capacity) {
52          if (capacity < 1) {
53              throw new IllegalArgumentException("capacity must be >= 1 but was " + capacity);
54          }
55          memory = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(capacity));
56          memoryAddress = Buffer.memoryAddress(memory);
57          this.capacity = capacity;
58      }
59  
60      /**
61       * Return the {@code memoryAddress} which points to the start of this {@link KQueueEventArray}.
62       */
63      long memoryAddress() {
64          return memoryAddress;
65      }
66  
67      /**
68       * Return the capacity of the {@link KQueueEventArray} which represent the maximum number of {@code kevent}s
69       * that can be stored in it.
70       */
71      int capacity() {
72          return capacity;
73      }
74  
75      int size() {
76          return size;
77      }
78  
79      void clear() {
80          size = 0;
81      }
82  
83      void evSet(int ident, short filter, short flags, int fflags, long data, long udata) {
84          reallocIfNeeded();
85          evSet(getKEventOffset(size++) + memoryAddress, ident, filter, flags, fflags, data, udata);
86      }
87  
88      private void reallocIfNeeded() {
89          if (size == capacity) {
90              realloc(true);
91          }
92      }
93  
94      /**
95       * Increase the storage of this {@link KQueueEventArray}.
96       */
97      void realloc(boolean throwIfFail) {
98          // Double the capacity while it is "sufficiently small", and otherwise increase by 50%.
99          int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1;
100 
101         try {
102             ByteBuffer buffer = Buffer.allocateDirectWithNativeOrder(calculateBufferCapacity(newLength));
103             // Copy over the old content of the memory and reset the position as we always act on the buffer as if
104             // the position was never increased.
105             memory.position(0).limit(size);
106             buffer.put(memory);
107             buffer.position(0);
108 
109             Buffer.free(memory);
110             memory = buffer;
111             memoryAddress = Buffer.memoryAddress(buffer);
112         } catch (OutOfMemoryError e) {
113             if (throwIfFail) {
114                 OutOfMemoryError error = new OutOfMemoryError(
115                         "unable to allocate " + newLength + " new bytes! Existing capacity is: " + capacity);
116                 error.initCause(e);
117                 throw error;
118             }
119         }
120     }
121 
122     /**
123      * Free this {@link KQueueEventArray}. Any usage after calling this method may segfault the JVM!
124      */
125     void free() {
126         Buffer.free(memory);
127         memoryAddress = size = capacity = 0;
128     }
129 
130     private static int getKEventOffset(int index) {
131         return index * KQUEUE_EVENT_SIZE;
132     }
133 
134     private long getKEventOffsetAddress(int index) {
135         return getKEventOffset(index) + memoryAddress;
136     }
137 
138     private short getShort(int index, int offset) {
139         if (PlatformDependent.hasUnsafe()) {
140             return PlatformDependent.getShort(getKEventOffsetAddress(index) + offset);
141         }
142         return memory.getShort(getKEventOffset(index) + offset);
143     }
144 
145     short flags(int index) {
146         return getShort(index, KQUEUE_FLAGS_OFFSET);
147     }
148 
149     short filter(int index) {
150         return getShort(index, KQUEUE_FILTER_OFFSET);
151     }
152 
153     short fflags(int index) {
154         return getShort(index, KQUEUE_FFLAGS_OFFSET);
155     }
156 
157     int ident(int index) {
158         if (PlatformDependent.hasUnsafe()) {
159             return PlatformDependent.getInt(getKEventOffsetAddress(index) + KQUEUE_IDENT_OFFSET);
160         }
161         return memory.getInt(getKEventOffset(index) + KQUEUE_IDENT_OFFSET);
162     }
163 
164     long data(int index) {
165         return getLong(index, KQUEUE_DATA_OFFSET);
166     }
167 
168     long udata(int index) {
169         return getLong(index, KQUEUE_UDATA_OFFSET);
170     }
171 
172     private long getLong(int index, int offset) {
173         if (PlatformDependent.hasUnsafe()) {
174             return PlatformDependent.getLong(getKEventOffsetAddress(index) + offset);
175         }
176         return memory.getLong(getKEventOffset(index) + offset);
177     }
178 
179     private static int calculateBufferCapacity(int capacity) {
180         return capacity * KQUEUE_EVENT_SIZE;
181     }
182 
183     private static native void evSet(
184             long keventAddress, int ident, short filter, short flags, int fflags, long data, long udata);
185 }