Package org.hornetq.core.persistence.impl.journal

Source Code of org.hornetq.core.persistence.impl.journal.OperationContextImpl

/*
* Copyright 2009 Red Hat, Inc.
* Red Hat 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.hornetq.core.persistence.impl.journal;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.hornetq.api.core.HornetQExceptionType;
import org.hornetq.core.journal.IOAsyncTask;
import org.hornetq.core.journal.impl.SimpleWaitIOCallback;
import org.hornetq.core.persistence.OperationContext;
import org.hornetq.core.server.HornetQServerLogger;
import org.hornetq.utils.ExecutorFactory;

/**
*
* Each instance of OperationContextImpl is associated with an executor (usually an ordered Executor).
*
* Tasks are hold until the operations are complete and executed in the natural order as soon as the operations are returned
* from replication and storage.
*
* If there are no pending IO operations, the tasks are just executed at the callers thread without any context switch.
*
* So, if you are doing operations that are not dependent on IO (e.g NonPersistentMessages) you wouldn't have any context switch.
*
* @author <mailto:clebert.suconic@jboss.org">Clebert Suconic</a>
*/
public class OperationContextImpl implements OperationContext
{
   private static final ThreadLocal<OperationContext> threadLocalContext = new ThreadLocal<OperationContext>();

   public static void clearContext()
   {
      OperationContextImpl.threadLocalContext.set(null);
   }

   public static final OperationContext getContext()
   {
      return getContext(null);
   }

   public static OperationContext getContext(final ExecutorFactory executorFactory)
   {
      OperationContext token = OperationContextImpl.threadLocalContext.get();
      if (token == null)
      {
         if (executorFactory == null)
         {
            return null;
         }
         else
         {
            token = new OperationContextImpl(executorFactory.getExecutor());
            OperationContextImpl.threadLocalContext.set(token);
         }
      }
      return token;
   }

   public static void setContext(final OperationContext context)
   {
      OperationContextImpl.threadLocalContext.set(context);
   }

   private List<TaskHolder> tasks;

   private long minimalStore = Long.MAX_VALUE;
   private long minimalReplicated = Long.MAX_VALUE;
   private long minimalPage = Long.MAX_VALUE;

   private final AtomicLong storeLineUp = new AtomicLong(0);
   private final AtomicLong replicationLineUp = new AtomicLong(0);
   private final AtomicLong pageLineUp = new AtomicLong(0);

   private long stored = 0;
   private long replicated = 0;
   private long paged = 0;

   private int errorCode = -1;

   private String errorMessage = null;

   private final Executor executor;

   private final AtomicInteger executorsPending = new AtomicInteger(0);

   public OperationContextImpl(final Executor executor)
   {
      super();
      this.executor = executor;
   }

   public void pageSyncLineUp()
   {
      pageLineUp.incrementAndGet();
   }

   public synchronized void pageSyncDone()
   {
      paged++;
      checkTasks();
   }

   public void storeLineUp()
   {
      storeLineUp.incrementAndGet();
   }

   public void replicationLineUp()
   {
      replicationLineUp.incrementAndGet();
   }

   public synchronized void replicationDone()
   {
      replicated++;
      checkTasks();
   }

   public void executeOnCompletion(final IOAsyncTask completion)
   {
      if (errorCode != -1)
      {
         completion.onError(errorCode, errorMessage);
         return;
      }

      boolean executeNow = false;

      synchronized (this)
      {
         if (tasks == null)
         {
            tasks = new LinkedList<TaskHolder>();
            minimalReplicated = replicationLineUp.intValue();
            minimalStore = storeLineUp.intValue();
            minimalPage = pageLineUp.intValue();
         }

         // On this case, we can just execute the context directly
         if (replicationLineUp.intValue() == replicated && storeLineUp.intValue() == stored &&
                  pageLineUp.intValue() == paged)
         {
            // We want to avoid the executor if everything is complete...
            // However, we can't execute the context if there are executions pending
            // We need to use the executor on this case
            if (executorsPending.get() == 0)
            {
               // No need to use an executor here or a context switch
               // there are no actions pending.. hence we can just execute the task directly on the same thread
               executeNow = true;
            }
            else
            {
               execute(completion);
            }
         }
         else
         {
            tasks.add(new TaskHolder(completion));
         }
      }

      if (executeNow)
      {
         // Executing outside of any locks
         completion.done();
      }

   }

