/**
 * ****************************************************************************
 * Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
 * <p>
 * This file is part of the Archimulator multicore architectural simulator.
 * <p>
 * Archimulator is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <p>
 * Archimulator 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.
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
 * ****************************************************************************
 */
package archimulator.core;

import archimulator.core.functionalUnit.FunctionalUnitOperationType;
import archimulator.isa.StaticInstructionType;
import archimulator.os.ContextState;
import archimulator.util.Reference;
import archimulator.util.RoundRobinScheduler;

import java.util.Iterator;
import java.util.List;

/**
 * Basic core.
 *
 * @author Min Cai
 */
public class BasicCore extends AbstractBasicCore {
    private RoundRobinScheduler<Thread> registerRenameScheduler;
    private RoundRobinScheduler<Thread> dispatchScheduler;

    /**
     * Create a basic core.
     *
     * @param processor the parent processor
     * @param num       the number of the core
     */
    public BasicCore(Processor processor, int num) {
        super(processor, num);

        this.registerRenameScheduler = new RoundRobinScheduler<>(
                this.threads,
                thread -> {
                    if (thread.getContext() == null) {
                        return false;
                    } else if (thread.getDecodeBuffer().isEmpty()) {
                        thread.incrementNumRegisterRenameStallsOnDecodeBufferIsEmpty();
                        return false;
                    } else if (thread.getReorderBuffer().isFull()) {
                        thread.incrementNumRegisterRenameStallsOnReorderBufferIsFull();
                        return false;
                    } else {
                        return true;
                    }
                },
                Thread::registerRenameOne, getExperiment().getConfig().getDecodeWidth()
        );

        this.dispatchScheduler = new RoundRobinScheduler<>(
                this.threads,
                thread -> thread.getContext() != null,
                Thread::dispatchOne,
                getExperiment().getConfig().getDecodeWidth()
        );
    }

    @Override
    protected void fetch() {
        this.threads.stream().filter(
                thread -> thread.getContext() != null && thread.getContext().getState() == ContextState.RUNNING
        ).forEach(Thread::fetch);
    }

    @Override
    protected void registerRename() {
        this.registerRenameScheduler.consumeNext();
    }

    @Override
    protected void dispatch() {
        this.dispatchScheduler.consumeNext();
    }

    @Override
    protected void wakeUp() {
        this.wakeUp(this.waitingInstructionQueue, this.readyInstructionQueue);
        this.wakeUp(this.waitingStoreQueue, this.readyStoreQueue);
    }

    /**
     * Wake up.
     *
     * @param waitingQueue the waiting queue
     * @param readyQueue   the ready queue
     */
    private void wakeUp(List<AbstractReorderBufferEntry> waitingQueue, List<AbstractReorderBufferEntry> readyQueue) {
        waitingQueue.stream().filter(AbstractReorderBufferEntry::isAllOperandReady).forEach(readyQueue::add);
        waitingQueue.removeIf(AbstractReorderBufferEntry::isAllOperandReady);
    }

    @Override
    protected void issue() {
        Reference<Integer> quant = new Reference<>(getExperiment().getConfig().getIssueWidth());

        this.issueInstructionQueue(quant);
        this.issueLoadQueue(quant);
        this.issueStoreQueue(quant);
    }

    /**
     * Issue the instruction queue.
     *
     * @param quant the quant
     */
    private void issueInstructionQueue(Reference<Integer> quant) {
        for (Iterator<AbstractReorderBufferEntry> it = this.readyInstructionQueue.iterator(); quant.get() > 0 && it.hasNext(); ) {
            final ReorderBufferEntry reorderBufferEntry = (ReorderBufferEntry) it.next();

            if (reorderBufferEntry.getDynamicInstruction().getStaticInstruction().getMnemonic().getFunctionalUnitOperationType() != FunctionalUnitOperationType.NONE) {
                if (this.functionalUnitPool.acquire(reorderBufferEntry, reorderBufferEntry::signalCompleted)) {
                    reorderBufferEntry.setIssued();
                } else {
                    reorderBufferEntry.getThread().incrementNumSelectionStallsOnNoFreeFunctionalUnit();
                    continue;
                }
            } else {
                reorderBufferEntry.setIssued();
                reorderBufferEntry.setCompleted();
                reorderBufferEntry.writeBack();
            }

            it.remove();

            quant.set(quant.get() - 1);
        }
    }

