1/*
2 * Copyright (c) 2001, 2012, 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
26package sun.nio.ch;
27
28import java.io.IOException;
29import java.util.BitSet;
30import java.util.Map;
31import java.util.HashMap;
32
33
34/**
35 * Manipulates a native array of pollfd structs on Solaris:
36 *
37 * typedef struct pollfd {
38 *    int fd;
39 *    short events;
40 *    short revents;
41 * } pollfd_t;
42 *
43 * @author Mike McCloskey
44 * @since 1.4
45 */
46
47class DevPollArrayWrapper {
48
49    // Event masks
50    static final short POLLIN       = 0x0001;
51    static final short POLLPRI      = 0x0002;
52    static final short POLLOUT      = 0x0004;
53    static final short POLLRDNORM   = 0x0040;
54    static final short POLLWRNORM   = POLLOUT;
55    static final short POLLRDBAND   = 0x0080;
56    static final short POLLWRBAND   = 0x0100;
57    static final short POLLNORM     = POLLRDNORM;
58    static final short POLLERR      = 0x0008;
59    static final short POLLHUP      = 0x0010;
60    static final short POLLNVAL     = 0x0020;
61    static final short POLLREMOVE   = 0x0800;
62    static final short POLLCONN     = POLLOUT;
63
64    // Miscellaneous constants
65    static final short SIZE_POLLFD   = 8;
66    static final short FD_OFFSET     = 0;
67    static final short EVENT_OFFSET  = 4;
68    static final short REVENT_OFFSET = 6;
69
70    // Special value to indicate that an update should be ignored
71    static final byte  IGNORE        = (byte)-1;
72
73    // Maximum number of open file descriptors
74    static final int   OPEN_MAX      = IOUtil.fdLimit();
75
76    // Number of pollfd structures to create.
77    // dpwrite/ioctl(DP_POLL) allows up to OPEN_MAX-1
78    static final int   NUM_POLLFDS   = Math.min(OPEN_MAX-1, 8192);
79
80    // Initial size of arrays for fd registration changes
81    private final int INITIAL_PENDING_UPDATE_SIZE = 64;
82
83    // maximum size of updatesLow
84    private final int MAX_UPDATE_ARRAY_SIZE = Math.min(OPEN_MAX, 64*1024);
85
86    // The pollfd array for results from devpoll driver
87    private final AllocatedNativeObject pollArray;
88
89    // Base address of the native pollArray
90    private final long pollArrayAddress;
91
92    // The fd of the devpoll driver
93    private int wfd;
94
95    // The fd of the interrupt line going out
96    private int outgoingInterruptFD;
97
98    // The fd of the interrupt line coming in
99    private int incomingInterruptFD;
100
101    // The index of the interrupt FD
102    private int interruptedIndex;
103
104    // Number of updated pollfd entries
105    int updated;
106
107    // object to synchronize fd registration changes
108    private final Object updateLock = new Object();
109
110    // number of file descriptors with registration changes pending
111    private int updateCount;
112
113    // file descriptors with registration changes pending
114    private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
115
116    // events for file descriptors with registration changes pending, indexed
117    // by file descriptor and stored as bytes for efficiency reasons. For
118    // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
119    // least then the update is stored in a map.
120    private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
121    private Map<Integer,Byte> eventsHigh;
122
123    // Used by release and updateRegistrations to track whether a file
124    // descriptor is registered with /dev/poll.
125    private final BitSet registered = new BitSet();
126
127    DevPollArrayWrapper() {
128        int allocationSize = NUM_POLLFDS * SIZE_POLLFD;
129        pollArray = new AllocatedNativeObject(allocationSize, true);
130        pollArrayAddress = pollArray.address();
131        wfd = init();
132        if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
133            eventsHigh = new HashMap<>();
134    }
135
136    void initInterrupt(int fd0, int fd1) {
137        outgoingInterruptFD = fd1;
138        incomingInterruptFD = fd0;
139        register(wfd, fd0, POLLIN);
140    }
141
142    void putReventOps(int i, int revent) {
143        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
144        pollArray.putShort(offset, (short)revent);
145    }
146
147    int getEventOps(int i) {
148        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
149        return pollArray.getShort(offset);
150    }
151
152    int getReventOps(int i) {
153        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
154        return pollArray.getShort(offset);
155    }
156
157    int getDescriptor(int i) {
158        int offset = SIZE_POLLFD * i + FD_OFFSET;
159        return pollArray.getInt(offset);
160    }
161
162    private void setUpdateEvents(int fd, byte events) {
163        if (fd < MAX_UPDATE_ARRAY_SIZE) {
164            eventsLow[fd] = events;
165        } else {
166            eventsHigh.put(Integer.valueOf(fd), Byte.valueOf(events));
167        }
168    }
169
170    private byte getUpdateEvents(int fd) {
171        if (fd < MAX_UPDATE_ARRAY_SIZE) {
172            return eventsLow[fd];
173        } else {
174            Byte result = eventsHigh.get(Integer.valueOf(fd));
175            // result should never be null
176            return result.byteValue();
177        }
178    }
179
180    void setInterest(int fd, int mask) {
181        synchronized (updateLock) {
182            // record the file descriptor and events, expanding the
183            // respective arrays first if necessary.
184            int oldCapacity = updateDescriptors.length;
185            if (updateCount == oldCapacity) {
186                int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
187                int[] newDescriptors = new int[newCapacity];
188                System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
189                updateDescriptors = newDescriptors;
190            }
191            updateDescriptors[updateCount++] = fd;
192
193            // events are stored as bytes for efficiency reasons
194            byte b = (byte)mask;
195            assert (b == mask) && (b != IGNORE);
196            setUpdateEvents(fd, b);
197        }
198    }
199
200    void release(int fd) {
201        synchronized (updateLock) {
202            // ignore any pending update for this file descriptor
203            setUpdateEvents(fd, IGNORE);
204
205            // remove from /dev/poll
206            if (registered.get(fd)) {
207                register(wfd, fd, POLLREMOVE);
208                registered.clear(fd);
209            }
210        }
211    }
212
213    void closeDevPollFD() throws IOException {
214        FileDispatcherImpl.closeIntFD(wfd);
215        pollArray.free();
216    }
217
218    int poll(long timeout) throws IOException {
219        updateRegistrations();
220        updated = poll0(pollArrayAddress, NUM_POLLFDS, timeout, wfd);
221        for (int i=0; i<updated; i++) {
222            if (getDescriptor(i) == incomingInterruptFD) {
223                interruptedIndex = i;
224                interrupted = true;
225                break;
226            }
227        }
228        return updated;
229    }
230
231    void updateRegistrations() throws IOException {
232        synchronized (updateLock) {
233            // Populate pollfd array with updated masks
234            int j = 0;
235            int index = 0;
236            while (j < updateCount) {
237                int fd = updateDescriptors[j];
238                short events = getUpdateEvents(fd);
239                boolean wasRegistered = registered.get(fd);
240
241                // events = 0 => POLLREMOVE or do-nothing
242                if (events != IGNORE) {
243                    if (events == 0) {
244                        if (wasRegistered) {
245                            events = POLLREMOVE;
246                            registered.clear(fd);
247                        } else {
248                            events = IGNORE;
249                        }
250                    } else {
251                        if (!wasRegistered) {
252                            registered.set(fd);
253                        }
254                    }
255                }
256
257                // populate pollfd array with updated event
258                if (events != IGNORE) {
259                    // insert POLLREMOVE if changing events
260                    if (wasRegistered && events != POLLREMOVE) {
261                        putPollFD(pollArray, index, fd, POLLREMOVE);
262                        index++;
263                    }
264                    putPollFD(pollArray, index, fd, events);
265                    index++;
266                    if (index >= (NUM_POLLFDS-1)) {
267                        registerMultiple(wfd, pollArray.address(), index);
268                        index = 0;
269                    }
270
271                    // events for this fd now up to date
272                    setUpdateEvents(fd, IGNORE);
273                }
274                j++;
275            }
276
277            // write any remaining updates
278            if (index > 0)
279                registerMultiple(wfd, pollArray.address(), index);
280
281            updateCount = 0;
282        }
283    }
284
285    private void putPollFD(AllocatedNativeObject array, int index, int fd,
286                           short event)
287    {
288        int structIndex = SIZE_POLLFD * index;
289        array.putInt(structIndex + FD_OFFSET, fd);
290        array.putShort(structIndex + EVENT_OFFSET, event);
291        array.putShort(structIndex + REVENT_OFFSET, (short)0);
292    }
293
294    boolean interrupted = false;
295
296    public void interrupt() {
297        interrupt(outgoingInterruptFD);
298    }
299
300    public int interruptedIndex() {
301        return interruptedIndex;
302    }
303
304    boolean interrupted() {
305        return interrupted;
306    }
307
308    void clearInterrupted() {
309        interrupted = false;
310    }
311
312    private native int init();
313    private native void register(int wfd, int fd, int mask);
314    private native void registerMultiple(int wfd, long address, int len)
315        throws IOException;
316    private native int poll0(long pollAddress, int numfds, long timeout,
317                             int wfd);
318    private static native void interrupt(int fd);
319}
320