   public synchronized void done()
   {
      stored++;
      checkTasks();
   }

   private void checkTasks()
   {
      if (stored >= minimalStore && replicated >= minimalReplicated && paged >= minimalPage)
      {
         Iterator<TaskHolder> iter = tasks.iterator();
         while (iter.hasNext())
         {
            TaskHolder holder = iter.next();
            if (stored >= holder.storeLined && replicated >= holder.replicationLined && paged >= holder.pageLined)
            {
               // If set, we use an executor to avoid the server being single threaded
               execute(holder.task);

               iter.remove();
            }
            else
            {
               // End of list here. No other task will be completed after this
               break;
            }
         }
      }
   }

   /**
    * @param task
    */
   private void execute(final IOAsyncTask task)
   {
      executorsPending.incrementAndGet();
      try
      {
         executor.execute(new Runnable()
         {
            public void run()
            {
               try
               {
                  // If any IO is done inside the callback, it needs to be done on a new context
                  OperationContextImpl.clearContext();
                  task.done();
               }
               finally
               {
                  executorsPending.decrementAndGet();
               }
            }
         });
      }
      catch (Throwable e)
      {
         HornetQServerLogger.LOGGER.errorExecutingIOAsyncTask(e);
         executorsPending.decrementAndGet();
         task.onError(HornetQExceptionType.INTERNAL_ERROR.getCode(),
                      "It wasn't possible to complete IO operation - " + e.getMessage());
      }
   }

   /*
    * (non-Javadoc)
    * @see org.hornetq.core.replication.ReplicationToken#complete()
    */
   public void complete()
   {
   }

   @Override
   public synchronized void onError(final int errorCode, final String errorMessage)
   {
      this.errorCode = errorCode;
      this.errorMessage = errorMessage;

      if (tasks != null)
      {
         Iterator<TaskHolder> iter = tasks.iterator();
         while (iter.hasNext())
         {
            TaskHolder holder = iter.next();
            holder.task.onError(errorCode, errorMessage);
            iter.remove();
         }
      }
   }

   final class TaskHolder
   {

      @Override
      public String toString()
      {
         return "TaskHolder [storeLined=" + storeLined +
                ", replicationLined=" +
                replicationLined +
                ", pageLined=" +
                pageLined +
                ", task=" +
                task +
                "]";
      }

      final int storeLined;
      final int replicationLined;
      final int pageLined;

      final IOAsyncTask task;

      TaskHolder(final IOAsyncTask task)
      {
         storeLined = storeLineUp.intValue();
         replicationLined = replicationLineUp.intValue();
         pageLined = pageLineUp.intValue();
         this.task = task;
      }
   }

   @Override
   public void waitCompletion() throws Exception
   {
      waitCompletion(0);
   }

   @Override
   public boolean waitCompletion(final long timeout) throws Exception
   {
      SimpleWaitIOCallback waitCallback = new SimpleWaitIOCallback();
      executeOnCompletion(waitCallback);
      complete();
      if (timeout == 0)
      {
         waitCallback.waitCompletion();
         return true;
      }
      else
      {
         return waitCallback.waitCompletion(timeout);
      }
   }

   @Override
   public String toString()
   {
      StringBuffer buffer = new StringBuffer();
      if (tasks != null)
      {
         for (TaskHolder hold : tasks)
         {
            buffer.append("Task = " + hold + "\n");
         }
      }

      return "OperationContextImpl [" + hashCode() + "] [minimalStore=" + minimalStore +
             ", storeLineUp=" +
             storeLineUp +
             ", stored=" +
             stored +
             ", minimalReplicated=" +
             minimalReplicated +
             ", replicationLineUp=" +
             replicationLineUp +
             ", replicated=" +
             replicated +
             ", paged=" +
             paged +
             ", minimalPage=" +
             minimalPage +
             ", pageLineUp=" +
             pageLineUp +
             ", errorCode=" +
             errorCode +
             ", errorMessage=" +
             errorMessage +
             ", executorsPending=" +
             executorsPending +
             ", executor=" + this.executor +
             "]" + buffer.toString();
   }
}
TOP

Related Classes of org.hornetq.core.persistence.impl.journal.OperationContextImpl

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.