/* * 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.scheduledexecutor.impl; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; import com.hazelcast.nio.serialization.IdentifiedDataSerializable; import com.hazelcast.scheduledexecutor.impl.operations.GetAllScheduledOnMemberOperation; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * Metadata holder for scheduled tasks. * Scheduled ones have a reference in their future in {@link #future}. * Stashed ones have this reference null. */ public class ScheduledTaskDescriptor implements IdentifiedDataSerializable { private TaskDefinition definition; private ScheduledFuture<?> future; /** * Only accessed through a member lock or partition threads * Used to identify which replica of the task is the owner, to only return that instance * when {@link GetAllScheduledOnMemberOperation} operation is triggered. * This flag is set to true only on initial scheduling of a task, and on after a promotion (stashed or migration), * in the latter case the other replicas get disposed. */ private transient boolean isTaskOwner; /** * SPMC (see. Member owned tasks) */ private volatile ScheduledTaskStatisticsImpl stats; /** * MPMC (Multiple Producers Multiple Concumers) * MP when cancelling, due to member owned tasks, all other writes are SP and through partition threads. * Reads are MP for member owned tasks. */ private AtomicReference<ScheduledTaskResult> resultRef = new AtomicReference<ScheduledTaskResult>(null); /** * SPMC */ private Map<?, ?> state; public ScheduledTaskDescriptor() { } public ScheduledTaskDescriptor(TaskDefinition definition) { this.definition = definition; this.state = new HashMap(); this.stats = new ScheduledTaskStatisticsImpl(); } public ScheduledTaskDescriptor(TaskDefinition definition, Map<?, ?> state, ScheduledTaskStatisticsImpl stats, ScheduledTaskResult result) { this.definition = definition; this.stats = stats; this.state = state; this.resultRef.set(result); } public TaskDefinition getDefinition() { return definition; } public boolean isTaskOwner() { return isTaskOwner; } void setTaskOwner(boolean taskOwner) { this.isTaskOwner = taskOwner; } ScheduledTaskStatisticsImpl getStatsSnapshot() { return stats.snapshot(); } Map<?, ?> getState() { return state; } ScheduledTaskResult getTaskResult() { return resultRef.get(); } void setStats(ScheduledTaskStatisticsImpl stats) { this.stats = stats; } void setState(Map<?, ?> snapshot) { this.state = snapshot; } ScheduledFuture<?> getScheduledFuture() { return future; } void setScheduledFuture(ScheduledFuture<?> future) { this.future = future; } void setTaskResult(ScheduledTaskResult result) { this.resultRef.set(result); } Object get() throws ExecutionException, InterruptedException { ScheduledTaskResult result = resultRef.get(); if (result != null) { result.checkErroneousState(); return result.getReturnValue(); } return future.get(); } boolean cancel(boolean mayInterrupt) throws ExecutionException, InterruptedException { if (!resultRef.compareAndSet(null, new ScheduledTaskResult(true)) || future == null) { return false; } return future.cancel(mayInterrupt); } long getDelay(TimeUnit unit) { boolean wasDoneOrCancelled = resultRef.get() != null; if (wasDoneOrCancelled) { return 0; } return future.getDelay(unit); } boolean isCancelled() throws ExecutionException, InterruptedException { ScheduledTaskResult result = resultRef.get(); boolean wasCancelled = result != null && result.wasCancelled(); return wasCancelled || (future != null && future.isCancelled()); } boolean isDone() throws ExecutionException, InterruptedException { boolean wasDone = resultRef.get() != null; return wasDone || (future != null && future.isDone()); } boolean shouldSchedule() { // Stashed tasks that never got scheduled, and weren't cancelled in-between return future == null && this.resultRef.get() == null; } @Override public int getFactoryId() { return ScheduledExecutorDataSerializerHook.F_ID; } @Override public int getId() { return ScheduledExecutorDataSerializerHook.TASK_DESCRIPTOR; } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeObject(definition); out.writeObject(state); out.writeObject(stats); out.writeObject(resultRef.get()); } @Override public void readData(ObjectDataInput in) throws IOException { definition = in.readObject(); state = in.readObject(); stats = in.readObject(); resultRef.set((ScheduledTaskResult) in.readObject()); } @Override public String toString() { return "ScheduledTaskDescriptor{" + "definition=" + definition + ", " + "future=" + future + ", " + "stats=" + stats + ", " + "state=" + state + ", " + "isTaskOwner=" + isTaskOwner + ", " + "result=" + resultRef.get() + '}'; } }