Package com.sun.sgs.impl.kernel

Source Code of com.sun.sgs.impl.kernel.TaskSchedulerImpl$TaskRunner

/*
* Copyright 2007-2010 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* --
*/

package com.sun.sgs.impl.kernel;

import com.sun.sgs.app.TaskRejectedException;

import com.sun.sgs.auth.Identity;

import com.sun.sgs.impl.profile.ProfileCollectorHandle;
import com.sun.sgs.impl.sharedutil.LoggerWrapper;
import com.sun.sgs.impl.util.NamedThreadFactory;

import com.sun.sgs.kernel.KernelRunnable;
import com.sun.sgs.kernel.TaskQueue;
import com.sun.sgs.kernel.RecurringTaskHandle;
import com.sun.sgs.kernel.TaskReservation;
import com.sun.sgs.kernel.TaskScheduler;


import java.util.LinkedList;
import java.util.Properties;

import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import java.util.concurrent.atomic.AtomicInteger;

import java.util.logging.Level;
import java.util.logging.Logger;


/**
* Package-private implementation of {@code TaskScheduler} that is used by
* the system scheduling and running all non-transactional, arbitrary-length
* tasks. This is an intentionally simple implementation that uses a backing
* {@code Executor} instead of a {@code SchedulerQueue} until there
* is better understanding of what (if any) custom scheduling behavior will
* help these kinds of tasks.
* <p>
* This class supports the following configuration properties:
* <dl style="margin-left: 1em">
*
* <dt> <i>Property:</i> <code><b>{@value #CONSUMER_THREADS_PROPERTY}
</b></code> <br>
<i>Default:</i> <code>{@value #DEFAULT_CONSUMER_THREADS}</code>
*
* <dd style="padding-top: .5em">The number of initial threads used to process
*      non-transactional tasks.<p>
* </dl>
* FIXME: the profiling code needs a way to learn about the thread count
* from this scheduler separately from the transaction pool. When this gets
* added, this class should start tracking thread counts.
*/
final class TaskSchedulerImpl implements TaskScheduler {

    // logger for this class
    private static final LoggerWrapper logger =
        new LoggerWrapper(Logger.getLogger(TaskSchedulerImpl.
                                           class.getName()));

    /**
     * The property used to define the default number of initial consumer
     * threads.
     */
    public static final String CONSUMER_THREADS_PROPERTY =
        "com.sun.sgs.impl.kernel.task.threads";

    /**
     * The default number of initial consumer threads.
     */
    public static final String DEFAULT_CONSUMER_THREADS = "4";

    // the executor used to run tasks
    private final ScheduledExecutorService executor;

    // the collector handle used for profiling data
    private final ProfileCollectorHandle profileCollectorHandle;

    // the number of tasks waiting to run
    private final AtomicInteger waitingSize = new AtomicInteger(0);

    // flag to note that this scheduler has shutdown
    private volatile boolean isShutdown = false;

    // the context we're using for the application's tasks
    private volatile KernelContext kernelContext = null;

    /**
     * Creates an instance of {@code TaskSchedulerImpl}.
     *
     * @param properties the {@code Properties} for the system
     * @param profileCollectorHandle the {@code ProfileCollectorHandler} used to
     *          manage collection of per-task profiling data
     *
     * @throws Exception if there is any failure creating the scheduler
     */
    TaskSchedulerImpl(Properties properties,
                      ProfileCollectorHandle profileCollectorHandle)
        throws Exception
    {
        logger.log(Level.CONFIG, "Creating TaskSchedulerImpl");
        if (properties == null) {
            throw new NullPointerException("Properties cannot be null");
        }
        if (profileCollectorHandle == null) {
            throw new NullPointerException("Collector handle cannot be null");
        }

        this.profileCollectorHandle = profileCollectorHandle;

        int requestedThreads =
            Integer.parseInt(properties.getProperty(CONSUMER_THREADS_PROPERTY,
                                                    DEFAULT_CONSUMER_THREADS));

        // NOTE: this is replicating previous behavior where there is a
        // fixed-size pool for running tasks, but in practice we may
        // want a flexible pool that allows (e.g.) for tasks that run
        // for the lifetime of a stack
        this.executor = Executors.newScheduledThreadPool(
                requestedThreads, new NamedThreadFactory("TaskScheduler"));

        logger.log(Level.CONFIG,
                   "Created TaskSchedulerImpl with properties:" +
                   "\n  " + CONSUMER_THREADS_PROPERTY + "=" + requestedThreads);
    }

    /**
     * Package-private method used to set the context being used by the kernel.
     *
     * @param kernelContext the {@code KernelContext} for this scheduler
     */
    void setContext(KernelContext kernelContext) {
        this.kernelContext = kernelContext;
    }

    /*
     * Implementations of the TaskScheduler interface.
     */

