1/*
2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * KQueueArrayWrapper.java
28 * Implementation of Selector using FreeBSD / Mac OS X kqueues
29 * Derived from Sun's DevPollArrayWrapper
30 */
31
32package sun.nio.ch;
33
34import java.io.IOException;
35import java.util.Iterator;
36import java.util.LinkedList;
37import sun.security.action.GetPropertyAction;
38
39/*
40 * struct kevent {           // 32-bit    64-bit
41 *     uintptr_t ident;      //   4         8
42 *     short     filter;     //   2         2
43 *     u_short   flags;      //   2         2
44 *     u_int     fflags;     //   4         4
45 *     intptr_t  data;       //   4         8
46 *     void      *udata;     //   4         8
47 * }                  // Total:  20        32
48 *
49 * The implementation works in 32-bit and 64-bit world. We do this by calling a
50 * native function that actually sets the sizes and offsets of the fields based
51 * on which mode we're in.
52 */
53
54class KQueueArrayWrapper {
55    // kevent filters
56    static short EVFILT_READ;
57    static short EVFILT_WRITE;
58
59    // kevent struct
60    // These fields are now set by initStructSizes in the static initializer.
61    static short SIZEOF_KEVENT;
62    static short FD_OFFSET;
63    static short FILTER_OFFSET;
64
65    // kevent array size
66    static final int NUM_KEVENTS = 128;
67
68    // Are we in a 64-bit VM?
69    static boolean is64bit = false;
70
71    // The kevent array (used for outcoming events only)
72    private AllocatedNativeObject keventArray = null;
73    private long keventArrayAddress;
74
75    // The kqueue fd
76    private int kq = -1;
77
78    // The fd of the interrupt line going out
79    private int outgoingInterruptFD;
80
81    // The fd of the interrupt line coming in
82    private int incomingInterruptFD;
83
84    static {
85        IOUtil.load();
86        initStructSizes();
87        String datamodel =
88                GetPropertyAction.privilegedGetProperty("sun.arch.data.model");
89        is64bit = "64".equals(datamodel);
90    }
91
92    KQueueArrayWrapper() {
93        int allocationSize = SIZEOF_KEVENT * NUM_KEVENTS;
94        keventArray = new AllocatedNativeObject(allocationSize, true);
95        keventArrayAddress = keventArray.address();
96        kq = init();
97    }
98
99    // Used to update file description registrations
100    private static class Update {
101        SelChImpl channel;
102        int events;
103        Update(SelChImpl channel, int events) {
104            this.channel = channel;
105            this.events = events;
106        }
107    }
108
109    private LinkedList<Update> updateList = new LinkedList<Update>();
110
111    void initInterrupt(int fd0, int fd1) {
112        outgoingInterruptFD = fd1;
113        incomingInterruptFD = fd0;
114        register0(kq, fd0, 1, 0);
115    }
116
117    int getReventOps(int index) {
118        int result = 0;
119        int offset = SIZEOF_KEVENT*index + FILTER_OFFSET;
120        short filter = keventArray.getShort(offset);
121
122        // This is all that's necessary based on inspection of usage:
123        //   SinkChannelImpl, SourceChannelImpl, DatagramChannelImpl,
124        //   ServerSocketChannelImpl, SocketChannelImpl
125        if (filter == EVFILT_READ) {
126            result |= Net.POLLIN;
127        } else if (filter == EVFILT_WRITE) {
128            result |= Net.POLLOUT;
129        }
130
131        return result;
132    }
133
134    int getDescriptor(int index) {
135        int offset = SIZEOF_KEVENT*index + FD_OFFSET;
136        /* The ident field is 8 bytes in 64-bit world, however the API wants us
137         * to return an int. Hence read the 8 bytes but return as an int.
138         */
139        if (is64bit) {
140          long fd = keventArray.getLong(offset);
141          assert fd <= Integer.MAX_VALUE;
142          return (int) fd;
143        } else {
144          return keventArray.getInt(offset);
145        }
146    }
147
148    void setInterest(SelChImpl channel, int events) {
149        synchronized (updateList) {
150            // update existing registration
151            updateList.add(new Update(channel, events));
152        }
153    }
154
155    void release(SelChImpl channel) {
156        synchronized (updateList) {
157            // flush any pending updates
158            for (Iterator<Update> it = updateList.iterator(); it.hasNext();) {
159                if (it.next().channel == channel) {
160                    it.remove();
161                }
162            }
163
164            // remove
165            register0(kq, channel.getFDVal(), 0, 0);
166        }
167    }
168
169    void updateRegistrations() {
170        synchronized (updateList) {
171            Update u = null;
172            while ((u = updateList.poll()) != null) {
173                SelChImpl ch = u.channel;
174                if (!ch.isOpen())
175                    continue;
176
177                register0(kq, ch.getFDVal(), u.events & Net.POLLIN, u.events & Net.POLLOUT);
178            }
179        }
180    }
181
182
183    void close() throws IOException {
184        if (keventArray != null) {
185            keventArray.free();
186            keventArray = null;
187        }
188        if (kq >= 0) {
189            FileDispatcherImpl.closeIntFD(kq);
190            kq = -1;
191        }
192    }
193
194    int poll(long timeout) {
195        updateRegistrations();
196        int updated = kevent0(kq, keventArrayAddress, NUM_KEVENTS, timeout);
197        return updated;
198    }
199
200    void interrupt() {
201        interrupt(outgoingInterruptFD);
202    }
203
204    private native int init();
205    private static native void initStructSizes();
206
207    private native void register0(int kq, int fd, int read, int write);
208    private native int kevent0(int kq, long keventAddress, int keventCount,
209                               long timeout);
210    private static native void interrupt(int fd);
211}
212