forked from I2P_Developers/i2p.i2p
I2PTunnel:
- Fix bug that left server acceptor thread running after close - Add destroy() methods to release all resources when closing a tunnel for good, particularly the streaming timer threads - Use COWAL to prevent concurrency problems - Javadocs Streaming: - Don't return null from accept() any more; actually throw ConnectException as the javadocs have always specified - Throw ConnectException from accept() if interrupted; previously caught and ignored - Throw exceptions from ConnectionHandler.accept(), not higher up - Close ServerSocket when ConnectionManager is shut down - Synchronize setActive(), clear queue when starting to accept, better handling of calls that don't change state - Javadocs ConfigClientsHelper: Call isPluginRunning() less often PluginStarter: Simplify detection of active threads Above changes mostly in support of zzzot plugin implementing ClientApp and being able to shut down completely so there are no threads in its thread group, so /configclients will all show status as stopped. Previously, the I2PTunnelServer acceptor thread and one or more streaming timer threads would remain.
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
package net.i2p.client.streaming.impl;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -43,16 +45,24 @@ class ConnectionHandler {
|
||||
_acceptTimeout = DEFAULT_ACCEPT_TIMEOUT;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
public synchronized void setActive(boolean active) {
|
||||
// FIXME active=false this only kills for one thread in accept()
|
||||
// if they are more, they won't ket a poison packet.
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("setActive(" + active + ") called");
|
||||
// if starting, clear any old poison
|
||||
// if stopping, the accept() loop will clear any pending sockets
|
||||
if (active && !_active)
|
||||
_synQueue.clear();
|
||||
boolean wasActive = _active;
|
||||
_active = active;
|
||||
if (!active) {
|
||||
if (wasActive && !active) {
|
||||
try {
|
||||
_synQueue.put(new PoisonPacket()); // so we break from the accept() - waits until space is available
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getActive() { return _active; }
|
||||
|
||||
/**
|
||||
@ -102,17 +112,21 @@ class ConnectionHandler {
|
||||
*
|
||||
* @param timeoutMs max amount of time to wait for a connection (if less
|
||||
* than 1ms, wait indefinitely)
|
||||
* @return connection received, or null if there was a timeout or the
|
||||
* handler was shut down
|
||||
* @return connection received. Prior to 0.9.17, or null if there was a timeout or the
|
||||
* handler was shut down. As of 0.9.17, never null.
|
||||
* @throws ConnectException since 0.9.17, returned null before;
|
||||
* if the I2PServerSocket is closed, or if interrupted.
|
||||
* @throws SocketTimeoutException since 0.9.17, returned null before;
|
||||
* if a timeout was previously set with setSoTimeout and the timeout has been reached.
|
||||
*/
|
||||
public Connection accept(long timeoutMs) {
|
||||
public Connection accept(long timeoutMs) throws ConnectException, SocketTimeoutException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Accept("+ timeoutMs+") called");
|
||||
|
||||
long expiration = timeoutMs + _context.clock().now();
|
||||
while (true) {
|
||||
if ( (timeoutMs > 0) && (expiration < _context.clock().now()) )
|
||||
return null;
|
||||
throw new SocketTimeoutException("accept() timed out");
|
||||
if (!_active) {
|
||||
// fail all the ones we had queued up
|
||||
while(true) {
|
||||
@ -121,7 +135,7 @@ class ConnectionHandler {
|
||||
break;
|
||||
sendReset(packet);
|
||||
}
|
||||
return null;
|
||||
throw new ConnectException("ServerSocket closed");
|
||||
}
|
||||
|
||||
Packet syn = null;
|
||||
@ -132,7 +146,11 @@ class ConnectionHandler {
|
||||
if (timeoutMs <= 0) {
|
||||
try {
|
||||
syn = _synQueue.take(); // waits forever
|
||||
} catch (InterruptedException ie) { } // { break;}
|
||||
} catch (InterruptedException ie) {
|
||||
ConnectException ce = new ConnectException("Interrupted accept()");
|
||||
ce.initCause(ie);
|
||||
throw ce;
|
||||
}
|
||||
} else {
|
||||
long remaining = expiration - _context.clock().now();
|
||||
// (dont think this applies anymore for LinkedBlockingQueue)
|
||||
@ -144,14 +162,18 @@ class ConnectionHandler {
|
||||
break;
|
||||
try {
|
||||
syn = _synQueue.poll(remaining, TimeUnit.MILLISECONDS); // waits the specified time max
|
||||
} catch (InterruptedException ie) { }
|
||||
} catch (InterruptedException ie) {
|
||||
ConnectException ce = new ConnectException("Interrupted accept()");
|
||||
ce.initCause(ie);
|
||||
throw ce;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (syn != null) {
|
||||
if (syn.getOptionalDelay() == PoisonPacket.POISON_MAX_DELAY_REQUEST)
|
||||
return null;
|
||||
throw new ConnectException("ServerSocket closed");
|
||||
|
||||
// deal with forged / invalid syn packets in _manager.receiveConnection()
|
||||
|
||||
|
@ -590,10 +590,14 @@ class ConnectionManager {
|
||||
/**
|
||||
* Something b0rked hard, so kill all of our connections without mercy.
|
||||
* Don't bother sending close packets.
|
||||
* This will not close the ServerSocket.
|
||||
* This will not kill the timer threads.
|
||||
*
|
||||
* CAN continue to use the manager.
|
||||
*/
|
||||
public void disconnectAllHard() {
|
||||
//if (_log.shouldLog(Log.INFO))
|
||||
// _log.info("ConnMan hard disconnect", new Exception("I did it"));
|
||||
for (Iterator<Connection> iter = _connectionByInboundId.values().iterator(); iter.hasNext(); ) {
|
||||
Connection con = iter.next();
|
||||
con.disconnect(false, false);
|
||||
@ -603,21 +607,30 @@ class ConnectionManager {
|
||||
_recentlyClosed.clear();
|
||||
}
|
||||
_pendingPings.clear();
|
||||
// FIXME
|
||||
// Ideally we would like to stop all TCBShare and all the timer threads here,
|
||||
// but leave them ready to restart when things resume.
|
||||
// However that's quite difficult.
|
||||
// So the timer threads will continue to run.
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill all connections and the timers.
|
||||
* Don't bother sending close packets.
|
||||
* As of 0.9.17, this will close the ServerSocket, killing one thread in accept().
|
||||
*
|
||||
* CANNOT continue to use the manager or restart.
|
||||
*
|
||||
* @since 0.9.7
|
||||
*/
|
||||
public void shutdown() {
|
||||
//if (_log.shouldLog(Log.INFO))
|
||||
// _log.info("ConnMan shutdown", new Exception("I did it"));
|
||||
disconnectAllHard();
|
||||
_tcbShare.stop();
|
||||
_timer.stop();
|
||||
_outboundQueue.close();
|
||||
_connectionHandler.setActive(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.i2p.client.streaming.impl;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
@ -20,13 +21,20 @@ class I2PServerSocketFull implements I2PServerSocket {
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning, unlike regular ServerSocket, may return null
|
||||
* Waits for the next socket connecting. If a remote user tried to make a
|
||||
* connection and the local application wasn't .accept()ing new connections,
|
||||
* they should get refused (if .accept() doesnt occur in some small period).
|
||||
* Warning - unlike regular ServerSocket, may return null (through 0.9.16 only).
|
||||
*
|
||||
* @return I2PSocket OR NULL
|
||||
* @throws net.i2p.I2PException
|
||||
* @throws SocketTimeoutException
|
||||
* @return a connected I2PSocket OR NULL through 0.9.16; never null as of 0.9.17
|
||||
*
|
||||
* @throws I2PException if there is a problem with reading a new socket
|
||||
* from the data available (e.g. the I2PSession is closed)
|
||||
* @throws ConnectException if the I2PServerSocket is closed, or if interrupted.
|
||||
* Not actually thrown through 0.9.16; thrown as of 0.9.17
|
||||
* @throws SocketTimeoutException if a timeout was previously set with setSoTimeout and the timeout has been reached.
|
||||
*/
|
||||
public I2PSocket accept() throws I2PException, SocketTimeoutException {
|
||||
public I2PSocket accept() throws I2PException, ConnectException, SocketTimeoutException {
|
||||
return _socketManager.receiveSocket();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.i2p.client.streaming.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
@ -121,27 +122,19 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* The accept() call.
|
||||
*
|
||||
* @return connected I2PSocket OR NULL
|
||||
* @throws net.i2p.I2PException
|
||||
* @throws java.net.SocketTimeoutException
|
||||
* @return connected I2PSocket, or null through 0.9.16, non-null as of 0.9.17
|
||||
* @throws I2PException if session is closed
|
||||
* @throws ConnectException (since 0.9.17; I2PServerSocket interface always declared it)
|
||||
* @throws SocketTimeoutException if a timeout was previously set with setSoTimeout and the timeout has been reached.
|
||||
*/
|
||||
public I2PSocket receiveSocket() throws I2PException, SocketTimeoutException {
|
||||
public I2PSocket receiveSocket() throws I2PException, ConnectException, SocketTimeoutException {
|
||||
verifySession();
|
||||
Connection con = _connectionManager.getConnectionHandler().accept(_connectionManager.getSoTimeout());
|
||||
if(_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("receiveSocket() called: " + con);
|
||||
}
|
||||
if (con != null) {
|
||||
I2PSocketFull sock = new I2PSocketFull(con,_context);
|
||||
con.setSocket(sock);
|
||||
return sock;
|
||||
} else {
|
||||
if(_connectionManager.getSoTimeout() == -1) {
|
||||
return null;
|
||||
}
|
||||
throw new SocketTimeoutException("I2PSocket timed out");
|
||||
}
|
||||
I2PSocketFull sock = new I2PSocketFull(con, _context);
|
||||
con.setSocket(sock);
|
||||
return sock;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,6 +210,13 @@ public class I2PSocketManagerFull implements I2PSocketManager {
|
||||
return _defaultOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-null socket.
|
||||
* This method does not throw exceptions, but methods on the returned socket
|
||||
* may throw exceptions if the socket or socket manager is closed.
|
||||
*
|
||||
* @return non-null
|
||||
*/
|
||||
public I2PServerSocket getServerSocket() {
|
||||
_connectionManager.setAllowIncomingConnections(true);
|
||||
return _serverSocket;
|
||||
|
Reference in New Issue
Block a user