Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  <20132014  2015  2016  2017  2018  2019  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  <20132014  2015  2016  2017  2018  2019 
<== Date ==> <== Thread ==>

Subject: Re: CAJ "AlreadyBoundException"
From: Till Straumann <strauman@slac.stanford.edu>
To: "Kasemir, Kay" <kasemirk@ornl.gov>, "L. C. De Silva" <desilva@rcf.rhic.bnl.gov>
Cc: "tech-talk@aps.anl.gov" <tech-talk@aps.anl.gov>
Date: Wed, 04 Dec 2013 20:49:44 -0800
I recently ported JCA/CAJ to android and came across the fact that
BroadcastConnector does not bind the underlying DatagramSocket
to a local address.

According to the java.nio.channels.DatagramChannel docs a channel
is open()ed and then the underlying socket can be bound. Under android
it apparently must be. openjdk is more forgiving and binds the socket
automatically. However, there might be a problem:

For openjdk I came across the following source code

http://cr.openjdk.java.net/~dxu/8000404/webrev/src/share/classes/sun/nio/ch/DatagramChannelImpl.java.html

which happens to match the lines in your stack trace. In this class (I have no
other reason to believe it is the same implementation you are using than
two matching line numbers) the socket is bound to a local address automatically
(if the user has not done so already).

At first glance there seems to be a race condition which could explain what
you experience:

receive(ByteBuffer dst) [starting at line 317] contains the following code:

   synchronized( readLock ) {
      ...
      if ( localAddress() == null ) {
          bind(null);
      }
.     ...
   }

and bind(SocketAddress local) [starting at line 654] is coded:

  synchronized( readLock ) {
    synchronized ( writeLock ) {
        if ( localAddress != null ) {
           throw new AlreadyBoundException();
        }
        // do actual binding here
        localAddress = xxx;
    }
  }

However, in send(ByteBuffer src, SocketAddress target) [starting at line 413] we find

  synchronized ( writeLock ) {
        ...
        if ( localAddress == null ) {
          localAddress = Net.localAddress( fd );
        }
  }


Hence, it could occur that thread A takes the read lock, tests localAddress and then proceeds to bind. It finds the writeLock held by thread B which - in the middle of 'send' - sets 'localAddress'. When thread A resumes and obtains 'writeLock'
then it finds 'localAddress' non-null and throws the exception.

I don't know how likely this is (do you see this often?) - there might be other scenarios or you might not even use the implementation I looked at (I didn't spend more than 1h
on this analysis).

In any case it is IMHO worthwhile to fix CAJ so that it binds datagram channels
at creation time:

--- a/src/com/cosylab/epics/caj/impl/BroadcastConnector.java Fri Nov 08 11:26:19 2013 -0800 +++ b/src/com/cosylab/epics/caj/impl/BroadcastConnector.java Fri Nov 08 11:29:22 2013 -0800
@@ -58,6 +58,9 @@

             // use non-blocking channel (no need for soTimeout)
             socket.configureBlocking(false);
+
+            // bind to any local port
+            socket.socket().bind( null );

             // set SO_BROADCAST
             socket.socket().setBroadcast(true);


HTH
-- Till

On 12/04/2013 06:48 PM, Kasemir, Kay wrote:
Hello:

Does anybody else see these messages from CAJ, the pure java channel access client?

Thanks,
Kay

2013-12-04 14:44:27.667 SEVERE [Thread 25] com.cosylab.epics.caj.impl.reactor.Reactor (processInternal) -
java.nio.channels.AlreadyBoundException
         at sun.nio.ch.DatagramChannelImpl.bind(DatagramChannelImpl.java:661)
         at sun.nio.ch.DatagramChannelImpl.receive(DatagramChannelImpl.java:327)
         at com.cosylab.epics.caj.impl.BroadcastTransport.processRead(BroadcastTransport.java:178)
         at com.cosylab.epics.caj.impl.BroadcastTransport.handleEvent(BroadcastTransport.java:155)
         at com.cosylab.epics.caj.impl.reactor.lf.LeaderFollowersHandler.handleEvent(LeaderFollowersHandler.java:77)
         at com.cosylab.epics.caj.impl.reactor.Reactor.processInternal(Reactor.java:400)
         at com.cosylab.epics.caj.impl.reactor.Reactor.process(Reactor.java:284)
         at com.cosylab.epics.caj.CAJContext$2.run(CAJContext.java:708)
         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
         at java.lang.Thread.run(Thread.java:722)




References:
CAJ "AlreadyBoundException" Kasemir, Kay

Navigate by Date:
Prev: Re: CAJ "AlreadyBoundException" Mark Rivers
Next: Re: which time/timestamp format to use for an EPICS facility? Benoit
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  <20132014  2015  2016  2017  2018  2019 
Navigate by Thread:
Prev: Re: CAJ "AlreadyBoundException" Mark Rivers
Next: StreamDevice "No reply from device" messages John Dobbins
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  <20132014  2015  2016  2017  2018  2019 
ANJ, 20 Apr 2015 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·