    /**
     * {@inheritDoc}
     */
    public TaskReservation reserveTask(KernelRunnable task, Identity owner) {
        TaskDetail detail = new TaskDetail(task, owner,
                                           System.currentTimeMillis());
        return new TaskReservationImpl(detail);
    }

    /**
     * {@inheritDoc}
     */
    public TaskReservation reserveTask(KernelRunnable task, Identity owner,
                                       long startTime)
    {
        return new TaskReservationImpl(new TaskDetail(task, owner, startTime));
    }

    /**
     * {@inheritDoc}
     */
    public void scheduleTask(KernelRunnable task, Identity owner) {
        try {
            TaskDetail detail = new TaskDetail(task, owner,
                                               System.currentTimeMillis());
            executor.submit(new TaskRunner(detail));
            waitingSize.incrementAndGet();
        } catch (RejectedExecutionException ree) {
            throw new TaskRejectedException("Couldn't schedule task", ree);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void scheduleTask(KernelRunnable task, Identity owner,
                             long startTime)
    {
        try {
            TaskDetail detail = new TaskDetail(task, owner, startTime);
            executor.schedule(new TaskRunner(detail),
                              startTime - System.currentTimeMillis(),
                              TimeUnit.MILLISECONDS);
            waitingSize.incrementAndGet();
        } catch (RejectedExecutionException ree) {
            throw new TaskRejectedException("Couldn't schedule task", ree);
        }
    }

    /**
     * {@inheritDoc}
     */
    public RecurringTaskHandle scheduleRecurringTask(KernelRunnable task,
                                                     Identity owner,
                                                     long startTime,
                                                     long period)
    {
        if (period <= 0) {
            throw new IllegalArgumentException("Illegal period: " + period);
        }

        return new RecurringTaskHandleImpl(new TaskDetail(task, owner,
                                                          startTime, period));
    }

    /**
     * {@inheritDoc}
     */
    public TaskQueue createTaskQueue() {
        if (isShutdown) {
            throw new IllegalStateException("Scheduler is shutdown");
        }
        return new TaskQueueImpl();
    }

    /*
     * Utility methods and classes.
     */

    /**
     * Tells this scheduler to shutdown.
     *
     */
    void shutdown() {
        synchronized (this) {
            if (isShutdown) {
                return; // return silently
            }
            isShutdown = true;
            executor.shutdown();
        }
    }

    /** Private implementation of {@code TaskReservation}. */
    private class TaskReservationImpl implements TaskReservation {
        private final TaskDetail taskDetail;
        private boolean usedOrCancelled = false;
        /** Creates an instance of {@code TaskReservationImpl}. */
        TaskReservationImpl(TaskDetail taskDetail) {
            this.taskDetail = taskDetail;
        }
        /** {@inheritDoc} */
        public synchronized void cancel() {
            if (usedOrCancelled) {
                throw new IllegalStateException("This reservation cannot be " +
                                                "cancelled");
            }
            usedOrCancelled = true;
        }
        /**
         * {@inheritDoc}
         * <p>
         * @throws TaskRejectedException if the system has become too
         *                               overloaded to honor this reservation
         */
        public void use() {
            synchronized (this) {
                if (usedOrCancelled) {
                    throw new IllegalStateException("This reservation cannot " +
                                                    "be used");
                }
                usedOrCancelled = true;
            }

            try {
                long delay = taskDetail.startTime - System.currentTimeMillis();
                executor.schedule(new TaskRunner(taskDetail), delay,
                                  TimeUnit.MILLISECONDS);
                waitingSize.incrementAndGet();
            } catch (RejectedExecutionException ree) {
                throw new TaskRejectedException("The system has run out of " +
                                                "resources and cannot run " +
                                                "the requested task", ree);
            }
        }
    }

    /** Private implementation of {@code RecurringTaskHandle}.  */
    private class RecurringTaskHandleImpl implements RecurringTaskHandle {
        private final TaskDetail taskDetail;
        private boolean isCancelled = false;
        private boolean isStarted = false;
        private volatile ScheduledFuture<?> future = null;
        /** Creates an instance of {@code RecurringTaskHandleImpl}. */
        RecurringTaskHandleImpl(TaskDetail taskDetail) {
            if (isShutdown) {
                throw new IllegalStateException("Scheduler is shutdown");
            }
            this.taskDetail = taskDetail;
        }
        /** {@inheritDoc} */
        public void cancel() {
            synchronized (this) {
                if (isCancelled) {
                    throw new IllegalStateException("Handle already cancelled");
                }
                isCancelled = true;
            }
            if (future != null) {
                future.cancel(false);
            }
        }
        /** {@inheritDoc} */
        public void start() {
            synchronized (this) {
                if (isCancelled) {
                    throw new IllegalStateException("Handle already cancelled");
                }
                if ((future != null) || (isStarted)) {
                    throw new IllegalStateException("Handle already used");
                }
                isStarted = true;
            }

            long delay = taskDetail.startTime - System.currentTimeMillis();
            try {
                future =
                    executor.scheduleAtFixedRate(new TaskRunner(taskDetail),
                                                 delay, taskDetail.period,
                                                 TimeUnit.MILLISECONDS);
            } catch (RejectedExecutionException ree) {
                throw new TaskRejectedException("The system has run out of " +
                                                "resources and cannot start " +
                                                "the requested task", ree);
            }
        }
    }

    /** Private class used to maintain task detail. */
    private static class TaskDetail {
        final KernelRunnable task;
        final Identity owner;
        volatile long startTime;
        final long period;
        final TaskQueueImpl queue;
        /** Creates an instance of {@code TaskDetail}. */
        TaskDetail(KernelRunnable task, Identity owner, long startTime) {
            this(task, owner, startTime, 0);
        }
        /** Creates an instance of {@code TaskDetail}. */
        TaskDetail(KernelRunnable task, Identity owner, long startTime,
                   long period)
        {
            if (task == null) {
                throw new NullPointerException("Task cannot be null");
            }
            if (owner == null) {
                throw new NullPointerException("Owner cannot be null");
            }

            this.task = task;
            this.owner = owner;
            this.startTime = startTime;
            this.period = period;
            this.queue = null;
        }
        /** Creates an instance of {@code TaskDetail} with dependency. */
        TaskDetail(KernelRunnable task, Identity owner, TaskQueueImpl queue) {
            if (task == null) {
                throw new NullPointerException("Task cannot be null");
            }
            if (owner == null) {
                throw new NullPointerException("Owner cannot be null");
            }
            if (queue == null) {
                throw new NullPointerException("TaskQueue cannot be null");
            }

            this.task = task;
            this.owner = owner;
            this.startTime = System.currentTimeMillis();
            this.period = 0;
            this.queue = queue;
        }
        /** Returns whether this task is recurring. */
        boolean isRecurring() {
            return period != 0;
        }
    }

    /**
     * Private {@code Runnable} used to wrap all {@code KernelRunnable} tasks
     * submitted to this scheduler.
     */
    private class TaskRunner implements Runnable {
        private final TaskDetail taskDetail;
        /** Creates an instance of {@code TaskRunner} to run the task. */
        TaskRunner(TaskDetail taskDetail) {
            this.taskDetail = taskDetail;
        }
        /** {@inheritDoc} */
        public void run() {
            logger.log(Level.FINE, "Running a non-transactional task");

            int queueSize = (taskDetail.isRecurring() ? waitingSize.get() :
                             waitingSize.decrementAndGet());
            profileCollectorHandle.startTask(taskDetail.task, taskDetail.owner,
                                       taskDetail.startTime, queueSize);
            if (taskDetail.isRecurring()) {
                taskDetail.startTime += taskDetail.period;
            }

            // store the current owner, and then push the new thread detail
            Identity parent = ContextResolver.getCurrentOwner();
            ContextResolver.setTaskState(kernelContext, taskDetail.owner);

            try {
                taskDetail.task.run();
                profileCollectorHandle.finishTask(1);
            } catch (Exception e) {
                profileCollectorHandle.finishTask(1, e);
                if (logger.isLoggable(Level.WARNING)) {
                    if (taskDetail.isRecurring()) {
                        logger.logThrow(Level.WARNING, e, "failed to run " +
                                        "task {0}", taskDetail.task);
                    } else {
                        logger.logThrow(Level.WARNING, e, "failed to run " +
                                        "recurrence of task {0}",
                                        taskDetail.task);
                    }
                }
            } finally {
                // always restore the previous owner before leaving
                ContextResolver.setTaskState(kernelContext, parent);
                // schedule the next task, if any
                if (taskDetail.queue != null) {
                    taskDetail.queue.scheduleNextTask();
                }
            }
        }
    }

    /** Private implementation of {@code TaskQueue}. */
    private final class TaskQueueImpl implements TaskQueue {
        private final LinkedList<TaskDetail> queue =
            new LinkedList<TaskDetail>();
        private boolean inScheduler = false;
        /** {@inheritDoc} */
        public void addTask(KernelRunnable task, Identity owner) {
            TaskDetail detail = new TaskDetail(task, owner, this);
      waitingSize.incrementAndGet();
            synchronized (this) {
                if (inScheduler) {
                    queue.offer(detail);
                } else {
                    inScheduler = true;
                    executor.submit(new TaskRunner(detail));
                }
            }
        }
        /** Private method to schedule the next task, if any. */
        void scheduleNextTask() {
            synchronized (this) {
                if (queue.isEmpty()) {
                    inScheduler = false;
                } else {
                    // re-set the start time before scheduling, since the
                    // task isn't really requested to start until all
                    // tasks ahead of it have run
                    TaskDetail detail = queue.poll();
                    detail.startTime = System.currentTimeMillis();
                    executor.submit(new TaskRunner(detail));
                }
            }
        }
    }

}
TOP

Related Classes of com.sun.sgs.impl.kernel.TaskSchedulerImpl$TaskRunner

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.