/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.ignite.internal.processors.platform.compute; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.ignite.IgniteException; import org.apache.ignite.compute.ComputeJobResult; import org.apache.ignite.compute.ComputeJobResultPolicy; import org.apache.ignite.compute.ComputeTask; import org.apache.ignite.internal.binary.BinaryRawWriterEx; import org.apache.ignite.internal.processors.platform.PlatformContext; import org.apache.ignite.internal.processors.platform.PlatformNativeException; import org.apache.ignite.internal.processors.platform.memory.PlatformMemory; import org.apache.ignite.internal.processors.platform.memory.PlatformOutputStream; import org.apache.ignite.internal.processors.platform.utils.PlatformUtils; import org.apache.ignite.internal.util.typedef.X; import org.jetbrains.annotations.Nullable; /** * Base class for all interop tasks. */ public abstract class PlatformAbstractTask implements ComputeTask<Object, Void> { /** Platform context. */ protected final PlatformContext ctx; /** Pointer to the task in the native platform. */ protected final long taskPtr; /** Lock for safe access to native pointers. */ protected final ReadWriteLock lock = new ReentrantReadWriteLock(); /** Done flag. */ protected boolean done; /** * Constructor. * * @param ctx Platform context. * @param taskPtr Task pointer. */ protected PlatformAbstractTask(PlatformContext ctx, long taskPtr) { this.ctx = ctx; this.taskPtr = taskPtr; } /** {@inheritDoc} */ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "unchecked"}) @Override public ComputeJobResultPolicy result(ComputeJobResult res, List<ComputeJobResult> rcvd) { assert rcvd.isEmpty() : "Should not cache result in Java for interop task"; lock.readLock().lock(); try { assert !done; PlatformAbstractJob job = res.getJob(); assert job.pointer() != 0; Object res0bj = res.getData(); int plc; if (res0bj == PlatformAbstractJob.LOC_JOB_RES) // Processing local job execution result. plc = ctx.gateway().computeTaskLocalJobResult(taskPtr, job.pointer()); else { // Processing remote job execution result or exception. try (PlatformMemory mem = ctx.memory().allocate()) { PlatformOutputStream out = mem.output(); BinaryRawWriterEx writer = ctx.writer(out); writer.writeLong(taskPtr); writer.writeLong(job.pointer()); writer.writeUuid(res.getNode().id()); writer.writeBoolean(res.isCancelled()); IgniteException err = res.getException(); PlatformUtils.writeInvocationResult(writer, res0bj, err); out.synchronize(); plc = ctx.gateway().computeTaskJobResult(mem.pointer()); } } ComputeJobResultPolicy plc0 = ComputeJobResultPolicy.fromOrdinal((byte) plc); assert plc0 != null : plc; return plc0; } finally { lock.readLock().unlock(); } } /** {@inheritDoc} */ @Nullable @Override public Void reduce(List<ComputeJobResult> results) { assert results.isEmpty() : "Should not cache result in java for interop task"; lock.readLock().lock(); try { assert !done; ctx.gateway().computeTaskReduce(taskPtr); } finally { lock.readLock().unlock(); } return null; } /** * Callback invoked when task future is completed and all resources could be safely cleaned up. * * @param e If failed. */ @SuppressWarnings("ThrowableResultOfMethodCallIgnored") public void onDone(Exception e) { lock.writeLock().lock(); try { assert !done; if (e == null) // Normal completion. ctx.gateway().computeTaskComplete(taskPtr, 0); else { PlatformNativeException e0 = X.cause(e, PlatformNativeException.class); try (PlatformMemory mem = ctx.memory().allocate()) { PlatformOutputStream out = mem.output(); BinaryRawWriterEx writer = ctx.writer(out); if (e0 == null) { writer.writeBoolean(false); writer.writeString(e.getClass().getName()); writer.writeString(e.getMessage()); writer.writeString(X.getFullStackTrace(e)); } else { writer.writeBoolean(true); writer.writeObject(e0.cause()); } out.synchronize(); ctx.gateway().computeTaskComplete(taskPtr, mem.pointer()); } } } finally { // Done flag is set irrespective of any exceptions. done = true; lock.writeLock().unlock(); } } /** * Callback invoked by job when it wants to lock the task. * * @return {@code} True if task is not completed yet, {@code false} otherwise. */ @SuppressWarnings("LockAcquiredButNotSafelyReleased") boolean onJobLock() { lock.readLock().lock(); if (done) { lock.readLock().unlock(); return false; } else return true; } /** * Callback invoked by job when task can be unlocked. */ void onJobUnlock() { assert !done; lock.readLock().unlock(); } }