Home | History | Annotate | Download | only in ch
      1 /*
      2  * Copyright (c) 2008, 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 package sun.nio.ch;
     27 
     28 import java.nio.channels.*;
     29 import java.nio.ByteBuffer;
     30 import java.net.*;
     31 import java.util.concurrent.*;
     32 import java.io.IOException;
     33 import java.io.FileDescriptor;
     34 import java.security.AccessController;
     35 import sun.net.NetHooks;
     36 import sun.security.action.GetPropertyAction;
     37 
     38 import dalvik.annotation.optimization.ReachabilitySensitive;
     39 import dalvik.system.CloseGuard;
     40 
     41 /**
     42  * Unix implementation of AsynchronousSocketChannel
     43  */
     44 
     45 class UnixAsynchronousSocketChannelImpl
     46     extends AsynchronousSocketChannelImpl implements Port.PollableChannel
     47 {
     48     private final static NativeDispatcher nd = new SocketDispatcher();
     49     private static enum OpType { CONNECT, READ, WRITE };
     50 
     51     private static final boolean disableSynchronousRead;
     52     static {
     53         String propValue = AccessController.doPrivileged(
     54             new GetPropertyAction("sun.nio.ch.disableSynchronousRead", "false"));
     55         disableSynchronousRead = (propValue.length() == 0) ?
     56             true : Boolean.valueOf(propValue);
     57     }
     58 
     59     private final Port port;
     60     private final int fdVal;
     61 
     62     // used to ensure that the context for I/O operations that complete
     63     // ascynrhonously is visible to the pooled threads handling I/O events.
     64     private final Object updateLock = new Object();
     65 
     66     // pending connect (updateLock)
     67     private boolean connectPending;
     68     private CompletionHandler<Void,Object> connectHandler;
     69     private Object connectAttachment;
     70     private PendingFuture<Void,Object> connectFuture;
     71 
     72     // pending remote address (stateLock)
     73     private SocketAddress pendingRemote;
     74 
     75     // pending read (updateLock)
     76     private boolean readPending;
     77     private boolean isScatteringRead;
     78     private ByteBuffer readBuffer;
     79     private ByteBuffer[] readBuffers;
     80     private CompletionHandler<Number,Object> readHandler;
     81     private Object readAttachment;
     82     private PendingFuture<Number,Object> readFuture;
     83     private Future<?> readTimer;
     84 
     85     // pending write (updateLock)
     86     private boolean writePending;
     87     private boolean isGatheringWrite;
     88     private ByteBuffer writeBuffer;
     89     private ByteBuffer[] writeBuffers;
     90     private CompletionHandler<Number,Object> writeHandler;
     91     private Object writeAttachment;
     92     private PendingFuture<Number,Object> writeFuture;
     93     private Future<?> writeTimer;
     94 
     95     // Android-added: CloseGuard support.
     96     @ReachabilitySensitive
     97     private final CloseGuard guard = CloseGuard.get();
     98 
     99     UnixAsynchronousSocketChannelImpl(Port port)
    100         throws IOException
    101     {
    102         super(port);
    103 
    104         // set non-blocking
    105         try {
    106             IOUtil.configureBlocking(fd, false);
    107         } catch (IOException x) {
    108             nd.close(fd);
    109             throw x;
    110         }
    111 
    112         this.port = port;
    113         this.fdVal = IOUtil.fdVal(fd);
    114 
    115         // add mapping from file descriptor to this channel
    116         port.register(fdVal, this);
    117         // Android-added: CloseGuard support.
    118         guard.open("close");
    119     }
    120 
    121     // Constructor for sockets created by UnixAsynchronousServerSocketChannelImpl
    122     UnixAsynchronousSocketChannelImpl(Port port,
    123                                       FileDescriptor fd,
    124                                       InetSocketAddress remote)
    125         throws IOException
    126     {
    127         super(port, fd, remote);
    128 
    129         this.fdVal = IOUtil.fdVal(fd);
    130         IOUtil.configureBlocking(fd, false);
    131 
    132         try {
    133             port.register(fdVal, this);
    134         } catch (ShutdownChannelGroupException x) {
    135             // ShutdownChannelGroupException thrown if we attempt to register a
    136             // new channel after the group is shutdown
    137             throw new IOException(x);
    138         }
    139 
    140         this.port = port;
    141         // Android-added: CloseGuard support.
    142         guard.open("close");
    143     }
    144 
    145     @Override
    146     public AsynchronousChannelGroupImpl group() {
    147         return port;
    148     }
    149 
    150     // register events for outstanding I/O operations, caller already owns updateLock
    151     private void updateEvents() {
    152         assert Thread.holdsLock(updateLock);
    153         int events = 0;
    154         if (readPending)
    155             events |= Net.POLLIN;
    156         if (connectPending || writePending)
    157             events |= Net.POLLOUT;
    158         if (events != 0)
    159             port.startPoll(fdVal, events);
    160     }
    161 
    162     // register events for outstanding I/O operations
    163     private void lockAndUpdateEvents() {
    164         synchronized (updateLock) {
    165             updateEvents();
    166         }
    167     }
    168 
    169     // invoke to finish read and/or write operations
    170     private void finish(boolean mayInvokeDirect,
    171                         boolean readable,
    172                         boolean writable)
    173     {
    174         boolean finishRead = false;
    175         boolean finishWrite = false;
    176         boolean finishConnect = false;
    177 
    178         // map event to pending result
    179         synchronized (updateLock) {
    180             if (readable && this.readPending) {
    181                 this.readPending = false;
    182                 finishRead = true;
    183             }
    184             if (writable) {
    185                 if (this.writePending) {
    186                     this.writePending = false;
    187                     finishWrite = true;
    188                 } else if (this.connectPending) {
    189                     this.connectPending = false;
    190                     finishConnect = true;
    191                 }
    192             }
    193         }
    194 
    195         // complete the I/O operation. Special case for when channel is
    196         // ready for both reading and writing. In that case, submit task to
    197         // complete write if write operation has a completion handler.
    198         if (finishRead) {
    199             if (finishWrite)
    200                 finishWrite(false);
    201             finishRead(mayInvokeDirect);
    202             return;
    203         }
    204         if (finishWrite) {
    205             finishWrite(mayInvokeDirect);
    206         }
    207         if (finishConnect) {
    208             finishConnect(mayInvokeDirect);
    209         }
    210     }
    211 
    212     /**
    213      * Invoked by event handler thread when file descriptor is polled
    214      */
    215     @Override
    216     public void onEvent(int events, boolean mayInvokeDirect) {
    217         boolean readable = (events & Net.POLLIN) > 0;
    218         boolean writable = (events & Net.POLLOUT) > 0;
    219         if ((events & (Net.POLLERR | Net.POLLHUP)) > 0) {
    220             readable = true;
    221             writable = true;
    222         }
    223         finish(mayInvokeDirect, readable, writable);
    224     }
    225 
    226     @Override
    227     void implClose() throws IOException {
    228         // Android-added: CloseGuard support.
    229         guard.close();
    230         // remove the mapping
    231         port.unregister(fdVal);
    232 
    233         // close file descriptor
    234         nd.close(fd);
    235 
    236         // All outstanding I/O operations are required to fail
    237         finish(false, true, true);
    238     }
    239 
    240     // Android-added: CloseGuard support.
    241     protected void finalize() throws Throwable {
    242         try {
    243             if (guard != null) {
    244                 guard.warnIfOpen();
    245             }
    246             close();
    247         } finally {
    248             super.finalize();
    249         }
    250     }
    251 
    252     @Override
    253     public void onCancel(PendingFuture<?,?> task) {
    254         if (task.getContext() == OpType.CONNECT)
    255             killConnect();
    256         if (task.getContext() == OpType.READ)
    257             killReading();
    258         if (task.getContext() == OpType.WRITE)
    259             killWriting();
    260     }
    261 
    262     // -- connect --
    263 
    264     private void setConnected() throws IOException {
    265         synchronized (stateLock) {
    266             state = ST_CONNECTED;
    267             localAddress = Net.localAddress(fd);
    268             remoteAddress = (InetSocketAddress)pendingRemote;
    269         }
    270     }
    271 
    272     private void finishConnect(boolean mayInvokeDirect) {
    273         Throwable e = null;
    274         try {
    275             begin();
    276             checkConnect(fdVal);
    277             setConnected();
    278         } catch (Throwable x) {
    279             if (x instanceof ClosedChannelException)
    280                 x = new AsynchronousCloseException();
    281             e = x;
    282         } finally {
    283             end();
    284         }
    285         if (e != null) {
    286             // close channel if connection cannot be established
    287             try {
    288                 close();
    289             } catch (Throwable suppressed) {
    290                 e.addSuppressed(suppressed);
    291             }
    292         }
    293 
    294         // invoke handler and set result
    295         CompletionHandler<Void,Object> handler = connectHandler;
    296         Object att = connectAttachment;
    297         PendingFuture<Void,Object> future = connectFuture;
    298         if (handler == null) {
    299             future.setResult(null, e);
    300         } else {
    301             if (mayInvokeDirect) {
    302                 Invoker.invokeUnchecked(handler, att, null, e);
    303             } else {
    304                 Invoker.invokeIndirectly(this, handler, att, null, e);
    305             }
    306         }
    307     }
    308 
    309     @Override
    310     @SuppressWarnings("unchecked")
    311     <A> Future<Void> implConnect(SocketAddress remote,
    312                                  A attachment,
    313                                  CompletionHandler<Void,? super A> handler)
    314     {
    315         if (!isOpen()) {
    316             Throwable e = new ClosedChannelException();
    317             if (handler == null) {
    318                 return CompletedFuture.withFailure(e);
    319             } else {
    320                 Invoker.invoke(this, handler, attachment, null, e);
    321                 return null;
    322             }
    323         }
    324 
    325         InetSocketAddress isa = Net.checkAddress(remote);
    326 
    327         // permission check
    328         SecurityManager sm = System.getSecurityManager();
    329         if (sm != null)
    330             sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
    331 
    332         // check and set state
    333         boolean notifyBeforeTcpConnect;
    334         synchronized (stateLock) {
    335             if (state == ST_CONNECTED)
    336                 throw new AlreadyConnectedException();
    337             if (state == ST_PENDING)
    338                 throw new ConnectionPendingException();
    339             state = ST_PENDING;
    340             pendingRemote = remote;
    341             notifyBeforeTcpConnect = (localAddress == null);
    342         }
    343 
    344         Throwable e = null;
    345         try {
    346             begin();
    347             // notify hook if unbound
    348             if (notifyBeforeTcpConnect)
    349                 NetHooks.beforeTcpConnect(fd, isa.getAddress(), isa.getPort());
    350             int n = Net.connect(fd, isa.getAddress(), isa.getPort());
    351             if (n == IOStatus.UNAVAILABLE) {
    352                 // connection could not be established immediately
    353                 PendingFuture<Void,A> result = null;
    354                 synchronized (updateLock) {
    355                     if (handler == null) {
    356                         result = new PendingFuture<Void,A>(this, OpType.CONNECT);
    357                         this.connectFuture = (PendingFuture<Void,Object>)result;
    358                     } else {
    359                         this.connectHandler = (CompletionHandler<Void,Object>)handler;
    360                         this.connectAttachment = attachment;
    361                     }
    362                     this.connectPending = true;
    363                     updateEvents();
    364                 }
    365                 return result;
    366             }
    367             setConnected();
    368         } catch (Throwable x) {
    369             if (x instanceof ClosedChannelException)
    370                 x = new AsynchronousCloseException();
    371             e = x;
    372         } finally {
    373             end();
    374         }
    375 
    376         // close channel if connect fails
    377         if (e != null) {
    378             try {
    379                 close();
    380             } catch (Throwable suppressed) {
    381                 e.addSuppressed(suppressed);
    382             }
    383         }
    384         if (handler == null) {
    385             return CompletedFuture.withResult(null, e);
    386         } else {
    387             Invoker.invoke(this, handler, attachment, null, e);
    388             return null;
    389         }
    390     }
    391 
    392     // -- read --
    393 
    394     private void finishRead(boolean mayInvokeDirect) {
    395         int n = -1;
    396         Throwable exc = null;
    397 
    398         // copy fields as we can't access them after reading is re-enabled.
    399         boolean scattering = isScatteringRead;
    400         CompletionHandler<Number,Object> handler = readHandler;
    401         Object att = readAttachment;
    402         PendingFuture<Number,Object> future = readFuture;
    403         Future<?> timeout = readTimer;
    404 
    405         try {
    406             begin();
    407 
    408             if (scattering) {
    409                 n = (int)IOUtil.read(fd, readBuffers, nd);
    410             } else {
    411                 n = IOUtil.read(fd, readBuffer, -1, nd);
    412             }
    413             if (n == IOStatus.UNAVAILABLE) {
    414                 // spurious wakeup, is this possible?
    415                 synchronized (updateLock) {
    416                     readPending = true;
    417                 }
    418                 return;
    419             }
    420 
    421             // allow objects to be GC'ed.
    422             this.readBuffer = null;
    423             this.readBuffers = null;
    424             this.readAttachment = null;
    425 
    426             // allow another read to be initiated
    427             enableReading();
    428 
    429         } catch (Throwable x) {
    430             enableReading();
    431             if (x instanceof ClosedChannelException)
    432                 x = new AsynchronousCloseException();
    433             exc = x;
    434         } finally {
    435             // restart poll in case of concurrent write
    436             if (!(exc instanceof AsynchronousCloseException))
    437                 lockAndUpdateEvents();
    438             end();
    439         }
    440 
    441         // cancel the associated timer
    442         if (timeout != null)
    443             timeout.cancel(false);
    444 
    445         // create result
    446         Number result = (exc != null) ? null : (scattering) ?
    447             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
    448 
    449         // invoke handler or set result
    450         if (handler == null) {
    451             future.setResult(result, exc);
    452         } else {
    453             if (mayInvokeDirect) {
    454                 Invoker.invokeUnchecked(handler, att, result, exc);
    455             } else {
    456                 Invoker.invokeIndirectly(this, handler, att, result, exc);
    457             }
    458         }
    459     }
    460 
    461     private Runnable readTimeoutTask = new Runnable() {
    462         public void run() {
    463             CompletionHandler<Number,Object> handler = null;
    464             Object att = null;
    465             PendingFuture<Number,Object> future = null;
    466 
    467             synchronized (updateLock) {
    468                 if (!readPending)
    469                     return;
    470                 readPending = false;
    471                 handler = readHandler;
    472                 att = readAttachment;
    473                 future = readFuture;
    474             }
    475 
    476             // kill further reading before releasing waiters
    477             enableReading(true);
    478 
    479             // invoke handler or set result
    480             Exception exc = new InterruptedByTimeoutException();
    481             if (handler == null) {
    482                 future.setFailure(exc);
    483             } else {
    484                 AsynchronousChannel ch = UnixAsynchronousSocketChannelImpl.this;
    485                 Invoker.invokeIndirectly(ch, handler, att, null, exc);
    486             }
    487         }
    488     };
    489 
    490     /**
    491      * Initiates a read or scattering read operation
    492      */
    493     @Override
    494     @SuppressWarnings("unchecked")
    495     <V extends Number,A> Future<V> implRead(boolean isScatteringRead,
    496                                             ByteBuffer dst,
    497                                             ByteBuffer[] dsts,
    498                                             long timeout,
    499                                             TimeUnit unit,
    500                                             A attachment,
    501                                             CompletionHandler<V,? super A> handler)
    502     {
    503         // A synchronous read is not attempted if disallowed by system property
    504         // or, we are using a fixed thread pool and the completion handler may
    505         // not be invoked directly (because the thread is not a pooled thread or
    506         // there are too many handlers on the stack).
    507         Invoker.GroupAndInvokeCount myGroupAndInvokeCount = null;
    508         boolean invokeDirect = false;
    509         boolean attemptRead = false;
    510         if (!disableSynchronousRead) {
    511             if (handler == null) {
    512                 attemptRead = true;
    513             } else {
    514                 myGroupAndInvokeCount = Invoker.getGroupAndInvokeCount();
    515                 invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port);
    516                 // okay to attempt read with user thread pool
    517                 attemptRead = invokeDirect || !port.isFixedThreadPool();
    518             }
    519         }
    520 
    521         int n = IOStatus.UNAVAILABLE;
    522         Throwable exc = null;
    523         boolean pending = false;
    524 
    525         try {
    526             begin();
    527 
    528             if (attemptRead) {
    529                 if (isScatteringRead) {
    530                     n = (int)IOUtil.read(fd, dsts, nd);
    531                 } else {
    532                     n = IOUtil.read(fd, dst, -1, nd);
    533                 }
    534             }
    535 
    536             if (n == IOStatus.UNAVAILABLE) {
    537                 PendingFuture<V,A> result = null;
    538                 synchronized (updateLock) {
    539                     this.isScatteringRead = isScatteringRead;
    540                     this.readBuffer = dst;
    541                     this.readBuffers = dsts;
    542                     if (handler == null) {
    543                         this.readHandler = null;
    544                         result = new PendingFuture<V,A>(this, OpType.READ);
    545                         this.readFuture = (PendingFuture<Number,Object>)result;
    546                         this.readAttachment = null;
    547                     } else {
    548                         this.readHandler = (CompletionHandler<Number,Object>)handler;
    549                         this.readAttachment = attachment;
    550                         this.readFuture = null;
    551                     }
    552                     if (timeout > 0L) {
    553                         this.readTimer = port.schedule(readTimeoutTask, timeout, unit);
    554                     }
    555                     this.readPending = true;
    556                     updateEvents();
    557                 }
    558                 pending = true;
    559                 return result;
    560             }
    561         } catch (Throwable x) {
    562             if (x instanceof ClosedChannelException)
    563                 x = new AsynchronousCloseException();
    564             exc = x;
    565         } finally {
    566             if (!pending)
    567                 enableReading();
    568             end();
    569         }
    570 
    571         Number result = (exc != null) ? null : (isScatteringRead) ?
    572             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
    573 
    574         // read completed immediately
    575         if (handler != null) {
    576             if (invokeDirect) {
    577                 Invoker.invokeDirect(myGroupAndInvokeCount, handler, attachment, (V)result, exc);
    578             } else {
    579                 Invoker.invokeIndirectly(this, handler, attachment, (V)result, exc);
    580             }
    581             return null;
    582         } else {
    583             return CompletedFuture.withResult((V)result, exc);
    584         }
    585     }
    586 
    587     // -- write --
    588 
    589     private void finishWrite(boolean mayInvokeDirect) {
    590         int n = -1;
    591         Throwable exc = null;
    592 
    593         // copy fields as we can't access them after reading is re-enabled.
    594         boolean gathering = this.isGatheringWrite;
    595         CompletionHandler<Number,Object> handler = this.writeHandler;
    596         Object att = this.writeAttachment;
    597         PendingFuture<Number,Object> future = this.writeFuture;
    598         Future<?> timer = this.writeTimer;
    599 
    600         try {
    601             begin();
    602 
    603             if (gathering) {
    604                 n = (int)IOUtil.write(fd, writeBuffers, nd);
    605             } else {
    606                 n = IOUtil.write(fd, writeBuffer, -1, nd);
    607             }
    608             if (n == IOStatus.UNAVAILABLE) {
    609                 // spurious wakeup, is this possible?
    610                 synchronized (updateLock) {
    611                     writePending = true;
    612                 }
    613                 return;
    614             }
    615 
    616             // allow objects to be GC'ed.
    617             this.writeBuffer = null;
    618             this.writeBuffers = null;
    619             this.writeAttachment = null;
    620 
    621             // allow another write to be initiated
    622             enableWriting();
    623 
    624         } catch (Throwable x) {
    625             enableWriting();
    626             if (x instanceof ClosedChannelException)
    627                 x = new AsynchronousCloseException();
    628             exc = x;
    629         } finally {
    630             // restart poll in case of concurrent write
    631             if (!(exc instanceof AsynchronousCloseException))
    632                 lockAndUpdateEvents();
    633             end();
    634         }
    635 
    636         // cancel the associated timer
    637         if (timer != null)
    638             timer.cancel(false);
    639 
    640         // create result
    641         Number result = (exc != null) ? null : (gathering) ?
    642             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
    643 
    644         // invoke handler or set result
    645         if (handler == null) {
    646             future.setResult(result, exc);
    647         } else {
    648             if (mayInvokeDirect) {
    649                 Invoker.invokeUnchecked(handler, att, result, exc);
    650             } else {
    651                 Invoker.invokeIndirectly(this, handler, att, result, exc);
    652             }
    653         }
    654     }
    655 
    656     private Runnable writeTimeoutTask = new Runnable() {
    657         public void run() {
    658             CompletionHandler<Number,Object> handler = null;
    659             Object att = null;
    660             PendingFuture<Number,Object> future = null;
    661 
    662             synchronized (updateLock) {
    663                 if (!writePending)
    664                     return;
    665                 writePending = false;
    666                 handler = writeHandler;
    667                 att = writeAttachment;
    668                 future = writeFuture;
    669             }
    670 
    671             // kill further writing before releasing waiters
    672             enableWriting(true);
    673 
    674             // invoke handler or set result
    675             Exception exc = new InterruptedByTimeoutException();
    676             if (handler != null) {
    677                 Invoker.invokeIndirectly(UnixAsynchronousSocketChannelImpl.this,
    678                     handler, att, null, exc);
    679             } else {
    680                 future.setFailure(exc);
    681             }
    682         }
    683     };
    684 
    685     /**
    686      * Initiates a read or scattering read operation
    687      */
    688     @Override
    689     @SuppressWarnings("unchecked")
    690     <V extends Number,A> Future<V> implWrite(boolean isGatheringWrite,
    691                                              ByteBuffer src,
    692                                              ByteBuffer[] srcs,
    693                                              long timeout,
    694                                              TimeUnit unit,
    695                                              A attachment,
    696                                              CompletionHandler<V,? super A> handler)
    697     {
    698         Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
    699             Invoker.getGroupAndInvokeCount();
    700         boolean invokeDirect = Invoker.mayInvokeDirect(myGroupAndInvokeCount, port);
    701         boolean attemptWrite = (handler == null) || invokeDirect ||
    702             !port.isFixedThreadPool();  // okay to attempt write with user thread pool
    703 
    704         int n = IOStatus.UNAVAILABLE;
    705         Throwable exc = null;
    706         boolean pending = false;
    707 
    708         try {
    709             begin();
    710 
    711             if (attemptWrite) {
    712                 if (isGatheringWrite) {
    713                     n = (int)IOUtil.write(fd, srcs, nd);
    714                 } else {
    715                     n = IOUtil.write(fd, src, -1, nd);
    716                 }
    717             }
    718 
    719             if (n == IOStatus.UNAVAILABLE) {
    720                 PendingFuture<V,A> result = null;
    721                 synchronized (updateLock) {
    722                     this.isGatheringWrite = isGatheringWrite;
    723                     this.writeBuffer = src;
    724                     this.writeBuffers = srcs;
    725                     if (handler == null) {
    726                         this.writeHandler = null;
    727                         result = new PendingFuture<V,A>(this, OpType.WRITE);
    728                         this.writeFuture = (PendingFuture<Number,Object>)result;
    729                         this.writeAttachment = null;
    730                     } else {
    731                         this.writeHandler = (CompletionHandler<Number,Object>)handler;
    732                         this.writeAttachment = attachment;
    733                         this.writeFuture = null;
    734                     }
    735                     if (timeout > 0L) {
    736                         this.writeTimer = port.schedule(writeTimeoutTask, timeout, unit);
    737                     }
    738                     this.writePending = true;
    739                     updateEvents();
    740                 }
    741                 pending = true;
    742                 return result;
    743             }
    744         } catch (Throwable x) {
    745             if (x instanceof ClosedChannelException)
    746                 x = new AsynchronousCloseException();
    747             exc = x;
    748         } finally {
    749             if (!pending)
    750                 enableWriting();
    751             end();
    752         }
    753 
    754         Number result = (exc != null) ? null : (isGatheringWrite) ?
    755             (Number)Long.valueOf(n) : (Number)Integer.valueOf(n);
    756 
    757         // write completed immediately
    758         if (handler != null) {
    759             if (invokeDirect) {
    760                 Invoker.invokeDirect(myGroupAndInvokeCount, handler, attachment, (V)result, exc);
    761             } else {
    762                 Invoker.invokeIndirectly(this, handler, attachment, (V)result, exc);
    763             }
    764             return null;
    765         } else {
    766             return CompletedFuture.withResult((V)result, exc);
    767         }
    768     }
    769 
    770     // -- Native methods --
    771 
    772     private static native void checkConnect(int fdVal) throws IOException;
    773 
    774     // Android-removed: Code to load native libraries, doesn't make sense on Android.
    775     /*
    776     static {
    777         IOUtil.load();
    778     }
    779     */
    780 }
    781