    /**
     * Issue the load queue.
     *
     * @param quant the quant
     */
    private void issueLoadQueue(Reference<Integer> quant) {
        for (Iterator<AbstractReorderBufferEntry> it = this.readyLoadQueue.iterator(); quant.get() > 0 && it.hasNext(); ) {
            final LoadStoreQueueEntry loadStoreQueueEntry = (LoadStoreQueueEntry) it.next();

            boolean hitInLoadStoreQueue = loadStoreQueueEntry.getThread().getLoadStoreQueue().getEntries().stream().anyMatch(
                    loadStoreQueueEntryFound
                            -> loadStoreQueueEntryFound.getDynamicInstruction().getStaticInstruction().getMnemonic().getType() == StaticInstructionType.STORE
                            && loadStoreQueueEntryFound.getEffectiveAddress() == loadStoreQueueEntry.getEffectiveAddress()
            );

            if (hitInLoadStoreQueue) {
                loadStoreQueueEntry.setIssued();
                loadStoreQueueEntry.signalCompleted();
            } else {
                if (!this.canLoad(loadStoreQueueEntry.getThread(), loadStoreQueueEntry.getEffectiveAddress())) {
                    loadStoreQueueEntry.getThread().incrementNumSelectionStallsOnCanNotLoad();
                    break;
                }

                this.load(
                        loadStoreQueueEntry.getDynamicInstruction(),
                        loadStoreQueueEntry.getEffectiveAddress(),
                        loadStoreQueueEntry.getDynamicInstruction().getPc(),
                        loadStoreQueueEntry::signalCompleted
                );
                loadStoreQueueEntry.setIssued();
            }

            it.remove();

            quant.set(quant.get() - 1);
        }
    }

    /**
     * Issue the store queue.
     *
     * @param quant the store queue
     */
    private void issueStoreQueue(Reference<Integer> quant) {
        for (Iterator<AbstractReorderBufferEntry> it = this.readyStoreQueue.iterator(); quant.get() > 0 && it.hasNext(); ) {
            final LoadStoreQueueEntry loadStoreQueueEntry = (LoadStoreQueueEntry) it.next();

            if (!this.canStore(loadStoreQueueEntry.getThread(), loadStoreQueueEntry.getEffectiveAddress())) {
                loadStoreQueueEntry.getThread().incrementNumSelectionStallsOnCanNotStore();
                break;
            }

            this.store(loadStoreQueueEntry.getDynamicInstruction(), loadStoreQueueEntry.getEffectiveAddress(), loadStoreQueueEntry.getDynamicInstruction().getPc(), () -> {
//                    loadStoreQueueEntry.signalCompleted(); //TODO: should we need to wait for store to complete?
            });
            loadStoreQueueEntry.setIssued();
            loadStoreQueueEntry.signalCompleted(); //TODO: should we need to wait for store to complete?

            it.remove();

            quant.set(quant.get() - 1);
        }
    }

    @Override
    protected void writeBack() {
        for (AbstractReorderBufferEntry reorderBufferEntry : this.oooEventQueue) {
            reorderBufferEntry.setCompleted();
            reorderBufferEntry.writeBack();
        }

        this.oooEventQueue.clear();
    }

    @Override
    protected void refreshLoadStoreQueue() {
        this.threads.stream().filter(thread -> thread.getContext() != null).forEach(Thread::refreshLoadStoreQueue);
    }

    @Override
    protected void commit() {
        this.threads.stream().filter(thread -> thread.getContext() != null).forEach(Thread::commit);
    }
}

@@ -2489,16 +2489,32 @@
 nameOne,
+%0A               
  getExpe

Reformat core/BasicCore.java.