All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squareup.okhttp.Dispatcher Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2013 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.okhttp;

import com.squareup.okhttp.Call.AsyncCall;
import com.squareup.okhttp.internal.Util;
import com.squareup.okhttp.internal.http.HttpEngine;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Policy on when async requests are executed.
 *
 * 

Each dispatcher uses an {@link ExecutorService} to run calls internally. If you * supply your own executor, it should be able to run {@linkplain #getMaxRequests the * configured maximum} number of calls concurrently. */ public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; /** Executes calls. Created lazily. */ private ExecutorService executorService; /** Ready calls in the order they'll be run. */ private final Deque readyCalls = new ArrayDeque<>(); /** Running calls. Includes canceled calls that haven't finished yet. */ private final Deque runningCalls = new ArrayDeque<>(); /** In-flight synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque executedCalls = new ArrayDeque<>(); public Dispatcher(ExecutorService executorService) { this.executorService = executorService; } public Dispatcher() { } public synchronized ExecutorService getExecutorService() { if (executorService == null) { executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; } /** * Set the maximum number of requests to execute concurrently. Above this * requests queue in memory, waiting for the running calls to complete. * *

If more than {@code maxRequests} requests are in flight when this is * invoked, those requests will remain in flight. */ public synchronized void setMaxRequests(int maxRequests) { if (maxRequests < 1) { throw new IllegalArgumentException("max < 1: " + maxRequests); } this.maxRequests = maxRequests; promoteCalls(); } public synchronized int getMaxRequests() { return maxRequests; } /** * Set the maximum number of requests for each host to execute concurrently. * This limits requests by the URL's host name. Note that concurrent requests * to a single IP address may still exceed this limit: multiple hostnames may * share an IP address or be routed through the same HTTP proxy. * *

If more than {@code maxRequestsPerHost} requests are in flight when this * is invoked, those requests will remain in flight. */ public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) { if (maxRequestsPerHost < 1) { throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost); } this.maxRequestsPerHost = maxRequestsPerHost; promoteCalls(); } public synchronized int getMaxRequestsPerHost() { return maxRequestsPerHost; } synchronized void enqueue(AsyncCall call) { if (runningCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningCalls.add(call); getExecutorService().execute(call); } else { readyCalls.add(call); } } /** Cancel all calls with the tag {@code tag}. */ public synchronized void cancel(Object tag) { for (AsyncCall call : readyCalls) { if (Util.equal(tag, call.tag())) { call.cancel(); } } for (AsyncCall call : runningCalls) { if (Util.equal(tag, call.tag())) { call.get().canceled = true; HttpEngine engine = call.get().engine; if (engine != null) engine.cancel(); } } for (Call call : executedCalls) { if (Util.equal(tag, call.tag())) { call.cancel(); } } } /** Used by {@code AsyncCall#run} to signal completion. */ synchronized void finished(AsyncCall call) { if (!runningCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!"); promoteCalls(); } private void promoteCalls() { if (runningCalls.size() >= maxRequests) return; // Already running max capacity. if (readyCalls.isEmpty()) return; // No ready calls to promote. for (Iterator i = readyCalls.iterator(); i.hasNext(); ) { AsyncCall call = i.next(); if (runningCallsForHost(call) < maxRequestsPerHost) { i.remove(); runningCalls.add(call); getExecutorService().execute(call); } if (runningCalls.size() >= maxRequests) return; // Reached max capacity. } } /** Returns the number of running calls that share a host with {@code call}. */ private int runningCallsForHost(AsyncCall call) { int result = 0; for (AsyncCall c : runningCalls) { if (c.host().equals(call.host())) result++; } return result; } /** Used by {@code Call#execute} to signal it is in-flight. */ synchronized void executed(Call call) { executedCalls.add(call); } /** Used by {@code Call#execute} to signal completion. */ synchronized void finished(Call call) { if (!executedCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); } public synchronized int getRunningCallCount() { return runningCalls.size(); } public synchronized int getQueuedCallCount() { return readyCalls.size(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy