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    *   http://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.util.internal.PlatformDependent;
19  
20  /**
21   * Represents an array of kevent structures, backed by offheap memory.
22   *
23   * struct kevent {
24   *  uintptr_t ident;
25   *  short     keventFilter;
26   *  u_short   flags;
27   *  u_int     fflags;
28   *  intptr_t  data;
29   *  void      *udata;
30   * };
31   */
32  final class KQueueEventArray {
33      private static final int KQUEUE_EVENT_SIZE = Native.sizeofKEvent();
34      private static final int KQUEUE_IDENT_OFFSET = Native.offsetofKEventIdent();
35      private static final int KQUEUE_FILTER_OFFSET = Native.offsetofKEventFilter();
36      private static final int KQUEUE_FFLAGS_OFFSET = Native.offsetofKEventFFlags();
37      private static final int KQUEUE_FLAGS_OFFSET = Native.offsetofKEventFlags();
38      private static final int KQUEUE_DATA_OFFSET = Native.offsetofKeventData();
39  
40      private long memoryAddress;
41      private int size;
42      private int capacity;
43  
44      KQueueEventArray(int capacity) {
45          if (capacity < 1) {
46              throw new IllegalArgumentException("capacity must be >= 1 but was " + capacity);
47          }
48          memoryAddress = PlatformDependent.allocateMemory(capacity * KQUEUE_EVENT_SIZE);
49          this.capacity = capacity;
50      }
51  
52      /**
53       * Return the {@code memoryAddress} which points to the start of this {@link KQueueEventArray}.
54       */
55      long memoryAddress() {
56          return memoryAddress;
57      }
58  
59      /**
60       * Return the capacity of the {@link KQueueEventArray} which represent the maximum number of {@code kevent}s
61       * that can be stored in it.
62       */
63      int capacity() {
64          return capacity;
65      }
66  
67      int size() {
68          return size;
69      }
70  
71      void clear() {
72          size = 0;
73      }
74  
75      void evSet(AbstractKQueueChannel ch, short filter, short flags, int fflags) {
76          checkSize();
77          evSet(getKEventOffset(size++), ch, ch.socket.intValue(), filter, flags, fflags);
78      }
79  
80      private void checkSize() {
81          if (size == capacity) {
82              realloc(true);
83          }
84      }
85  
86      /**
87       * Increase the storage of this {@link KQueueEventArray}.
88       */
89      void realloc(boolean throwIfFail) {
90          // Double the capacity while it is "sufficiently small", and otherwise increase by 50%.
91          int newLength = capacity <= 65536 ? capacity << 1 : capacity + capacity >> 1;
92          long newMemoryAddress = PlatformDependent.reallocateMemory(memoryAddress, newLength * KQUEUE_EVENT_SIZE);
93          if (newMemoryAddress != 0) {
94              memoryAddress = newMemoryAddress;
95              capacity = newLength;
96              return;
97          }
98          if (throwIfFail) {
99              throw new OutOfMemoryError("unable to allocate " + newLength + " new bytes! Existing capacity is: "
100                     + capacity);
101         }
102     }
103 
104     /**
105      * Free this {@link KQueueEventArray}. Any usage after calling this method may segfault the JVM!
106      */
107     void free() {
108         PlatformDependent.freeMemory(memoryAddress);
109         memoryAddress = size = capacity = 0;
110     }
111 
112     long getKEventOffset(int index) {
113         return memoryAddress + index * KQUEUE_EVENT_SIZE;
114     }
115 
116     short flags(int index) {
117         return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FLAGS_OFFSET);
118     }
119 
120     short filter(int index) {
121         return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FILTER_OFFSET);
122     }
123 
124     short fflags(int index) {
125         return PlatformDependent.getShort(getKEventOffset(index) + KQUEUE_FFLAGS_OFFSET);
126     }
127 
128     int fd(int index) {
129         return PlatformDependent.getInt(getKEventOffset(index) + KQUEUE_IDENT_OFFSET);
130     }
131 
132     long data(int index) {
133         return PlatformDependent.getLong(getKEventOffset(index) + KQUEUE_DATA_OFFSET);
134     }
135 
136     AbstractKQueueChannel channel(int index) {
137         return getChannel(getKEventOffset(index));
138     }
139 
140     private static native void evSet(long keventAddress, AbstractKQueueChannel ch,
141                                      int ident, short filter, short flags, int fflags);
142     private static native AbstractKQueueChannel getChannel(long keventAddress);
143     static native void deleteGlobalRefs(long channelAddressStart, long channelAddressEnd);
144 }