aboutsummaryrefslogtreecommitdiff
path: root/src/solaris/demo/jni/Poller/Poller.java
blob: 8b8d6af863166dac46bfda7e3c27d8fd195c2aa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/*
 * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This source code is provided to illustrate the usage of a given feature
 * or technique and has been deliberately simplified. Additional steps
 * required for a production-quality application, such as security checks,
 * input validation and proper error handling, might not be present in
 * this sample code.
 */


import java.lang.reflect.*;
import java.io.*;
import java.net.*;

/**
 * This class is provided for access to the underlying poll(2)
 * or /dev/poll kernel interfaces.  This may be needed for
 * multiplexing IO when an application cannot afford to have
 * a thread block on each outstanding IO request.
 *
 * It currently supports the same basic functionality as the
 * C poll(2) API, although for efficiency we needed to avoid
 * passing the entire pollfd array for every call.  See man
 * pages for poll(2) for info on C API and event types.
 *
 *
 * @author  Bruce Chapman
 * @see     java.io.FileDescriptor
 * @see     java.net.Socket
 * @see     attached README.txt
 * @since   JDK1.2
 */

public class Poller {
  /**
   * Solaris POLL event types.
   */
  public final static short POLLERR  = 0x08;
  public final static short POLLHUP  = 0x10;
  public final static short POLLNVAL = 0x20;
  public final static short POLLIN   = 1;
  public final static short POLLPRI  = 2;
  public final static short POLLOUT  = 4;
  public final static short POLLRDNORM = 0x40;
  public final static short POLLWRNORM = POLLOUT ;
  public final static short POLLRDBAND = 0x80;
  public final static short POLLWRBAND = 0x100;
  public final static short POLLNORM   = POLLRDNORM;

  /*
   * This global synchronization object must be used for all
   * creation or destruction of Poller objects.
   */
  private final static Object globalSync = new Object();

  /*
   * The handle for a Poller Object...is used in the JNI C code
   * where all the associated data is kept.
   */
  private int handle;

  /**
   * Constructs an instance of a <code>Poller</code> object.
   * Native code uses sysconf(_SC_OPEN_MAX) to determine how
   * many fd/skt objects this Poller object can contain.
   */
  public Poller() throws Exception {
    synchronized(globalSync) {
      this.handle = nativeCreatePoller(-1);
    }
  }

  /**
   * Constructs an instance of a <code>Poller</code> object.
   * @param  maxFd the maximum number of FileDescriptors/Sockets
   *         this Poller object can contain.
   */
  public Poller(int maxFd) throws Exception {
    synchronized(globalSync) {
      this.handle = nativeCreatePoller(maxFd);
    }
  }

  /**
   * Needed to clean up at the JNI C level when object is GCd.
   */
  protected void finalize() throws Throwable {
    synchronized(globalSync) {
      nativeDestroyPoller(handle);
      super.finalize();
    }
  }

  /**
   * Since we can't guarantee WHEN finalize is called, we may
   * recycle on our own.
   * @param  maxFd the maximum number of FileDescriptors/Sockets
   *         this Poller object can contain.
   */
  public void reset(int maxFd) throws Exception {
    synchronized(globalSync) {
      nativeDestroyPoller(handle);
      this.handle = nativeCreatePoller(maxFd);
    }
  }
  /**
   * Since we can't guarantee WHEN finalize is called, we may
   * recycle on our own.
   */
  public void reset() throws Exception {
    synchronized(globalSync) {
      nativeDestroyPoller(handle);
      this.handle = nativeCreatePoller(-1);
    }
  }

  /**
   * Add FileDescriptor to the set handled by this Poller object.
   *
   * @param  fdObj the FileDescriptor, Socket, or ServerSocket to add.
   * @param  event the bitmask of events we are interested in.
   * @return the OS level fd associated with this IO Object
   *          (which is what waitMultiple() stores in fds[])
   */
  public synchronized int add(Object fdObj, short event) throws Exception {
    return nativeAddFd(handle,findfd(fdObj), event);
  }

