org.apache.hadoop.hbase.procedure2.AbstractProcedureScheduler.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.procedure2.AbstractProcedureScheduler.java

Source

/**
 * 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.hadoop.hbase.procedure2;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.classification.InterfaceStability;

@InterfaceAudience.Private
@InterfaceStability.Evolving
public abstract class AbstractProcedureScheduler implements ProcedureScheduler {
    private static final Log LOG = LogFactory.getLog(AbstractProcedureScheduler.class);

    private final ReentrantLock schedLock = new ReentrantLock();
    private final Condition schedWaitCond = schedLock.newCondition();
    private boolean running = false;

    // TODO: metrics
    private long pollCalls = 0;
    private long nullPollCalls = 0;

    @Override
    public void start() {
        schedLock();
        try {
            running = true;
        } finally {
            schedUnlock();
        }
    }

    @Override
    public void stop() {
        schedLock();
        try {
            running = false;
            schedWaitCond.signalAll();
        } finally {
            schedUnlock();
        }
    }

    @Override
    public void signalAll() {
        schedLock();
        try {
            schedWaitCond.signalAll();
        } finally {
            schedUnlock();
        }
    }

    // ==========================================================================
    //  Add related
    // ==========================================================================
    /**
     * Add the procedure to the queue.
     * NOTE: this method is called with the sched lock held.
     * @param procedure the Procedure to add
     * @param addFront true if the item should be added to the front of the queue
     */
    protected abstract void enqueue(Procedure procedure, boolean addFront);

    public void addFront(final Procedure procedure) {
        push(procedure, true, true);
    }

    public void addBack(final Procedure procedure) {
        push(procedure, false, true);
    }

    protected void push(final Procedure procedure, final boolean addFront, final boolean notify) {
        schedLock.lock();
        try {
            enqueue(procedure, addFront);
            if (notify) {
                schedWaitCond.signal();
            }
        } finally {
            schedLock.unlock();
        }
    }

    // ==========================================================================
    //  Poll related
    // ==========================================================================
    /**
     * Fetch one Procedure from the queue
     * NOTE: this method is called with the sched lock held.
     * @return the Procedure to execute, or null if nothing is available.
     */
    protected abstract Procedure dequeue();

    @Override
    public Procedure poll() {
        return poll(-1);
    }

    @Override
    public Procedure poll(long timeout, TimeUnit unit) {
        return poll(unit.toNanos(timeout));
    }

    public Procedure poll(long nanos) {
        final boolean waitForever = (nanos < 0);
        schedLock();
        try {
            while (!queueHasRunnables()) {
                if (!running)
                    return null;
                if (waitForever) {
                    schedWaitCond.await();
                } else {
                    if (nanos <= 0)
                        return null;
                    nanos = schedWaitCond.awaitNanos(nanos);
                }
            }

            final Procedure pollResult = dequeue();
            pollCalls++;
            nullPollCalls += (pollResult == null) ? 1 : 0;
            return pollResult;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            nullPollCalls++;
            return null;
        } finally {
            schedUnlock();
        }
    }

    // ==========================================================================
    //  Utils
    // ==========================================================================
    /**
     * Removes all of the elements from the queue
     * NOTE: this method is called with the sched lock held.
     */
    protected abstract void clearQueue();

    /**
     * Returns the number of elements in this queue.
     * NOTE: this method is called with the sched lock held.
     * @return the number of elements in this queue.
     */
    protected abstract int queueSize();

    /**
     * Returns true if there are procedures available to process.
     * NOTE: this method is called with the sched lock held.
     * @return true if there are procedures available to process, otherwise false.
     */
    protected abstract boolean queueHasRunnables();

    @Override
    public void clear() {
        // NOTE: USED ONLY FOR TESTING
        schedLock();
        try {
            clearQueue();
        } finally {
            schedUnlock();
        }
    }

    @Override
    public int size() {
        schedLock();
        try {
            return queueSize();
        } finally {
            schedUnlock();
        }
    }

    @Override
    public boolean hasRunnables() {
        schedLock();
        try {
            return queueHasRunnables();
        } finally {
            schedUnlock();
        }
    }

    // ============================================================================
    //  TODO: Metrics
    // ============================================================================
    public long getPollCalls() {
        return pollCalls;
    }

    public long getNullPollCalls() {
        return nullPollCalls;
    }

    // ==========================================================================
    //  Procedure Events
    // ==========================================================================
    @Override
    public boolean waitEvent(final ProcedureEvent event, final Procedure procedure) {
        synchronized (event) {
            if (event.isReady()) {
                return false;
            }
            suspendProcedure(event, procedure);
            return true;
        }
    }

    @Override
    public void suspendEvent(final ProcedureEvent event) {
        final boolean isTraceEnabled = LOG.isTraceEnabled();
        synchronized (event) {
            event.setReady(false);
            if (isTraceEnabled) {
                LOG.trace("Suspend event " + event);
            }
        }
    }

    @Override
    public void wakeEvent(final ProcedureEvent event) {
        wakeEvents(1, event);
    }

    @Override
    public void wakeEvents(final int count, final ProcedureEvent... events) {
        final boolean isTraceEnabled = LOG.isTraceEnabled();
        schedLock();
        try {
            int waitingCount = 0;
            for (int i = 0; i < count; ++i) {
                final ProcedureEvent event = events[i];
                synchronized (event) {
                    event.setReady(true);
                    if (isTraceEnabled) {
                        LOG.trace("Wake event " + event);
                    }
                    waitingCount += popEventWaitingObjects(event);
                }
            }
            wakePollIfNeeded(waitingCount);
        } finally {
            schedUnlock();
        }
    }

    protected int popEventWaitingObjects(final ProcedureEvent event) {
        return popEventWaitingProcedures(event);
    }

    protected int popEventWaitingProcedures(final ProcedureEventQueue event) {
        int count = 0;
        while (event.hasWaitingProcedures()) {
            wakeProcedure(event.popWaitingProcedure(false));
            count++;
        }
        return count;
    }

    protected void suspendProcedure(final ProcedureEventQueue event, final Procedure procedure) {
        procedure.suspend();
        event.suspendProcedure(procedure);
    }

    protected void wakeProcedure(final Procedure procedure) {
        procedure.resume();
        push(procedure, /* addFront= */ true, /* notify= */false);
    }

    // ==========================================================================
    //  Internal helpers
    // ==========================================================================
    protected void schedLock() {
        schedLock.lock();
    }

    protected void schedUnlock() {
        schedLock.unlock();
    }

    protected void wakePollIfNeeded(final int waitingCount) {
        if (waitingCount > 1) {
            schedWaitCond.signalAll();
        } else if (waitingCount > 0) {
            schedWaitCond.signal();
        }
    }
}