blob: 24b2d2e2f3a8f7edf13c0dfd5aa8cb4c88d7a439 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.intellij.vcs.log.data;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Collects incoming requests into a list, and provides them to an underlying background task via {@link #popRequests()}. <br/>
* Such task is started immediately after the first request arrives, if no other task is currently running. <br/>
* A task indicates its completion by calling {@link #taskCompleted(Object)} and providing a result which is immediately passed to the
* result handler.
* <p/>
* The purpose of this class is to provide a single thread, which processes incoming requests in the background and continues to process
* new ones if they arrive while the previous ones were processed. An alternative would be a long living thread which always checks some
* queue for new requests - but current approach starts a thread only when needed, and finishes it once all requests are processed.
* <p/>
* The class is thread-safe: all operations are synchronized.
*/
public abstract class SingleTaskController<Request, Result> {
@NotNull private final Consumer<Result> myResultHandler;
@NotNull private final Object LOCK = new Object();
@NotNull private List<Request> myAwaitingRequests;
private boolean myActive;
public SingleTaskController(@NotNull Consumer<Result> handler) {
myResultHandler = handler;
myAwaitingRequests = ContainerUtil.newArrayList();
}
/**
* Posts a request into a queue. <br/>
* If there is no active task, starts a new one. <br/>
* Otherwise just remembers the request in the queue. Later it can be achieved by {@link #popRequests()}.
*/
public final void request(@NotNull Request requests) {
synchronized (LOCK) {
myAwaitingRequests.add(requests);
if (!myActive) {
startNewBackgroundTask();
myActive = true;
}
}
}
/**
* Starts new task on a background thread. <br/>
* <b>NB:</b> Don't invoke StateController methods inside this method, otherwise a deadlock will happen.
*/
protected abstract void startNewBackgroundTask();
/**
* Returns all awaiting requests and clears the queue. <br/>
* I.e. the second call to this method will return an empty list (unless new requests came via {@link #request(Object)}.
*/
@NotNull
protected final List<Request> popRequests() {
synchronized (LOCK) {
List<Request> requests = myAwaitingRequests;
myAwaitingRequests = ContainerUtil.newArrayList();
return requests;
}
}
/**
* The underlying currently active task should use this method to inform that it has completed the execution. <br/>
* The result is immediately passed to the result handler specified in the constructor.
*/
protected final void taskCompleted(@NotNull Result result) {
myResultHandler.consume(result);
synchronized (LOCK) {
if (myAwaitingRequests.isEmpty()) {
myActive = false;
}
else {
startNewBackgroundTask();
}
}
}
}