diff options
| author | 2019-05-29 16:10:39 -0700 | |
|---|---|---|
| committer | 2019-05-29 16:10:39 -0700 | |
| commit | fb518d790a35c582535c5e1b45b7cba7498e9489 (patch) | |
| tree | 294fa26edc4ffa24ffacea809ced3ffe069d9600 | |
| parent | 1a1cd0035d9309e57e9d83c6cce039291ea08d24 (diff) | |
| parent | eed3cc94d7170d4d0e42feec2dc9a26e6f5bf043 (diff) | |
Merge "Fix MediaHTTPConnection.disconnect() blocking for a long time." into qt-dev am: 0c1f41d303
am: eed3cc94d7
Change-Id: I4246fc1b74477cefa1099592e6c48fe3f0ec3be2
| -rw-r--r-- | media/java/android/media/MediaHTTPConnection.java | 61 |
1 files changed, 48 insertions, 13 deletions
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index b5c4cca12ff7..8ee929e77899 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -38,6 +38,7 @@ import java.net.URL; import java.net.UnknownServiceException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** @hide */ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @@ -83,6 +84,10 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private final static int HTTP_TEMP_REDIRECT = 307; private final static int MAX_REDIRECTS = 20; + // The number of threads that are currently running disconnect() (possibly + // not yet holding the synchronized lock). + private final AtomicInteger mNumDisconnectingThreads = new AtomicInteger(0); + @UnsupportedAppUsage public MediaHTTPConnection() { CookieHandler cookieHandler = CookieHandler.getDefault(); @@ -155,19 +160,24 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { - HttpURLConnection connectionToDisconnect = mConnection; - // Call disconnect() before blocking for the lock in order to ensure that any - // other thread that is blocked in readAt() will return quickly. - if (connectionToDisconnect != null) { - connectionToDisconnect.disconnect(); - } - synchronized (this) { - // It's unlikely but possible that while we were waiting to acquire the lock, another - // thread concurrently started a new connection; if so, we're disconnecting that one - // here, too. - teardownConnection(); - mHeaders = null; - mURL = null; + mNumDisconnectingThreads.incrementAndGet(); + try { + HttpURLConnection connectionToDisconnect = mConnection; + // Call disconnect() before blocking for the lock in order to ensure that any + // other thread that is blocked in readAt() will return quickly. + if (connectionToDisconnect != null) { + connectionToDisconnect.disconnect(); + } + synchronized (this) { + // It's possible that while we were waiting to acquire the lock, another thread + // concurrently started a new connection; if so, we're disconnecting that one + // here, too. + teardownConnection(); + mHeaders = null; + mURL = null; + } + } finally { + mNumDisconnectingThreads.decrementAndGet(); } } @@ -224,11 +234,36 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { boolean noProxy = isLocalHost(url); while (true) { + // If another thread is concurrently disconnect()ing, there's a race + // between them and us. Therefore, we check mNumDisconnectingThreads shortly + // (not atomically) before & after writing mConnection. This guarantees that + // we won't "lose" a disconnect by creating a new connection that might + // miss the disconnect. + // + // Note that throwing an instanceof IOException is also what this thread + // would have done if another thread disconnect()ed the connection while + // this thread was blocked reading from that connection further down in this + // loop. + if (mNumDisconnectingThreads.get() > 0) { + throw new IOException("concurrently disconnecting"); + } if (noProxy) { mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY); } else { mConnection = (HttpURLConnection)url.openConnection(); } + // If another thread is concurrently disconnecting, throwing IOException will + // cause us to release the lock, giving the other thread a chance to acquire + // it. It also ensures that the catch block will run, which will tear down + // the connection even if the other thread happens to already be on its way + // out of disconnect(). + if (mNumDisconnectingThreads.get() > 0) { + throw new IOException("concurrently disconnecting"); + } + // If we get here without having thrown, we know that other threads + // will see our write to mConnection. Any disconnect() on that mConnection + // instance will cause our read from/write to that connection instance below + // to encounter an instanceof IOException. mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); // handle redirects ourselves if we do not allow cross-domain redirect |