  /**
   * Remove FileDescriptor from the set handled by this Poller object.
   *
   * Must be called before the fd/skt is closed.
   * @param fdObj the FileDescriptor, Socket, or ServerSocket to remove.
   * @return true if removal succeeded.
   */
  public synchronized boolean remove(Object fdObj) throws Exception {
    return (nativeRemoveFd(handle,findfd(fdObj)) == 1);
  }
  /**
   * Check if fd or socket is already in the set handled by this Poller object
   *
   * @param fdObj the FileDescriptor or [Server]Socket to check.
   * @return true if fd/skt is in the set for this Poller object.
   */
  public synchronized boolean isMember(Object fdObj) throws Exception {
    return (nativeIsMember(handle,findfd(fdObj)) == 1);
  }
  /**
   * Wait on Multiple IO Objects.
   *
   * @param maxRet    the maximum number of fds[] and revents[] to return.
   * @param fds[]     (return) an array of ints in which to store fds with
   *                  available data upon a successful non-timeout return.
   *                  fds.length must be >= maxRet
   * @param revents[] (return) the actual events available on the
   *                  same-indexed fds[] (i.e. fds[0] has events revents[0])
   *                  revents.length must be >= maxRet
   *
   * Note : both above arrays are "dense," i.e. only fds[] with events
   *        available are returned.
   *
   * @param timeout   the maximum number of milliseconds to wait for
   *                  events before timing out.
   * @return          the number of fds with triggered events.
   *
   * Note : convenience methods exist for skipping the timeout parameter
   *        or the maxRet parameter (in the case of no maxRet, fds.length
   *        must equal revents.length)
   *
   * obj.waitMultiple(null,null,timeout) can be used for pausing the LWP
   * (much more reliable and scalable than Thread.sleep() or Object.wait())
   */
  public synchronized int waitMultiple(int maxRet, int[] fds,short[] revents,
                                       long timeout) throws Exception
    {
      if ((revents == null) || (fds == null)) {
        if (maxRet > 0) {
          throw new NullPointerException("fds or revents is null");
        }
      } else if ( (maxRet < 0) ||
                  (maxRet > revents.length) || (maxRet > fds.length) ) {
        throw new IllegalArgumentException("maxRet out of range");
      }

      int ret = nativeWait(handle, maxRet, fds, revents, timeout);
      if (ret < 0) {
        throw new InterruptedIOException();
      }
      return ret;
    }

  /**
   * Wait on Multiple IO Objects (no timeout).
   * A convenience method for waiting indefinitely on IO events
   *
   * @see Poller#waitMultiple
   *
   */
  public int waitMultiple(int maxRet, int[] fds, short[] revents)
    throws Exception
    {
      return waitMultiple(maxRet, fds, revents,-1L); // already synchronized
    }

  /**
   * Wait on Multiple IO Objects (no maxRet).
   * A convenience method for waiting on IO events when the fds
   * and revents arrays are the same length and that specifies the
   * maximum number of return events.
   *
   * @see Poller#waitMultiple
   *
   */
  public synchronized int waitMultiple(int[] fds, short[] revents,
                                       long timeout) throws Exception
    {
      if ((revents == null) && (fds == null)) {
        return nativeWait(handle,0,null,null,timeout);
      } else if ((revents == null) || (fds == null)) {
        throw new NullPointerException("revents or fds is null");
      } else if (fds.length == revents.length) {
        return nativeWait(handle, fds.length, fds, revents, timeout);
      }
      throw new IllegalArgumentException("fds.length != revents.length");
    }


  /**
   * Wait on Multiple IO Objects (no maxRet/timeout).
   * A convenience method for waiting on IO events when the fds
   * and revents arrays are the same length and that specifies the
   * maximum number of return events, and when waiting indefinitely
   * for IO events to occur.
   *
   * @see Poller#waitMultiple
   *
   */
  public int waitMultiple(int[] fds, short[] revents)
    throws Exception
    {
      if ((revents == null) || (fds == null)) {
        throw new NullPointerException("fds or revents is null");
      } else if (fds.length == revents.length) {
        return waitMultiple(revents.length,fds,revents,-1L); // already sync
      }
      throw new IllegalArgumentException("fds.length != revents.length");
    }

  // Utility - get (int) fd from FileDescriptor or [Server]Socket objects.

  private int findfd(Object fdObj) throws Exception {
    Class cl;
    Field f;
    Object val, implVal;

    if ((fdObj instanceof java.net.Socket) ||
        (fdObj instanceof java.net.ServerSocket)) {
      cl = fdObj.getClass();
      f = cl.getDeclaredField("impl");
      f.setAccessible(true);
      val = f.get(fdObj);
      cl = f.getType();
      f = cl.getDeclaredField("fd");
      f.setAccessible(true);
      implVal = f.get(val);
      cl = f.getType();
      f = cl.getDeclaredField("fd");
      f.setAccessible(true);
      return  ((Integer) f.get(implVal)).intValue();
    } else if ( fdObj instanceof java.io.FileDescriptor ) {
      cl = fdObj.getClass();
      f = cl.getDeclaredField("fd");
      f.setAccessible(true);
      return  ((Integer) f.get(fdObj)).intValue();
    }
    else {
      throw new IllegalArgumentException("Illegal Object type.");
    }
  }

  // Actual NATIVE calls

  private static native int  nativeInit();
  private native int  nativeCreatePoller(int maxFd) throws Exception;
  private native void nativeDestroyPoller(int handle) throws Exception;
  private native int  nativeAddFd(int handle, int fd, short events)
    throws Exception;
  private native int  nativeRemoveFd(int handle, int fd) throws Exception;
  private native int  nativeRemoveIndex(int handle, int index)
    throws Exception;
  private native int  nativeIsMember(int handle, int fd) throws Exception;
  private native int  nativeWait(int handle, int maxRet, int[] fds,
                                        short[] events, long timeout)
    throws Exception;
  /**
   * Get number of active CPUs in this machine
   * to determine proper level of concurrency.
   */
  public static native int  getNumCPUs();

  static {
      System.loadLibrary("poller");
      nativeInit();
  }
}