/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* 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.hazelcast.mapreduce.impl.task;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
/**
* Schedules execution of a {@link Runnable} tasks. It guarantees only a single thread will ever execute
* the task.
*
*/
class ReducerTaskScheduler {
private enum State {
// Task is not running or requested for execution
INACTIVE,
// Task is currently being executed. No further execution was requested.
RUNNING,
// Task is running and another execution was requested
REQUESTED
}
private final AtomicReference<State> state;
private final ExecutorService executorService;
private final Runnable task;
ReducerTaskScheduler(ExecutorService executorService, Runnable task) {
this.state = new AtomicReference<State>(State.INACTIVE);
this.executorService = executorService;
this.task = task;
}
private void scheduleExecution() {
executorService.submit(task);
}
/**
* Request a new task execution if needed.
*
*/
void requestExecution() {
for (;;) {
State currentState = state.get();
switch (currentState) {
case INACTIVE:
if (state.compareAndSet(State.INACTIVE, State.RUNNING)) {
scheduleExecution();
return;
}
break;
case RUNNING:
if (state.compareAndSet(State.RUNNING, State.REQUESTED)) {
return;
}
break;
default:
//someone else already requested execution
return;
}
}
}
/**
* The task has to call this method after its execution.
*
*/
void afterExecution() {
for (;;) {
State currentState = state.get();
switch (currentState) {
case REQUESTED:
state.set(State.RUNNING);
scheduleExecution();
return;
case RUNNING:
if (state.compareAndSet(State.RUNNING, State.INACTIVE)) {
return;
}
break;
default:
throw new IllegalStateException("Inactive state is illegal here.");
}
}
}
}