diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/PriorityDataSource.java b/library/src/main/java/com/google/android/exoplayer/upstream/PriorityDataSource.java index 310a054296..10f0f588a3 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/PriorityDataSource.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/PriorityDataSource.java @@ -16,39 +16,51 @@ package com.google.android.exoplayer.upstream; import com.google.android.exoplayer.util.Assertions; +import com.google.android.exoplayer.util.PriorityTaskManager; import android.net.Uri; import java.io.IOException; /** - * Allows {@link #open(DataSpec)} and {@link #read(byte[], int, int)} calls only if the specified - * priority is the highest priority of any task. {@link NetworkLock.PriorityTooLowException} is - * thrown when this condition does not hold. + * A {@link DataSource} that can be used as part of a task registered with a + * {@link PriorityTaskManager}. + *

+ * Calls to {@link #open(DataSpec)} and {@link #read(byte[], int, int)} are allowed to proceed only + * if there are no higher priority tasks registered to the {@link PriorityTaskManager}. If there + * exists a higher priority task then {@link PriorityTaskManager.PriorityTooLowException} is thrown. + *

+ * Instances of this class are intended to be used as parts of (possibly larger) tasks that are + * registered with the {@link PriorityTaskManager}, and hence do not register as tasks + * themselves. */ public final class PriorityDataSource implements DataSource { private final DataSource upstream; + private final PriorityTaskManager priorityTaskManager; private final int priority; /** - * @param priority The priority of the source. * @param upstream The upstream {@link DataSource}. + * @param priorityTaskManager The priority manager to which the task is registered. + * @param priority The priority of the task. */ - public PriorityDataSource(int priority, DataSource upstream) { - this.priority = priority; + public PriorityDataSource(DataSource upstream, PriorityTaskManager priorityTaskManager, + int priority) { this.upstream = Assertions.checkNotNull(upstream); + this.priorityTaskManager = Assertions.checkNotNull(priorityTaskManager); + this.priority = priority; } @Override public long open(DataSpec dataSpec) throws IOException { - NetworkLock.instance.proceedOrThrow(priority); + priorityTaskManager.proceedOrThrow(priority); return upstream.open(dataSpec); } @Override public int read(byte[] buffer, int offset, int max) throws IOException { - NetworkLock.instance.proceedOrThrow(priority); + priorityTaskManager.proceedOrThrow(priority); return upstream.read(buffer, offset, max); } diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/NetworkLock.java b/library/src/main/java/com/google/android/exoplayer/util/PriorityTaskManager.java similarity index 52% rename from library/src/main/java/com/google/android/exoplayer/upstream/NetworkLock.java rename to library/src/main/java/com/google/android/exoplayer/util/PriorityTaskManager.java index b8c6aa1f65..fd85d8fe02 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/NetworkLock.java +++ b/library/src/main/java/com/google/android/exoplayer/util/PriorityTaskManager.java @@ -13,26 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer.upstream; +package com.google.android.exoplayer.util; import java.io.IOException; +import java.util.Collections; import java.util.PriorityQueue; /** - * A network task prioritization mechanism. + * Allows tasks with associated priorities to control how they proceed relative to one another. *

- * Manages different priority network tasks. A network task that wishes to have its priority - * respected, and respect the priority of other tasks, should register itself with the lock prior - * to making network requests. It should then call one of the lock's proceed methods frequently - * during execution, so as to ensure that it continues only if it is the highest (or equally - * highest) priority task. - *

- * Note that lower integer values correspond to higher priorities. + * A task should call {@link #add(int)} to register with the manager and {@link #remove(int)} to + * unregister. A registered task will prevent tasks of lower priority from proceeding, and should + * call {@link #proceed(int)}, {@link #proceedNonBlocking(int)} or {@link #proceedOrThrow(int)} each + * time it wishes to check whether it is itself allowed to proceed. */ -public final class NetworkLock { +public final class PriorityTaskManager { /** - * Thrown when a task is attempts to proceed when it does not have the highest priority. + * Thrown when task attempts to proceed when another registered task has a higher priority. */ public static class PriorityTooLowException extends IOException { @@ -42,67 +40,15 @@ public final class NetworkLock { } - public static final NetworkLock instance = new NetworkLock(); - - /** - * Priority for network tasks associated with media streaming. - */ - public static final int STREAMING_PRIORITY = 0; - /** - * Priority for network tasks associated with background downloads. - */ - public static final int DOWNLOAD_PRIORITY = 10; - private final Object lock = new Object(); - /** Guarded by {@link #lock}. */ + // Guarded by lock. private final PriorityQueue queue; - - /** Guarded by {@link #lock}. */ private int highestPriority; - private NetworkLock() { - queue = new PriorityQueue<>(); - highestPriority = Integer.MAX_VALUE; - } - - /** - * Blocks until the passed priority is the lowest one (i.e. highest priority). - * - * @param priority The priority of the task that would like to proceed. - */ - public void proceed(int priority) throws InterruptedException { - synchronized (lock) { - while (highestPriority < priority) { - lock.wait(); - } - } - } - - /** - * A non-blocking variant of {@link #proceed(int)}. - * - * @param priority The priority of the task that would like to proceed. - * @return Whether the passed priority is allowed to proceed. - */ - public boolean proceedNonBlocking(int priority) { - synchronized (lock) { - return highestPriority >= priority; - } - } - - /** - * A throwing variant of {@link #proceed(int)}. - * - * @param priority The priority of the task that would like to proceed. - * @throws PriorityTooLowException If the passed priority is not high enough to proceed. - */ - public void proceedOrThrow(int priority) throws PriorityTooLowException { - synchronized (lock) { - if (highestPriority < priority) { - throw new PriorityTooLowException(priority, highestPriority); - } - } + private PriorityTaskManager() { + queue = new PriorityQueue<>(10, Collections.reverseOrder()); + highestPriority = Integer.MIN_VALUE; } /** @@ -115,7 +61,47 @@ public final class NetworkLock { public void add(int priority) { synchronized (lock) { queue.add(priority); - highestPriority = Math.min(highestPriority, priority); + highestPriority = Math.max(highestPriority, priority); + } + } + + /** + * Blocks until the task is allowed to proceed. + * + * @param priority The priority of the task. + * @throws InterruptedException If the thread is interrupted. + */ + public void proceed(int priority) throws InterruptedException { + synchronized (lock) { + while (highestPriority != priority) { + lock.wait(); + } + } + } + + /** + * A non-blocking variant of {@link #proceed(int)}. + * + * @param priority The priority of the task. + * @return Whether the task is allowed to proceed. + */ + public boolean proceedNonBlocking(int priority) { + synchronized (lock) { + return highestPriority == priority; + } + } + + /** + * A throwing variant of {@link #proceed(int)}. + * + * @param priority The priority of the task. + * @throws PriorityTooLowException If the task is not allowed to proceed. + */ + public void proceedOrThrow(int priority) throws PriorityTooLowException { + synchronized (lock) { + if (highestPriority != priority) { + throw new PriorityTooLowException(priority, highestPriority); + } } } @@ -127,7 +113,7 @@ public final class NetworkLock { public void remove(int priority) { synchronized (lock) { queue.remove(priority); - highestPriority = queue.isEmpty() ? Integer.MAX_VALUE : queue.peek(); + highestPriority = queue.isEmpty() ? Integer.MIN_VALUE : queue.peek(); lock.notifyAll(); } }