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