Package com.linkedin.parseq.internal

Source Code of com.linkedin.parseq.internal.ContextImpl

/*
* Copyright 2012 LinkedIn, 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.linkedin.parseq.internal;

import com.linkedin.parseq.After;
import com.linkedin.parseq.Cancellable;
import com.linkedin.parseq.Context;
import com.linkedin.parseq.EarlyFinishException;
import com.linkedin.parseq.Task;
import com.linkedin.parseq.promise.Promise;
import com.linkedin.parseq.promise.PromiseListener;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

/**
* @author Chris Pettitt (cpettitt@linkedin.com)
* @author Chi Chan (ckchan@linkedin.com)
*/
public class ContextImpl implements Context, Cancellable
{
  private static final Task<?> NO_PARENT = null;
  private static final List<Task<?>> NO_PREDECESSORS = Collections.emptyList();

  private static final Exception EARLY_FINISH_EXCEPTION;

  static
  {
    EARLY_FINISH_EXCEPTION = new EarlyFinishException("Task cancelled because parent was already finished");

    // Clear out everything but the last frame
    final StackTraceElement[] stackTrace = EARLY_FINISH_EXCEPTION.getStackTrace();
    if (stackTrace.length > 0) {
      EARLY_FINISH_EXCEPTION.setStackTrace(Arrays.copyOf(EARLY_FINISH_EXCEPTION.getStackTrace(), 1));
    }
  }

  /**
   * Plan level configuration and facilities.
   */
  private final PlanContext _planContext;

  private final Task<Object> _task;

  // A thread local that holds the root task iff the root task is being executed
  // on the current thread. In all other cases, this thread local will hold a
  // null value.
  private final ThreadLocal<Task<?>> _inTask = new ThreadLocal<Task<?>>();

  private final Task<?> _parent;
  private final List<Task<?>> _predecessorTasks;

  private final ConcurrentLinkedQueue<Cancellable> _cancellables = new ConcurrentLinkedQueue<Cancellable>();

  public ContextImpl(final PlanContext planContext,
                     final Task<?> task)
  {
    this(planContext, task, NO_PARENT, NO_PREDECESSORS);
  }

  private ContextImpl(final PlanContext planContext,
                      final Task<?> task,
                      final Task<?> parent,
                      final List<Task<?>> predecessorTasks)
  {
    _planContext = planContext;
    _task = InternalUtil.unwildcardTask(task);
    _parent = parent;
    _predecessorTasks = predecessorTasks;
  }

  public void runTask()
  {
    // Cancel everything created by this task once it finishes
    _task.addListener(new PromiseListener<Object>()
    {
      @Override
      public void onResolved(Promise<Object> resolvedPromise)
      {
        for (Iterator<Cancellable> it = _cancellables.iterator(); it.hasNext(); )
        {
          final Cancellable cancellable = it.next();
          cancellable.cancel(EARLY_FINISH_EXCEPTION);
          it.remove();
        }
      }
    });

    _planContext.execute(new PrioritizableRunnable()
    {
      @Override
      public void run()
      {
        _inTask.set(_task);
        try
        {
          _task.contextRun(ContextImpl.this, _planContext.getTaskLogger(), _parent, _predecessorTasks);
        }
        finally
        {
          _inTask.remove();
        }
      }

      @Override
      public int getPriority()
      {
        return _task.getPriority();
      }
    });
  }

  @Override
  public Cancellable createTimer(final long time, final TimeUnit unit,
                                 final Task<?> task)
  {
    checkInTask();
    final Cancellable cancellable = _planContext.schedule(time, unit, new Runnable()
    {
      @Override
      public void run()
      {
        runSubTask(task, NO_PREDECESSORS);
      }
    });
    _cancellables.add(cancellable);
    return cancellable;
  }

  @Override
  public void run(final Task<?>... tasks)
  {
    checkInTask();
    for (final Task<?> task : tasks)
    {
      runSubTask(task, NO_PREDECESSORS);
    }
  }

  @Override
  public After after(final Promise<?>... promises)
  {
    checkInTask();

    final List<Task<?>> tmpPredecessorTasks = new ArrayList<Task<?>>();
    for (Promise<?> promise : promises)
    {
      if (promise instanceof Task)
      {
        tmpPredecessorTasks.add((Task<?>) promise);
      }
    }
    final List<Task<?>> predecessorTasks = Collections.unmodifiableList(tmpPredecessorTasks);

    return new After() {
      @Override
      public void run(final Task<?> task)
      {
        InternalUtil.after(new PromiseListener()
        {
          @Override
          public void onResolved(Promise resolvedPromise)
          {
            runSubTask(task, predecessorTasks);
          }
        }, promises);
      }
    };
  }

  @Override
  public boolean cancel(Exception reason)
  {
    boolean result = _task.cancel(reason);
    //run the task to capture the trace data
    _task.contextRun(this, _planContext.getTaskLogger(), _parent, _predecessorTasks);
    return result;
  }

  @Override
  public Object getEngineProperty(String key)
  {
    return _planContext.getEngineProperty(key);
  }

  private ContextImpl createSubContext(final Task<?> task, final List<Task<?>> predecessors)
  {
    return new ContextImpl(_planContext, task, _task, predecessors);
  }

  private void runSubTask(final Task<?> task, final List<Task<?>> predecessors)
  {
    final ContextImpl subContext = createSubContext(task, predecessors);
    if (!isDone())
    {
      _cancellables.add(subContext);
      subContext.runTask();
    }
    else
    {
      subContext.cancel(EARLY_FINISH_EXCEPTION);
    }
  }

  private boolean isDone()
  {
    return _task.isDone();
  }

  private void checkInTask()
  {
    if (_inTask.get() != _task)
    {
      throw new IllegalStateException("Context method invoked while not in context's task");
    }
  }
}
TOP

Related Classes of com.linkedin.parseq.internal.ContextImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.