<commit_before>/**************************************************************************
 *
 * Copyright 2011 Jose Fonseca
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include <string.h>
#include <iostream>

#include "glws.hpp"
#include "os_binary.hpp"
#include "os_time.hpp"
#include "os_workqueue.hpp"
#include "image.hpp"
#include "trace_callset.hpp"
#include "trace_dump.hpp"
#include "retrace.hpp"

static bool waitOnFinish = false;
static bool use_threads;

static const char *comparePrefix = NULL;
static const char *snapshotPrefix = NULL;
static trace::CallSet snapshotFrequency;
static trace::CallSet compareFrequency;

static unsigned dumpStateCallNo = ~0;

retrace::Retracer retracer;

namespace retrace {

trace::Parser parser;
trace::Profiler profiler;

static std::map<unsigned long, os::WorkQueue *> thread_wq_map;

int verbosity = 0;
bool debug = true;
bool dumpingState = false;

bool doubleBuffer = true;
bool coreProfile = false;

bool profiling = false;
bool profilingGpuTimes = false;
bool profilingCpuTimes = false;
bool profilingPixelsDrawn = false;

unsigned frameNo = 0;
unsigned callNo = 0;
static bool state_dumped;

class RenderWork : public os::WorkQueueWork
{
	trace::Call *call;
public:
	void run(void);
	RenderWork(trace::Call *_call) { call = _call; }
	~RenderWork(void) { delete call; }
};

class FlushGLWork : public os::WorkQueueWork
{
public:
    void run(void) { flushRendering(); }
};

void
frameComplete(trace::Call &call) {
    ++frameNo;
}

static void
takeSnapshot(unsigned call_no) {
    assert(snapshotPrefix || comparePrefix);

    image::Image *ref = NULL;

    if (comparePrefix) {
        os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
        ref = image::readPNG(filename);
        if (!ref) {
            return;
        }
        if (retrace::verbosity >= 0) {
            std::cout << "Read " << filename << "\n";
        }
    }

    image::Image *src = getSnapshot();
    if (!src) {
        return;
    }

    if (snapshotPrefix) {
        if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
            char comment[21];
            snprintf(comment, sizeof comment, "%u", call_no);
            src->writePNM(std::cout, comment);
        } else {
            os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
            if (src->writePNG(filename) && retrace::verbosity >= 0) {
                std::cout << "Wrote " << filename << "\n";
            }
        }
    }

    if (ref) {
        std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
        delete ref;
    }

    delete src;

    return;
}

void RenderWork::run(void)
{
    bool swapRenderTarget = call->flags &
        trace::CALL_FLAG_SWAP_RENDERTARGET;
    bool doSnapshot = snapshotFrequency.contains(*call) ||
        compareFrequency.contains(*call);

    if (state_dumped)
        return;

    // For calls which cause rendertargets to be swaped, we take the
    // snapshot _before_ swapping the rendertargets.
    if (doSnapshot && swapRenderTarget) {
        if (call->flags & trace::CALL_FLAG_END_FRAME) {
            // For swapbuffers/presents we still use this
            // call number, spite not have been executed yet.
            takeSnapshot(call->no);
        } else {
            // Whereas for ordinate fbo/rendertarget changes we
            // use the previous call's number.
            takeSnapshot(call->no - 1);
        }
    }

    callNo = call->no;
    retracer.retrace(*call);

    if (doSnapshot && !swapRenderTarget)
        takeSnapshot(call->no);

    if (call->no >= dumpStateCallNo && dumpState(std::cout))
        state_dumped = true;
}

static os::WorkQueue *get_work_queue(unsigned long thread_id)
{
    os::WorkQueue *thread;
    std::map<unsigned long, os::WorkQueue *>::iterator it;

    it = thread_wq_map.find(thread_id);
    if (it == thread_wq_map.end()) {
        thread = new os::WorkQueue();
        thread_wq_map[thread_id] = thread;
    } else {
        thread = it->second;
    }

    return thread;
}

static void exit_work_queues(void)
{
    std::map<unsigned long, os::WorkQueue *>::iterator it;

    it = thread_wq_map.begin();
    while (it != thread_wq_map.end()) {
        os::WorkQueue *thread_wq = it->second;

        thread_wq->queue_work(new FlushGLWork);
        thread_wq->flush();
        thread_wq->destroy();
        thread_wq_map.erase(it++);
    }
}

static void do_all_calls(void)
{
    trace::Call *call;
    int prev_thread_id = -1;
    os::WorkQueue *thread_wq = NULL;

    while ((call = parser.parse_call())) {
        RenderWork *render_work = new RenderWork(call);

        if (use_threads) {
            if (prev_thread_id != call->thread_id) {
                if (thread_wq)
                    thread_wq->flush();
                thread_wq = get_work_queue(call->thread_id);
                prev_thread_id = call->thread_id;
            }

            thread_wq->queue_work(render_work);
        } else {
            render_work->run();
            delete render_work;
        }

        if (state_dumped)
            break;
    }

    exit_work_queues();
}

static void
mainLoop() {
    addCallbacks(retracer);

    long long startTime = 0;
    frameNo = 0;

    startTime = os::getTime();

    do_all_calls();

    if (!use_threads)
        /*
         * Reached the end of trace; if using threads we do the flush
         * when exiting the threads.
         */
        flushRendering();

    long long endTime = os::getTime();
    float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);

    if ((retrace::verbosity >= -1) || (retrace::profiling)) {
        std::cout <<
            "Rendered " << frameNo << " frames"
            " in " <<  timeInterval << " secs,"
            " average of " << (frameNo/timeInterval) << " fps\n";
    }

    if (waitOnFinish) {
        waitForInput();
    } else {
        return;
    }
}

} /* namespace retrace */

static void
usage(const char *argv0) {
    std::cout <<
        "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
        "Replay TRACE.\n"
        "\n"
        "  -b           benchmark mode (no error checking or warning messages)\n"
        "  -pcpu        cpu profiling (cpu times per call)\n"
        "  -pgpu        gpu profiling (gpu times per draw call)\n"
        "  -ppd         pixels drawn profiling (pixels drawn per draw call)\n"
        "  -c PREFIX    compare against snapshots\n"
        "  -C CALLSET   calls to compare (default is every frame)\n"
        "  -core        use core profile\n"
        "  -db          use a double buffer visual (default)\n"
        "  -sb          use a single buffer visual\n"
        "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
        "  -S CALLSET   calls to snapshot (default is every frame)\n"
        "  -v           increase output verbosity\n"
        "  -D CALLNO    dump state at specific call no\n"
        "  -w           waitOnFinish on final frame\n"
        "  -t           enable threading\n";
}

extern "C"
int main(int argc, char **argv)
{
    using namespace retrace;

    assert(compareFrequency.empty());
    assert(snapshotFrequency.empty());

    int i;
    for (i = 1; i < argc; ++i) {
        const char *arg = argv[i];

        if (arg[0] != '-') {
            break;
        }

        if (!strcmp(arg, "--")) {
            break;
        } else if (!strcmp(arg, "-b")) {
            retrace::debug = false;
            retrace::verbosity = -1;
        } else if (!strcmp(arg, "-c")) {
            comparePrefix = argv[++i];
            if (compareFrequency.empty()) {
                compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
        } else if (!strcmp(arg, "-C")) {
            compareFrequency = trace::CallSet(argv[++i]);
            if (comparePrefix == NULL) {
                comparePrefix = "";
            }
        } else if (!strcmp(arg, "-D")) {
            dumpStateCallNo = atoi(argv[++i]);
            dumpingState = true;
            retrace::verbosity = -2;
        } else if (!strcmp(arg, "-core")) {
            retrace::coreProfile = true;
        } else if (!strcmp(arg, "-db")) {
            retrace::doubleBuffer = true;
        } else if (!strcmp(arg, "-sb")) {
            retrace::doubleBuffer = false;
        } else if (!strcmp(arg, "--help")) {
            usage(argv[0]);
            return 0;
        } else if (!strcmp(arg, "-s")) {
            snapshotPrefix = argv[++i];
            if (snapshotFrequency.empty()) {
                snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
            if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
                os::setBinaryMode(stdout);
                retrace::verbosity = -2;
            }
        } else if (!strcmp(arg, "-S")) {
            snapshotFrequency = trace::CallSet(argv[++i]);
            if (snapshotPrefix == NULL) {
                snapshotPrefix = "";
            }
        } else if (!strcmp(arg, "-v")) {
            ++retrace::verbosity;
        } else if (!strcmp(arg, "-w")) {
            waitOnFinish = true;
        } else if (arg[1] == 'p') {
            retrace::debug = false;
            retrace::profiling = true;
            retrace::verbosity = -1;

            if (!strcmp(arg, "-pcpu")) {
                retrace::profilingCpuTimes = true;
            } else if (!strcmp(arg, "-pgpu")) {
                retrace::profilingGpuTimes = true;
            } else if (!strcmp(arg, "-ppd")) {
                retrace::profilingPixelsDrawn = true;
            }
        } else if (!strcmp(arg, "-t")) {
            use_threads = true;
        } else {
            std::cerr << "error: unknown option " << arg << "\n";
            usage(argv[0]);
            return 1;
        }
    }

    retrace::setUp();
    if (retrace::profiling) {
        retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
    }

    for ( ; i < argc; ++i) {
        if (!retrace::parser.open(argv[i])) {
            std::cerr << "error: failed to open " << argv[i] << "\n";
            return 1;
        }

        retrace::mainLoop();

        retrace::parser.close();
    }

    // XXX: X often hangs on XCloseDisplay
    //retrace::cleanUp();

    return 0;
}

<commit_msg>Don't parse concurrently with retracing.<commit_after>/**************************************************************************
 *
 * Copyright 2011 Jose Fonseca
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include <string.h>
#include <iostream>

#include "glws.hpp"
#include "os_binary.hpp"
#include "os_time.hpp"
#include "os_workqueue.hpp"
#include "image.hpp"
#include "trace_callset.hpp"
#include "trace_dump.hpp"
#include "retrace.hpp"

static bool waitOnFinish = false;
static bool use_threads;

static const char *comparePrefix = NULL;
static const char *snapshotPrefix = NULL;
static trace::CallSet snapshotFrequency;
static trace::CallSet compareFrequency;

static unsigned dumpStateCallNo = ~0;

retrace::Retracer retracer;

namespace retrace {

trace::Parser parser;
trace::Profiler profiler;

static std::map<unsigned long, os::WorkQueue *> thread_wq_map;

int verbosity = 0;
bool debug = true;
bool dumpingState = false;

bool doubleBuffer = true;
bool coreProfile = false;

bool profiling = false;
bool profilingGpuTimes = false;
bool profilingCpuTimes = false;
bool profilingPixelsDrawn = false;

unsigned frameNo = 0;
unsigned callNo = 0;
static bool state_dumped;

class RenderWork : public os::WorkQueueWork
{
	trace::Call *call;
public:
	void run(void);
	RenderWork(trace::Call *_call) { call = _call; }
	~RenderWork(void) { delete call; }
};

class FlushGLWork : public os::WorkQueueWork
{
public:
    void run(void) { flushRendering(); }
};

void
frameComplete(trace::Call &call) {
    ++frameNo;
}

static void
takeSnapshot(unsigned call_no) {
    assert(snapshotPrefix || comparePrefix);

    image::Image *ref = NULL;

    if (comparePrefix) {
        os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
        ref = image::readPNG(filename);
        if (!ref) {
            return;
        }
        if (retrace::verbosity >= 0) {
            std::cout << "Read " << filename << "\n";
        }
    }

    image::Image *src = getSnapshot();
    if (!src) {
        return;
    }

    if (snapshotPrefix) {
        if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
            char comment[21];
            snprintf(comment, sizeof comment, "%u", call_no);
            src->writePNM(std::cout, comment);
        } else {
            os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
            if (src->writePNG(filename) && retrace::verbosity >= 0) {
                std::cout << "Wrote " << filename << "\n";
            }
        }
    }

    if (ref) {
        std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
        delete ref;
    }

    delete src;

    return;
}

void RenderWork::run(void)
{
    bool swapRenderTarget = call->flags &
        trace::CALL_FLAG_SWAP_RENDERTARGET;
    bool doSnapshot = snapshotFrequency.contains(*call) ||
        compareFrequency.contains(*call);

    if (state_dumped)
        return;

    // For calls which cause rendertargets to be swaped, we take the
    // snapshot _before_ swapping the rendertargets.
    if (doSnapshot && swapRenderTarget) {
        if (call->flags & trace::CALL_FLAG_END_FRAME) {
            // For swapbuffers/presents we still use this
            // call number, spite not have been executed yet.
            takeSnapshot(call->no);
        } else {
            // Whereas for ordinate fbo/rendertarget changes we
            // use the previous call's number.
            takeSnapshot(call->no - 1);
        }
    }

    callNo = call->no;
    retracer.retrace(*call);

    if (doSnapshot && !swapRenderTarget)
        takeSnapshot(call->no);

    if (call->no >= dumpStateCallNo && dumpState(std::cout))
        state_dumped = true;
}

static os::WorkQueue *get_work_queue(unsigned long thread_id)
{
    os::WorkQueue *thread;
    std::map<unsigned long, os::WorkQueue *>::iterator it;

    it = thread_wq_map.find(thread_id);
    if (it == thread_wq_map.end()) {
        thread = new os::WorkQueue();
        thread_wq_map[thread_id] = thread;
    } else {
        thread = it->second;
    }

    return thread;
}

static void exit_work_queues(void)
{
    std::map<unsigned long, os::WorkQueue *>::iterator it;

    it = thread_wq_map.begin();
    while (it != thread_wq_map.end()) {
        os::WorkQueue *thread_wq = it->second;

        thread_wq->queue_work(new FlushGLWork);
        thread_wq->flush();
        thread_wq->destroy();
        thread_wq_map.erase(it++);
    }
}

static void do_all_calls(void)
{
    trace::Call *call;
    int prev_thread_id = -1;
    os::WorkQueue *thread_wq = NULL;

    while ((call = parser.parse_call())) {
        RenderWork *render_work = new RenderWork(call);

        if (use_threads) {
            if (prev_thread_id != call->thread_id) {
                if (thread_wq)
                    thread_wq->flush();
                thread_wq = get_work_queue(call->thread_id);
                prev_thread_id = call->thread_id;
            }

            thread_wq->queue_work(render_work);

            // XXX: Flush immediately to avoid race conditions on unprotected
            // static/global variables.
            thread_wq->flush();
        } else {
            render_work->run();
            delete render_work;
        }

        if (state_dumped)
            break;
    }

    exit_work_queues();
}

static void
mainLoop() {
    addCallbacks(retracer);

    long long startTime = 0;
    frameNo = 0;

    startTime = os::getTime();

    do_all_calls();

    if (!use_threads)
        /*
         * Reached the end of trace; if using threads we do the flush
         * when exiting the threads.
         */
        flushRendering();

    long long endTime = os::getTime();
    float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);

    if ((retrace::verbosity >= -1) || (retrace::profiling)) {
        std::cout <<
            "Rendered " << frameNo << " frames"
            " in " <<  timeInterval << " secs,"
            " average of " << (frameNo/timeInterval) << " fps\n";
    }

    if (waitOnFinish) {
        waitForInput();
    } else {
        return;
    }
}

} /* namespace retrace */

static void
usage(const char *argv0) {
    std::cout <<
        "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
        "Replay TRACE.\n"
        "\n"
        "  -b           benchmark mode (no error checking or warning messages)\n"
        "  -pcpu        cpu profiling (cpu times per call)\n"
        "  -pgpu        gpu profiling (gpu times per draw call)\n"
        "  -ppd         pixels drawn profiling (pixels drawn per draw call)\n"
        "  -c PREFIX    compare against snapshots\n"
        "  -C CALLSET   calls to compare (default is every frame)\n"
        "  -core        use core profile\n"
        "  -db          use a double buffer visual (default)\n"
        "  -sb          use a single buffer visual\n"
        "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
        "  -S CALLSET   calls to snapshot (default is every frame)\n"
        "  -v           increase output verbosity\n"
        "  -D CALLNO    dump state at specific call no\n"
        "  -w           waitOnFinish on final frame\n"
        "  -t           enable threading\n";
}

extern "C"
int main(int argc, char **argv)
{
    using namespace retrace;

    assert(compareFrequency.empty());
    assert(snapshotFrequency.empty());

    int i;
    for (i = 1; i < argc; ++i) {
        const char *arg = argv[i];

        if (arg[0] != '-') {
            break;
        }

        if (!strcmp(arg, "--")) {
            break;
        } else if (!strcmp(arg, "-b")) {
            retrace::debug = false;
            retrace::verbosity = -1;
        } else if (!strcmp(arg, "-c")) {
            comparePrefix = argv[++i];
            if (compareFrequency.empty()) {
                compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
        } else if (!strcmp(arg, "-C")) {
            compareFrequency = trace::CallSet(argv[++i]);
            if (comparePrefix == NULL) {
                comparePrefix = "";
            }
        } else if (!strcmp(arg, "-D")) {
            dumpStateCallNo = atoi(argv[++i]);
            dumpingState = true;
            retrace::verbosity = -2;
        } else if (!strcmp(arg, "-core")) {
            retrace::coreProfile = true;
        } else if (!strcmp(arg, "-db")) {
            retrace::doubleBuffer = true;
        } else if (!strcmp(arg, "-sb")) {
            retrace::doubleBuffer = false;
        } else if (!strcmp(arg, "--help")) {
            usage(argv[0]);
            return 0;
        } else if (!strcmp(arg, "-s")) {
            snapshotPrefix = argv[++i];
            if (snapshotFrequency.empty()) {
                snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
            }
            if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
                os::setBinaryMode(stdout);
                retrace::verbosity = -2;
            }
        } else if (!strcmp(arg, "-S")) {
            snapshotFrequency = trace::CallSet(argv[++i]);
            if (snapshotPrefix == NULL) {
                snapshotPrefix = "";
            }
        } else if (!strcmp(arg, "-v")) {
            ++retrace::verbosity;
        } else if (!strcmp(arg, "-w")) {
            waitOnFinish = true;
        } else if (arg[1] == 'p') {
            retrace::debug = false;
            retrace::profiling = true;
            retrace::verbosity = -1;

            if (!strcmp(arg, "-pcpu")) {
                retrace::profilingCpuTimes = true;
            } else if (!strcmp(arg, "-pgpu")) {
                retrace::profilingGpuTimes = true;
            } else if (!strcmp(arg, "-ppd")) {
                retrace::profilingPixelsDrawn = true;
            }
        } else if (!strcmp(arg, "-t")) {
            use_threads = true;
        } else {
            std::cerr << "error: unknown option " << arg << "\n";
            usage(argv[0]);
            return 1;
        }
    }

    retrace::setUp();
    if (retrace::profiling) {
        retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
    }

    for ( ; i < argc; ++i) {
        if (!retrace::parser.open(argv[i])) {
            std::cerr << "error: failed to open " << argv[i] << "\n";
            return 1;
        }

        retrace::mainLoop();

        retrace::parser.close();
    }

    // XXX: X often hangs on XCloseDisplay
    //retrace::cleanUp();

    return 0;
}

<|endoftext|>


<commit_before>//
//  ExecutorImplementation.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 01/05/2022.
//  Copyright © 2022 Thomas Harte. All rights reserved.
//

#ifndef InstructionSets_M68k_ExecutorImplementation_hpp
#define InstructionSets_M68k_ExecutorImplementation_hpp

#include "Perform.hpp"
#include <cassert>

namespace InstructionSet {
namespace M68k {

template <Model model, typename BusHandler>
Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handler) {
	reset();
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::reset() {
	// Establish: supervisor state, all interrupts blocked.
	status_.set_status(0b0010'0011'1000'0000);

	// Seed stack pointer and program counter.
	data_[7] = bus_handler_.template read<uint32_t>(0);
	program_counter_.l = bus_handler_.template read<uint32_t>(4);
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::read(DataSize size, uint32_t address, CPU::SlicedInt32 &value) {
	switch(size) {
		case DataSize::Byte:
			value.b = bus_handler_.template read<uint8_t>(address);
		break;
		case DataSize::Word:
			value.w = bus_handler_.template read<uint16_t>(address);
		break;
		case DataSize::LongWord:
			value.l = bus_handler_.template read<uint32_t>(address);
		break;
	}
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::write(DataSize size, uint32_t address, CPU::SlicedInt32 value) {
	switch(size) {
		case DataSize::Byte:
			 bus_handler_.template write<uint8_t>(address, value.b);
		break;
		case DataSize::Word:
			bus_handler_.template write<uint16_t>(address, value.w);
		break;
		case DataSize::LongWord:
			bus_handler_.template write<uint32_t>(address, value.l);
		break;
	}
}

template <Model model, typename BusHandler>
template <typename IntT> IntT Executor<model, BusHandler>::read_pc() {
	const IntT result = bus_handler_.template read<IntT>(program_counter_.l);

	if constexpr (sizeof(IntT) == 4) {
		program_counter_.l += 4;
	} else {
		program_counter_.l += 2;
	}

	return result;
}

template <Model model, typename BusHandler>
uint32_t Executor<model, BusHandler>::index_8bitdisplacement() {
	// TODO: if not a 68000, check bit 8 for whether this should be a full extension word;
	// also include the scale field even if not.
	const auto extension = read_pc<uint16_t>();
	const auto offset = int8_t(extension);
	const int register_index = (extension >> 11) & 7;
	const uint32_t displacement = (extension & 0x8000) ? address_[register_index].l : data_[register_index].l;
	return offset + (extension & 0x800) ? displacement : uint16_t(displacement);
}

template <Model model, typename BusHandler>
typename Executor<model, BusHandler>::EffectiveAddress Executor<model, BusHandler>::calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index) {
	EffectiveAddress ea;

	switch(instruction.mode(index)) {
		case AddressingMode::None:
			// Permit an uninitialised effective address to be returned;
			// this value shouldn't be used.
		break;

		//
		// Operands that don't have effective addresses, which are returned as values.
		//
		case AddressingMode::DataRegisterDirect:
			ea.value.l = data_[instruction.reg(index)];
			ea.requires_fetch = false;
		break;
		case AddressingMode::AddressRegisterDirect:
			ea.value.l = address_[instruction.reg(index)];
			ea.requires_fetch = false;
		break;
		case AddressingMode::Quick:
			ea.value.l = quick(instruction.operation, opcode);
			ea.requires_fetch = false;
		break;
		case AddressingMode::ImmediateData:
			read(instruction.size(), program_counter_.l, ea.value.l);
			program_counter_.l += (instruction.size() == DataSize::LongWord) ? 4 : 2;
			ea.requires_fetch = false;
		break;

		//
		// Absolute addresses.
		//
		case AddressingMode::AbsoluteShort:
			ea.value.l = int16_t(read_pc<uint16_t>());
			ea.requires_fetch = true;
		break;
		case AddressingMode::AbsoluteLong:
			ea.value.l = read_pc<uint32_t>();
			ea.requires_fetch = true;
		break;

		//
		// Address register indirects.
		//
		case AddressingMode::AddressRegisterIndirect:
			ea.value.l = address_[instruction.reg(index)];
			ea.requires_fetch = true;
		break;
		case AddressingMode::AddressRegisterIndirectWithPostincrement: {
			const auto reg = instruction.reg(index);

			ea.value.l = address_[reg];
			ea.requires_fetch = true;

			switch(instruction.size()) {
				case DataSize::Byte:		address_[reg] += byte_increments[reg];	break;
				case DataSize::Word:		address_[reg] += 2;						break;
				case DataSize::LongWord:	address_[reg] += 4;						break;
			}
		} break;
		case AddressingMode::AddressRegisterIndirectWithPredecrement: {
			const auto reg = instruction.reg(index);

			switch(instruction.size()) {
				case DataSize::Byte:		address_[reg] -= byte_increments[reg];	break;
				case DataSize::Word:		address_[reg] -= 2;						break;
				case DataSize::LongWord:	address_[reg] -= 4;						break;
			}

			ea.value.l = address_[reg];
			ea.requires_fetch = true;
		} break;
		case AddressingMode::AddressRegisterIndirectWithDisplacement:
			ea.value.l = address_[instruction.reg(index)] + int16_t(read_pc<uint16_t>());
			ea.requires_fetch = true;
		break;
		case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
			ea.value.l = address_[instruction.reg(index)] + index_8bitdisplacement();
			ea.requires_fetch = true;
		break;

		//
		// PC-relative addresses.
		//
		// TODO: rephrase these in terms of instruction_address_. Just for security
		// against whatever mutations the PC has been through already to get to here.
		//
		case AddressingMode::ProgramCounterIndirectWithDisplacement:
			ea.value.l = program_counter_.l + int16_t(read_pc<uint16_t>());
			ea.requires_fetch = true;
		break;
		case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
			ea.value.l = program_counter_.l + index_8bitdisplacement();
			ea.requires_fetch = true;
		break;

		default:
			// TODO.
			assert(false);
		break;
	}

	return ea;
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::run_for_instructions(int count) {
	while(count--) {
		// TODO: check interrupt level, trace flag.

		// Read the next instruction.
		instruction_address_ = program_counter_.l;
		const auto opcode = read_pc<uint16_t>();
		const Preinstruction instruction = decoder_.decode(opcode);
		program_counter_.l += 2;

		// TODO: check privilege level.

		// Temporary storage.
		CPU::SlicedInt32 operand_[2];
		EffectiveAddress effective_address_[2];

		// Calculate effective addresses; copy 'addresses' into the
		// operands by default both: (i) because they might be values,
		// rather than addresses; and (ii) then they'll be there for use
		// by LEA and PEA.
		//
		// TODO: this work should be performed by a full Decoder, so that it can be cached.
		effective_address_[0] = calculate_effective_address(instruction, opcode, 0);
		effective_address_[1] = calculate_effective_address(instruction, opcode, 1);
		operand_[0] = effective_address_[0].value;
		operand_[1] = effective_address_[1].value;

		// Obtain the appropriate sequence.
		//
		// TODO: make a decision about whether this goes into a fully-decoded Instruction.
		Sequence<model> sequence(instruction.operation);

		// Perform it.
		while(!sequence.empty()) {
			const auto step = sequence.pop_front();

			switch(step) {
				default: assert(false);	// i.e. TODO

				case Step::FetchOp1:
				case Step::FetchOp2: {
					const auto index = int(step) & 1;

					// If the operand wasn't indirect, it's already fetched.
					if(!effective_address_[index].requires_fetch) continue;

					// TODO: potential bus alignment exception.
					read(instruction.size(), effective_address_[index].value, operand_[index]);
				} break;

				case Step::Perform:
					perform<model>(instruction, operand_[0], operand_[1], status_, this);
				break;

				case Step::StoreOp1:
				case Step::StoreOp2: {
					const auto index = int(step) & 1;

					// If the operand wasn't indirect, store directly to Dn or An.
					if(!effective_address_[index].requires_fetch) {
						// This must be either address or data register indirect.
						assert(
							instruction.mode(index) == AddressingMode::DataRegisterDirect ||
							instruction.mode(index) == AddressingMode::AddressRegisterDirect);

						// TODO: is it worth holding registers as a single block to avoid this conditional?
						if(instruction.mode(index) == AddressingMode::DataRegisterDirect) {
							data_[instruction.reg(index)] = operand_[index];
						} else {
							address_[instruction.reg(index)] = operand_[index];
						}

						break;
					}

					// TODO: potential bus alignment exception.
					write(instruction.size(), effective_address_[index].value, operand_[index]);
				} break;
			}
		}
	}
}

}
}

#endif /* InstructionSets_M68k_ExecutorImplementation_hpp */
<commit_msg>Correct include path.<commit_after>//
//  ExecutorImplementation.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 01/05/2022.
//  Copyright © 2022 Thomas Harte. All rights reserved.
//

#ifndef InstructionSets_M68k_ExecutorImplementation_hpp
#define InstructionSets_M68k_ExecutorImplementation_hpp

#include "../Perform.hpp"
#include <cassert>

namespace InstructionSet {
namespace M68k {

template <Model model, typename BusHandler>
Executor<model, BusHandler>::Executor(BusHandler &handler) : bus_handler_(handler) {
	reset();
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::reset() {
	// Establish: supervisor state, all interrupts blocked.
	status_.set_status(0b0010'0011'1000'0000);

	// Seed stack pointer and program counter.
	data_[7] = bus_handler_.template read<uint32_t>(0);
	program_counter_.l = bus_handler_.template read<uint32_t>(4);
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::read(DataSize size, uint32_t address, CPU::SlicedInt32 &value) {
	switch(size) {
		case DataSize::Byte:
			value.b = bus_handler_.template read<uint8_t>(address);
		break;
		case DataSize::Word:
			value.w = bus_handler_.template read<uint16_t>(address);
		break;
		case DataSize::LongWord:
			value.l = bus_handler_.template read<uint32_t>(address);
		break;
	}
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::write(DataSize size, uint32_t address, CPU::SlicedInt32 value) {
	switch(size) {
		case DataSize::Byte:
			 bus_handler_.template write<uint8_t>(address, value.b);
		break;
		case DataSize::Word:
			bus_handler_.template write<uint16_t>(address, value.w);
		break;
		case DataSize::LongWord:
			bus_handler_.template write<uint32_t>(address, value.l);
		break;
	}
}

template <Model model, typename BusHandler>
template <typename IntT> IntT Executor<model, BusHandler>::read_pc() {
	const IntT result = bus_handler_.template read<IntT>(program_counter_.l);

	if constexpr (sizeof(IntT) == 4) {
		program_counter_.l += 4;
	} else {
		program_counter_.l += 2;
	}

	return result;
}

template <Model model, typename BusHandler>
uint32_t Executor<model, BusHandler>::index_8bitdisplacement() {
	// TODO: if not a 68000, check bit 8 for whether this should be a full extension word;
	// also include the scale field even if not.
	const auto extension = read_pc<uint16_t>();
	const auto offset = int8_t(extension);
	const int register_index = (extension >> 11) & 7;
	const uint32_t displacement = (extension & 0x8000) ? address_[register_index].l : data_[register_index].l;
	return offset + (extension & 0x800) ? displacement : uint16_t(displacement);
}

template <Model model, typename BusHandler>
typename Executor<model, BusHandler>::EffectiveAddress Executor<model, BusHandler>::calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index) {
	EffectiveAddress ea;

	switch(instruction.mode(index)) {
		case AddressingMode::None:
			// Permit an uninitialised effective address to be returned;
			// this value shouldn't be used.
		break;

		//
		// Operands that don't have effective addresses, which are returned as values.
		//
		case AddressingMode::DataRegisterDirect:
			ea.value.l = data_[instruction.reg(index)];
			ea.requires_fetch = false;
		break;
		case AddressingMode::AddressRegisterDirect:
			ea.value.l = address_[instruction.reg(index)];
			ea.requires_fetch = false;
		break;
		case AddressingMode::Quick:
			ea.value.l = quick(instruction.operation, opcode);
			ea.requires_fetch = false;
		break;
		case AddressingMode::ImmediateData:
			read(instruction.size(), program_counter_.l, ea.value.l);
			program_counter_.l += (instruction.size() == DataSize::LongWord) ? 4 : 2;
			ea.requires_fetch = false;
		break;

		//
		// Absolute addresses.
		//
		case AddressingMode::AbsoluteShort:
			ea.value.l = int16_t(read_pc<uint16_t>());
			ea.requires_fetch = true;
		break;
		case AddressingMode::AbsoluteLong:
			ea.value.l = read_pc<uint32_t>();
			ea.requires_fetch = true;
		break;

		//
		// Address register indirects.
		//
		case AddressingMode::AddressRegisterIndirect:
			ea.value.l = address_[instruction.reg(index)];
			ea.requires_fetch = true;
		break;
		case AddressingMode::AddressRegisterIndirectWithPostincrement: {
			const auto reg = instruction.reg(index);

			ea.value.l = address_[reg];
			ea.requires_fetch = true;

			switch(instruction.size()) {
				case DataSize::Byte:		address_[reg] += byte_increments[reg];	break;
				case DataSize::Word:		address_[reg] += 2;						break;
				case DataSize::LongWord:	address_[reg] += 4;						break;
			}
		} break;
		case AddressingMode::AddressRegisterIndirectWithPredecrement: {
			const auto reg = instruction.reg(index);

			switch(instruction.size()) {
				case DataSize::Byte:		address_[reg] -= byte_increments[reg];	break;
				case DataSize::Word:		address_[reg] -= 2;						break;
				case DataSize::LongWord:	address_[reg] -= 4;						break;
			}

			ea.value.l = address_[reg];
			ea.requires_fetch = true;
		} break;
		case AddressingMode::AddressRegisterIndirectWithDisplacement:
			ea.value.l = address_[instruction.reg(index)] + int16_t(read_pc<uint16_t>());
			ea.requires_fetch = true;
		break;
		case AddressingMode::AddressRegisterIndirectWithIndex8bitDisplacement:
			ea.value.l = address_[instruction.reg(index)] + index_8bitdisplacement();
			ea.requires_fetch = true;
		break;

		//
		// PC-relative addresses.
		//
		// TODO: rephrase these in terms of instruction_address_. Just for security
		// against whatever mutations the PC has been through already to get to here.
		//
		case AddressingMode::ProgramCounterIndirectWithDisplacement:
			ea.value.l = program_counter_.l + int16_t(read_pc<uint16_t>());
			ea.requires_fetch = true;
		break;
		case AddressingMode::ProgramCounterIndirectWithIndex8bitDisplacement:
			ea.value.l = program_counter_.l + index_8bitdisplacement();
			ea.requires_fetch = true;
		break;

		default:
			// TODO.
			assert(false);
		break;
	}

	return ea;
}

template <Model model, typename BusHandler>
void Executor<model, BusHandler>::run_for_instructions(int count) {
	while(count--) {
		// TODO: check interrupt level, trace flag.

		// Read the next instruction.
		instruction_address_ = program_counter_.l;
		const auto opcode = read_pc<uint16_t>();
		const Preinstruction instruction = decoder_.decode(opcode);
		program_counter_.l += 2;

		// TODO: check privilege level.

		// Temporary storage.
		CPU::SlicedInt32 operand_[2];
		EffectiveAddress effective_address_[2];

		// Calculate effective addresses; copy 'addresses' into the
		// operands by default both: (i) because they might be values,
		// rather than addresses; and (ii) then they'll be there for use
		// by LEA and PEA.
		//
		// TODO: this work should be performed by a full Decoder, so that it can be cached.
		effective_address_[0] = calculate_effective_address(instruction, opcode, 0);
		effective_address_[1] = calculate_effective_address(instruction, opcode, 1);
		operand_[0] = effective_address_[0].value;
		operand_[1] = effective_address_[1].value;

		// Obtain the appropriate sequence.
		//
		// TODO: make a decision about whether this goes into a fully-decoded Instruction.
		Sequence<model> sequence(instruction.operation);

		// Perform it.
		while(!sequence.empty()) {
			const auto step = sequence.pop_front();

			switch(step) {
				default: assert(false);	// i.e. TODO

				case Step::FetchOp1:
				case Step::FetchOp2: {
					const auto index = int(step) & 1;

					// If the operand wasn't indirect, it's already fetched.
					if(!effective_address_[index].requires_fetch) continue;

					// TODO: potential bus alignment exception.
					read(instruction.size(), effective_address_[index].value, operand_[index]);
				} break;

				case Step::Perform:
					perform<model>(instruction, operand_[0], operand_[1], status_, this);
				break;

				case Step::StoreOp1:
				case Step::StoreOp2: {
					const auto index = int(step) & 1;

					// If the operand wasn't indirect, store directly to Dn or An.
					if(!effective_address_[index].requires_fetch) {
						// This must be either address or data register indirect.
						assert(
							instruction.mode(index) == AddressingMode::DataRegisterDirect ||
							instruction.mode(index) == AddressingMode::AddressRegisterDirect);

						// TODO: is it worth holding registers as a single block to avoid this conditional?
						if(instruction.mode(index) == AddressingMode::DataRegisterDirect) {
							data_[instruction.reg(index)] = operand_[index];
						} else {
							address_[instruction.reg(index)] = operand_[index];
						}

						break;
					}

					// TODO: potential bus alignment exception.
					write(instruction.size(), effective_address_[index].value, operand_[index]);
				} break;
			}
		}
	}
}

}
}

#endif /* InstructionSets_M68k_ExecutorImplementation_hpp */
<|endoftext|>


<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkNavigationDataEvaluationFilter.h"
#include "mitkTestingMacros.h"

/**Documentation
* NavigationDataEvaluationFilter has a protected constructor and a protected itkNewMacro
* so that only it's friend class ClaronTrackingDevice is able to instantiate
* tool objects. Therefore, we derive from NavigationDataEvaluationFilter and add a
* public itkNewMacro, so that we can instantiate and test the class
*/
class NavigationDataEvaluationFilterTestClass : public mitk::NavigationDataEvaluationFilter
{

public:

static void TestInstantiation()
    {
    // let's create an object of our class
    mitk::NavigationDataEvaluationFilter::Pointer myNavigationDataEvaluationFilter = mitk::NavigationDataEvaluationFilter::New();
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter.IsNotNull(),"Testing instantiation")
    }

static void TestSimpleCase()
    {
    MITK_TEST_OUTPUT(<< "Starting simple test case...");
    mitk::NavigationData::Pointer testData = mitk::NavigationData::New();
    mitk::NavigationDataEvaluationFilter::Pointer myNavigationDataEvaluationFilter = mitk::NavigationDataEvaluationFilter::New();
    myNavigationDataEvaluationFilter->SetInput(testData);
    myNavigationDataEvaluationFilter->Update();
    testData->SetDataValid(true);
    myNavigationDataEvaluationFilter->Update();
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0)==1,".. Testing GetNumberOfAnalysedNavigationData");
    mitk::Point3D test;
    test[0] = 1;
    test[1] = 1;
    test[2] = 1;
    testData->SetPosition(test);
    myNavigationDataEvaluationFilter->Update();
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0)==2,".. Testing GetNumberOfAnalysedNavigationData (2nd test)");
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfInvalidSamples(0)==1,".. Testing GetNumberOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPercentageOfInvalidSamples(0),33.33333333),".. Testing GetPercentageOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED((myNavigationDataEvaluationFilter->GetPositionStandardDeviation(0)[0]==0.5),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionSampleStandardDeviation(0)[0],0.70710672),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMean(0),0.8660254),".. Testing GetPositionErrorMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorRMS(0),0.8660254),".. Testing GetPositionErrorRMS");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMax(0),0.8660254),".. Testing GetPositionErrorMax");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMedian(0),0.8660254),".. Testing GetPositionErrorMedian");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMin(0),0.8660254),".. Testing GetPositionErrorMin");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorSampleStandardDeviation(0),0),".. Testing GetPositionErrorSampleStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorStandardDeviation(0),0),".. Testing GetPositionErrorStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionMean(0)[0],0),".. Testing GetQuaternionMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionStandardDeviation(0)[0],0),".. Testing GetQuaternionStandardDeviation");
    myNavigationDataEvaluationFilter->ResetStatistic();
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0),0),".. Testing ResetStatistic");
    }
static void TestComplexCase()
    {
    MITK_TEST_OUTPUT(<< "Starting complex test case...");
    mitk::NavigationData::Pointer testData = mitk::NavigationData::New();
    mitk::NavigationDataEvaluationFilter::Pointer myNavigationDataEvaluationFilter = mitk::NavigationDataEvaluationFilter::New();
    myNavigationDataEvaluationFilter->SetInput(testData);
    testData->SetDataValid(true);
    mitk::Point3D testPoint;

    //1st point
    mitk::FillVector3D(testPoint,0,1,0);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //2nd point
    mitk::FillVector3D(testPoint,0,1,0.34);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //3rd point
    mitk::FillVector3D(testPoint,1,0.5,1);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //4th point
    mitk::FillVector3D(testPoint,15,3,2);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //5th point
    mitk::FillVector3D(testPoint,2,22.5,1.2655);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //6th point
    mitk::FillVector3D(testPoint,4,1.3,2);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //7th point
    mitk::FillVector3D(testPoint,0.001,0,1);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //8th point
    mitk::FillVector3D(testPoint,1.2525,2.22,3);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //9th point
    mitk::FillVector3D(testPoint,3.1,3,1);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0)==9,".. Testing GetNumberOfAnalysedNavigationData");
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfInvalidSamples(0)==0,".. Testing GetNumberOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPercentageOfInvalidSamples(0),0),".. Testing GetPercentageOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionStandardDeviation(0)[2],0.86614074),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionSampleStandardDeviation(0)[2],0.91868098),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMean(0),6.06656587),".. Testing GetPositionErrorMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorRMS(0),8.0793161),".. Testing GetPositionErrorRMS");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMax(0),18.6875241),".. Testing GetPositionErrorMax");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMedian(0),4.18522229),".. Testing GetPositionErrorMedian");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMin(0),0.90082741),".. Testing GetPositionErrorMin");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorSampleStandardDeviation(0),5.65960626),".. Testing GetPositionErrorSampleStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorStandardDeviation(0),5.33592795),".. Testing GetPositionErrorStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionMean(0)[0],0),".. Testing GetQuaternionMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionStandardDeviation(0)[0],0),".. Testing GetQuaternionStandardDeviation");
    myNavigationDataEvaluationFilter->ResetStatistic();
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0),0),".. Testing ResetStatistic");

    }
};

/**
 * This function tests the NavigationDataEvaluationFilter class.
 */
int mitkNavigationDataEvaluationFilterTest(int /* argc */, char* /*argv*/[])
{
  // always start with this!
  MITK_TEST_BEGIN("NavigationDataEvaluationFilter")

  NavigationDataEvaluationFilterTestClass::TestInstantiation();
  NavigationDataEvaluationFilterTestClass::TestSimpleCase();
  NavigationDataEvaluationFilterTestClass::TestComplexCase();

  // always end with this!
  MITK_TEST_END()
}
<commit_msg>Added correct epsiolons in test.<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkNavigationDataEvaluationFilter.h"
#include "mitkTestingMacros.h"

/**Documentation
* NavigationDataEvaluationFilter has a protected constructor and a protected itkNewMacro
* so that only it's friend class ClaronTrackingDevice is able to instantiate
* tool objects. Therefore, we derive from NavigationDataEvaluationFilter and add a
* public itkNewMacro, so that we can instantiate and test the class
*/
class NavigationDataEvaluationFilterTestClass : public mitk::NavigationDataEvaluationFilter
{

public:

static void TestInstantiation()
    {
    // let's create an object of our class
    mitk::NavigationDataEvaluationFilter::Pointer myNavigationDataEvaluationFilter = mitk::NavigationDataEvaluationFilter::New();
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter.IsNotNull(),"Testing instantiation")
    }

static void TestSimpleCase()
    {
    MITK_TEST_OUTPUT(<< "Starting simple test case...");
    mitk::NavigationData::Pointer testData = mitk::NavigationData::New();
    mitk::NavigationDataEvaluationFilter::Pointer myNavigationDataEvaluationFilter = mitk::NavigationDataEvaluationFilter::New();
    myNavigationDataEvaluationFilter->SetInput(testData);
    myNavigationDataEvaluationFilter->Update();
    testData->SetDataValid(true);
    myNavigationDataEvaluationFilter->Update();
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0)==1,".. Testing GetNumberOfAnalysedNavigationData");
    mitk::Point3D test;
    test[0] = 1;
    test[1] = 1;
    test[2] = 1;
    testData->SetPosition(test);
    myNavigationDataEvaluationFilter->Update();
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0)==2,".. Testing GetNumberOfAnalysedNavigationData (2nd test)");
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfInvalidSamples(0)==1,".. Testing GetNumberOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPercentageOfInvalidSamples(0),33.33333333, 1E-7),".. Testing GetPercentageOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED((myNavigationDataEvaluationFilter->GetPositionStandardDeviation(0)[0]==0.5),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionSampleStandardDeviation(0)[0],0.70710672, 1E-7),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMean(0),0.8660254, 1E-6),".. Testing GetPositionErrorMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorRMS(0),0.8660254, 1E-6),".. Testing GetPositionErrorRMS");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMax(0),0.8660254, 1E-6),".. Testing GetPositionErrorMax");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMedian(0),0.8660254, 1E-6),".. Testing GetPositionErrorMedian");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMin(0),0.8660254, 1E-6),".. Testing GetPositionErrorMin");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorSampleStandardDeviation(0),0),".. Testing GetPositionErrorSampleStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorStandardDeviation(0),0),".. Testing GetPositionErrorStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionMean(0)[0],0),".. Testing GetQuaternionMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionStandardDeviation(0)[0],0),".. Testing GetQuaternionStandardDeviation");
    myNavigationDataEvaluationFilter->ResetStatistic();
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0),0),".. Testing ResetStatistic");
    }
static void TestComplexCase()
    {
    MITK_TEST_OUTPUT(<< "Starting complex test case...");
    mitk::NavigationData::Pointer testData = mitk::NavigationData::New();
    mitk::NavigationDataEvaluationFilter::Pointer myNavigationDataEvaluationFilter = mitk::NavigationDataEvaluationFilter::New();
    myNavigationDataEvaluationFilter->SetInput(testData);
    testData->SetDataValid(true);
    mitk::Point3D testPoint;

    //1st point
    mitk::FillVector3D(testPoint,0,1,0);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //2nd point
    mitk::FillVector3D(testPoint,0,1,0.34);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //3rd point
    mitk::FillVector3D(testPoint,1,0.5,1);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //4th point
    mitk::FillVector3D(testPoint,15,3,2);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //5th point
    mitk::FillVector3D(testPoint,2,22.5,1.2655);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //6th point
    mitk::FillVector3D(testPoint,4,1.3,2);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //7th point
    mitk::FillVector3D(testPoint,0.001,0,1);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //8th point
    mitk::FillVector3D(testPoint,1.2525,2.22,3);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    //9th point
    mitk::FillVector3D(testPoint,3.1,3,1);
    testData->SetPosition(testPoint);
    myNavigationDataEvaluationFilter->Update();

    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0)==9,".. Testing GetNumberOfAnalysedNavigationData");
    MITK_TEST_CONDITION_REQUIRED(myNavigationDataEvaluationFilter->GetNumberOfInvalidSamples(0)==0,".. Testing GetNumberOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPercentageOfInvalidSamples(0),0),".. Testing GetPercentageOfInvalidSamples");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionStandardDeviation(0)[2],0.86614074, 1E-7),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionSampleStandardDeviation(0)[2],0.91868098, 1E-7),".. Testing GetPositionStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMean(0),6.06656587, 1E-7),".. Testing GetPositionErrorMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorRMS(0),8.0793161, 1E-6),".. Testing GetPositionErrorRMS");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMax(0),18.6875241, 1E-6),".. Testing GetPositionErrorMax");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMedian(0),4.18522229, 1E-7),".. Testing GetPositionErrorMedian");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorMin(0),0.90082741, 1E-7),".. Testing GetPositionErrorMin");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorSampleStandardDeviation(0),5.65960626, 1E-7),".. Testing GetPositionErrorSampleStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetPositionErrorStandardDeviation(0),5.33592795, 1E-7),".. Testing GetPositionErrorStandardDeviation");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionMean(0)[0],0),".. Testing GetQuaternionMean");
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetQuaternionStandardDeviation(0)[0],0),".. Testing GetQuaternionStandardDeviation");
    myNavigationDataEvaluationFilter->ResetStatistic();
    MITK_TEST_CONDITION_REQUIRED(mitk::Equal(myNavigationDataEvaluationFilter->GetNumberOfAnalysedNavigationData(0),0),".. Testing ResetStatistic");

    }
};

/**
 * This function tests the NavigationDataEvaluationFilter class.
 */
int mitkNavigationDataEvaluationFilterTest(int /* argc */, char* /*argv*/[])
{
  // always start with this!
  MITK_TEST_BEGIN("NavigationDataEvaluationFilter")

  NavigationDataEvaluationFilterTestClass::TestInstantiation();
  NavigationDataEvaluationFilterTestClass::TestSimpleCase();
  NavigationDataEvaluationFilterTestClass::TestComplexCase();

  // always end with this!
  MITK_TEST_END()
}
<|endoftext|>


<commit_before>#include "thread_pool.h"
#include <chrono>
#include <cassert>
#include <csignal>
#if __APPLE__ || __linux__
    #include <pthread.h>
#endif

namespace rpp
{
    ///////////////////////////////////////////////////////////////////////////////

    pool_task::pool_task()
    {
        th = thread{[this] { run(); }};
    }

    pool_task::~pool_task() noexcept
    {
        { unique_lock<mutex> lock{m};
            killed = true;
        }
        cv.notify_all();
        th.join();
    }

    void pool_task::run_range(int start, int end, const action<int, int>& newTask) noexcept
    {
        if (taskRunning)
            fprintf(stderr, "rpp::pool_task already running! This can cause a deadlock!\n");

        { lock_guard<mutex> lock{m};
            genericTask  = {};
            rangeTask    = newTask;
            rangeStart   = start;
            rangeEnd     = end;
            taskRunning  = true;
        }
        cv.notify_one();
    }

    void pool_task::run_generic(function<void()>&& newTask) noexcept
    {
        if (taskRunning)
            fprintf(stderr, "rpp::pool_task already running! This can cause a deadlock!\n");

        { lock_guard<mutex> lock{m};
            genericTask  = move(newTask);
            rangeTask    = {};
            rangeStart   = 0;
            rangeEnd     = 0;
            taskRunning  = true;
        }
        cv.notify_one();
    }

    void pool_task::wait(int timeoutMillis) noexcept
    {
        //printf("wait for (%d, %d)\n", loopStart, loopEnd);
        while (taskRunning) // duplicate check is intentional (to avoid lock)
        {
            unique_lock<mutex> lock{m};
            if (!taskRunning)
                return;
            if (timeoutMillis)
            {
                if (cv.wait_for(lock, chrono::milliseconds(timeoutMillis)) == cv_status::timeout)
                    return;
            }
            else
            {
                cv.wait(lock);
            }
        }
    }

    #if _WIN32
    #include <Windows.h>
    #pragma pack(push,8)
    struct THREADNAME_INFO
    {
        DWORD dwType; // Must be 0x1000.
        LPCSTR szName; // Pointer to name (in user addr space).
        DWORD dwThreadID; // Thread ID (-1=caller thread).
        DWORD dwFlags; // Reserved for future use, must be zero.
    };
    #pragma pack(pop)
    void SetThreadName(const char* threadName)
    {
        THREADNAME_INFO info;
        info.dwType     = 0x1000;
        info.szName     = threadName;
        info.dwThreadID = (DWORD)-1;
        info.dwFlags    = 0;

        #pragma warning(push)
        #pragma warning(disable: 6320 6322)
            const DWORD MS_VC_EXCEPTION = 0x406D1388;
            __try {
                RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
            } __except (1){}
        #pragma warning(pop)
    }
    #endif

    //static void segfault(int) { throw runtime_error("SIGSEGV"); }

    void pool_task::run() noexcept
    {
        static int pool_task_id;
        char name[32];
        snprintf(name, sizeof(name), "rpp_task_%d", pool_task_id++);

        #if __APPLE__
            pthread_setname_np(name);
        #elif __linux__
            pthread_setname_np(pthread_self(), name);
        #elif _WIN32
            SetThreadName(name);
        #endif

        //signal(SIGSEGV, segfault); // set SIGSEGV handler so we can catch it

        //printf("%s start\n", name);
        while (!killed)
        {
            //printf("%s wait for task\n", name);
            if (!wait_for_task())
                break;

            try
            {
                decltype(rangeTask)   range;
                decltype(genericTask) generic;

                // consume the tasks atomically
                { lock_guard<mutex> lock{m};
                    range   = move(rangeTask);
                    generic = move(genericTask);
                    rangeTask   = {};
                    genericTask = {}; // BUG: Clang on android doesn't move genericTask properly
                    taskRunning = true;
                }

                if (range)
                {
                    //printf("%s range [%d,%d) \n", name, rangeStart, rangeEnd);
                    range(rangeStart, rangeEnd);
                }
                else
                {
                    //printf("%s task\n", name);
                    generic();
                }
            }
            catch (const exception& e)
            {
                fprintf(stderr, "pool_task::run unhandled exception: %s\n", e.what());
            }
            catch (...) // prevent failure that would terminate the thread
            {
                fprintf(stderr, "pool_task::run unhandled exception!\n");
            }

            { lock_guard<mutex> lock{m};
                taskRunning = false;
                cv.notify_all();
            }
        }
        //printf("%s killed: %d\n", name, killed);
    }

    bool pool_task::got_task() const noexcept
    {
        return (bool)rangeTask || (bool)genericTask;
    }

    bool pool_task::wait_for_task() noexcept
    {
        unique_lock<mutex> lock {m};
        for (;;)
        {
            if (killed)
                return false;
            if ((bool)rangeTask || (bool)genericTask)
                return true;
            cv.wait(lock);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////

    thread_pool thread_pool::global;
    static int core_count;

#if _WIN32
    static int num_physical_cores()
    {
        DWORD bytes = 0;
        GetLogicalProcessorInformation(nullptr, &bytes);
        vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> coreInfo(bytes / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
        GetLogicalProcessorInformation(coreInfo.data(), &bytes);

        int cores = 0;
        for (auto& info : coreInfo)
        {
            if (info.Relationship == RelationProcessorCore)
                ++cores;
        }
        return cores;
    }
#else
    static int num_physical_cores()
    {
        return (int)thread::hardware_concurrency();
    }
#endif

    int thread_pool::physical_cores()
    {
        if (!core_count) core_count = num_physical_cores();
        return core_count;
    }

    thread_pool::thread_pool()
    {
        if (!core_count) core_count = num_physical_cores();
    }

    thread_pool::~thread_pool() noexcept
    {
        // defined destructor to prevent agressive inlining
    }

    int thread_pool::active_tasks() noexcept
    {
        int active = 0;
        for (auto& task : tasks)
            if (task->running()) ++active;
        return active;
    }

    int thread_pool::idle_tasks() noexcept
    {
        int idle = 0;
        for (auto& task : tasks)
            if (!task->running()) ++idle;
        return idle;
    }

    int thread_pool::total_tasks() const noexcept
    {
        return (int)tasks.size();
    }

    int thread_pool::clear_idle_tasks() noexcept
    {
        int cleared = 0;
        for (int i = 0; i < (int)tasks.size();)
        {
            if (!tasks[i]->running())
            {
                swap(tasks[i], tasks.back()); // erase_swap_last pattern
                tasks.pop_back();
                ++cleared;
            }
            else ++i;
        }
        return cleared;
    }

    pool_task* thread_pool::new_task() noexcept
    {
        tasks.emplace_back(make_unique<pool_task>());
        return tasks.back().get();
    }

    pool_task* thread_pool::next_task(size_t& poolIndex) noexcept
    {
        for (; poolIndex < tasks.size(); ++poolIndex)
        {
            pool_task* task = tasks[poolIndex].get();
            if (!task->running())
            {
                ++poolIndex;
                return task;
            }
        }
        return new_task();
    }

    pool_task* thread_pool::get_task() noexcept
    {
        size_t i = 0;
        return next_task(i);
    }

    void thread_pool::parallel_for(int rangeStart, int rangeEnd,
                                   const action<int, int>& rangeTask) noexcept
    {
        assert(!rangeRunning && "Fatal error: nested parallel loops are forbidden!");
        assert(core_count != 0 && "The appears to be no hardware concurrency");

        const int range = rangeEnd - rangeStart;
        if (range <= 0)
            return;

        const int cores = range < core_count ? range : core_count;
        const int len = range / cores;

        // only one physical core or only one task to run. don't run in a thread
        if (cores == 1)
        {
            rangeTask(0, len);
            return;
        }

        pool_task** active = (pool_task**)alloca(sizeof(pool_task*) * cores);
        //vector<pool_task*> active(cores, nullptr);
        {
            lock_guard<mutex> lock{poolMutex};
            rangeRunning = true;

            size_t poolIndex = 0;
            for (int i = 0; i < cores; ++i)
            {
                const int start = i * len;
                const int end   = i == cores - 1 ? rangeEnd : start + len;

                pool_task* task = next_task(poolIndex);
                active[i] = task;
                task->run_range(start, end, rangeTask);
            }
        }

        for (int i = 0; i < cores; ++i)
            active[i]->wait();

        rangeRunning = false;
    }

    pool_task* thread_pool::parallel_task(function<void()>&& genericTask) noexcept
    {
        pool_task* task = get_task();
        task->run_generic(move(genericTask));
        return task;
    }
}
<commit_msg>Updated thread name setting<commit_after>#include "thread_pool.h"
#include <chrono>
#include <cassert>
#include <csignal>
#if __APPLE__ || __linux__
    #include <pthread.h>
    #include <sys/prctl.h>
#endif

namespace rpp
{
    ///////////////////////////////////////////////////////////////////////////////

    pool_task::pool_task()
    {
        th = thread{[this] { run(); }};
    }

    pool_task::~pool_task() noexcept
    {
        { unique_lock<mutex> lock{m};
            killed = true;
        }
        cv.notify_all();
        th.join();
    }

    void pool_task::run_range(int start, int end, const action<int, int>& newTask) noexcept
    {
        if (taskRunning)
            fprintf(stderr, "rpp::pool_task already running! This can cause a deadlock!\n");

        { lock_guard<mutex> lock{m};
            genericTask  = {};
            rangeTask    = newTask;
            rangeStart   = start;
            rangeEnd     = end;
            taskRunning  = true;
        }
        if (th.joinable()) cv.notify_one();
        else {

            th = thread{[this] { run(); }}; // restart thread if needed
        }
    }

    void pool_task::run_generic(function<void()>&& newTask) noexcept
    {
        if (taskRunning)
            fprintf(stderr, "rpp::pool_task already running! This can cause a deadlock!\n");

        { lock_guard<mutex> lock{m};
            genericTask  = move(newTask);
            rangeTask    = {};
            rangeStart   = 0;
            rangeEnd     = 0;
            taskRunning  = true;
        }
        cv.notify_one();
    }

    void pool_task::wait(int timeoutMillis) noexcept
    {
        //printf("wait for (%d, %d)\n", loopStart, loopEnd);
        while (taskRunning) // duplicate check is intentional (to avoid lock)
        {
            unique_lock<mutex> lock{m};
            if (!taskRunning)
                return;
            if (timeoutMillis)
            {
                if (cv.wait_for(lock, chrono::milliseconds(timeoutMillis)) == cv_status::timeout)
                    return;
            }
            else
            {
                cv.wait(lock);
            }
        }
    }

    #if _WIN32
    #include <Windows.h>
    #pragma pack(push,8)
    struct THREADNAME_INFO
    {
        DWORD dwType; // Must be 0x1000.
        LPCSTR szName; // Pointer to name (in user addr space).
        DWORD dwThreadID; // Thread ID (-1=caller thread).
        DWORD dwFlags; // Reserved for future use, must be zero.
    };
    #pragma pack(pop)
    void SetThreadName(const char* threadName)
    {
        THREADNAME_INFO info;
        info.dwType     = 0x1000;
        info.szName     = threadName;
        info.dwThreadID = (DWORD)-1;
        info.dwFlags    = 0;

        #pragma warning(push)
        #pragma warning(disable: 6320 6322)
            const DWORD MS_VC_EXCEPTION = 0x406D1388;
            __try {
                RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
            } __except (1){}
        #pragma warning(pop)
    }
    #endif

    static void segfault(int) { throw runtime_error("SIGSEGV"); }

    void pool_task::run() noexcept
    {
        static int pool_task_id;
        char name[32];
        snprintf(name, sizeof(name), "rpp_task_%d", pool_task_id++);

        #if __APPLE__
            pthread_setname_np(name);
        #elif __linux__
            if (pthread_setname_np(pthread_self(), name)) {
                // failed. try using prctl instead:
                prctl(PR_SET_NAME, name, 0, 0, 0);
            }
        #elif _WIN32
            SetThreadName(name);
        #endif

        signal(SIGSEGV, segfault); // set SIGSEGV handler so we can catch it

        //printf("%s start\n", name);
        while (!killed)
        {
            //printf("%s wait for task\n", name);
            if (!wait_for_task())
                break;

            try
            {
                decltype(rangeTask)   range;
                decltype(genericTask) generic;

                // consume the tasks atomically
                { lock_guard<mutex> lock{m};
                    range   = move(rangeTask);
                    generic = move(genericTask);
                    rangeTask   = {};
                    genericTask = {}; // BUG: Clang on android doesn't move genericTask properly
                    taskRunning = true;
                }

                if (range)
                {
                    //printf("%s range [%d,%d) \n", name, rangeStart, rangeEnd);
                    range(rangeStart, rangeEnd);
                }
                else
                {
                    //printf("%s task\n", name);
                    generic();
                }
            }
            catch (const exception& e)
            {
                fprintf(stderr, "pool_task::run unhandled exception: %s\n", e.what());
            }
            catch (...) // prevent failure that would terminate the thread
            {
                fprintf(stderr, "pool_task::run unhandled exception!\n");
            }

            { lock_guard<mutex> lock{m};
                taskRunning = false;
                cv.notify_all();
            }
        }
        //printf("%s killed: %d\n", name, killed);
    }

    bool pool_task::got_task() const noexcept
    {
        return (bool)rangeTask || (bool)genericTask;
    }

    bool pool_task::wait_for_task() noexcept
    {
        unique_lock<mutex> lock {m};
        for (;;)
        {
            if (killed)
                return false;
            if ((bool)rangeTask || (bool)genericTask)
                return true;
            cv.wait(lock);
        }
    }

    ///////////////////////////////////////////////////////////////////////////////

    thread_pool thread_pool::global;
    static int core_count;

#if _WIN32
    static int num_physical_cores()
    {
        DWORD bytes = 0;
        GetLogicalProcessorInformation(nullptr, &bytes);
        vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> coreInfo(bytes / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
        GetLogicalProcessorInformation(coreInfo.data(), &bytes);

        int cores = 0;
        for (auto& info : coreInfo)
        {
            if (info.Relationship == RelationProcessorCore)
                ++cores;
        }
        return cores;
    }
#else
    static int num_physical_cores()
    {
        return (int)thread::hardware_concurrency();
    }
#endif

    int thread_pool::physical_cores()
    {
        if (!core_count) core_count = num_physical_cores();
        return core_count;
    }

    thread_pool::thread_pool()
    {
        if (!core_count) core_count = num_physical_cores();
    }

    thread_pool::~thread_pool() noexcept
    {
        // defined destructor to prevent agressive inlining
    }

    int thread_pool::active_tasks() noexcept
    {
        int active = 0;
        for (auto& task : tasks)
            if (task->running()) ++active;
        return active;
    }

    int thread_pool::idle_tasks() noexcept
    {
        int idle = 0;
        for (auto& task : tasks)
            if (!task->running()) ++idle;
        return idle;
    }

    int thread_pool::total_tasks() const noexcept
    {
        return (int)tasks.size();
    }

    int thread_pool::clear_idle_tasks() noexcept
    {
        int cleared = 0;
        for (int i = 0; i < (int)tasks.size();)
        {
            if (!tasks[i]->running())
            {
                swap(tasks[i], tasks.back()); // erase_swap_last pattern
                tasks.pop_back();
                ++cleared;
            }
            else ++i;
        }
        return cleared;
    }

    pool_task* thread_pool::new_task() noexcept
    {
        tasks.emplace_back(make_unique<pool_task>());
        return tasks.back().get();
    }

    pool_task* thread_pool::next_task(size_t& poolIndex) noexcept
    {
        for (; poolIndex < tasks.size(); ++poolIndex)
        {
            pool_task* task = tasks[poolIndex].get();
            if (!task->running())
            {
                ++poolIndex;
                return task;
            }
        }
        return new_task();
    }

    pool_task* thread_pool::get_task() noexcept
    {
        size_t i = 0;
        return next_task(i);
    }

    void thread_pool::parallel_for(int rangeStart, int rangeEnd,
                                   const action<int, int>& rangeTask) noexcept
    {
        assert(!rangeRunning && "Fatal error: nested parallel loops are forbidden!");
        assert(core_count != 0 && "The appears to be no hardware concurrency");

        const int range = rangeEnd - rangeStart;
        if (range <= 0)
            return;

        const int cores = range < core_count ? range : core_count;
        const int len = range / cores;

        // only one physical core or only one task to run. don't run in a thread
        if (cores == 1)
        {
            rangeTask(0, len);
            return;
        }

        pool_task** active = (pool_task**)alloca(sizeof(pool_task*) * cores);
        //vector<pool_task*> active(cores, nullptr);
        {
            lock_guard<mutex> lock{poolMutex};
            rangeRunning = true;

            size_t poolIndex = 0;
            for (int i = 0; i < cores; ++i)
            {
                const int start = i * len;
                const int end   = i == cores - 1 ? rangeEnd : start + len;

                pool_task* task = next_task(poolIndex);
                active[i] = task;
                task->run_range(start, end, rangeTask);
            }
        }

        for (int i = 0; i < cores; ++i)
            active[i]->wait();

        rangeRunning = false;
    }

    pool_task* thread_pool::parallel_task(function<void()>&& genericTask) noexcept
    {
        pool_task* task = get_task();
        task->run_generic(move(genericTask));
        return task;
    }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: rschash.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: pl $ $Date: 2001-10-10 11:51:13 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _RSCHASH_HXX
#define _RSCHASH_HXX

#ifndef _RSCTOOLS_HXX
#include <rsctools.hxx>
#endif

/****************** T Y P E S ********************************************/
/****************** C L A S S E S ****************************************/
/****************** S t r i n g - C o n **********************************/
class StringCon{
private:
    USHORT    nMax;     // sizeof character field
    USHORT    nFill;    // index after last entry
    char *    pField;   // pointer to character field
public:
           StringCon( USHORT nMaxEntries );  // maximum numbers of characters
           ~StringCon();
    char   *Put( const char * pStr ); // put string in field
};

/****************** H a s h - T a b e l **********************************/
class HashTabel{
private:
    USHORT      nMax;                 // size of hash-tabel
    USHORT      nFill;                // elements in hash-tabel
    ULONG       lAsk;                 // Anzahl der Anfragen
    ULONG       lTry;                 // Anzahl der Versuche
protected:
    HASHID  Test_Insert( const void *, BOOL bInsert );
public:
    HashTabel( USHORT nMaxEntries );    // max size of hash-tabel
    ~HashTabel();
    HASHID  Test( const void * );             // test of insert
    HASHID  Insert( const void * pElement );  // insert in hashtabel
    virtual USHORT HashFunc( const void * ){ return( 0 ); };
                                        // get hash value from subclass
    virtual BOOL IsEntry( HASHID ){ return( FALSE ); };
                                        // is field
                                        // nIndex = index in Field
    virtual COMPARE Compare( const void * , HASHID )
        { return( EQUAL ); };           // compare element with entry
};

/****************** H a s h - S t r i n g ********************************/
class HashString : public HashTabel{
private:
    StringCon * paSC;           // container of strings
    char * *    ppStr;          // pointer to char * array
public:
            HashString( USHORT nMaxEntries );   // max size of hash-tabel
            ~HashString();
    USHORT  HashFunc( const void * pElement );  // return hash value
    BOOL    IsEntry( HASHID nIndex );
    COMPARE Compare( const void * pElement, HASHID nIndex );
    HASHID  Insert( const char * pStr );        // test of insert string
    HASHID  Test( const char * pStr );          // insert string
    char *  Get( HASHID nIndex );               // return pointer to string
};

#endif // _RSCHASH_HXX

<commit_msg>INTEGRATION: CWS mergebuild (1.2.68); FILE MERGED 2003/12/08 14:46:36 hjs 1.2.68.1: #i8252# fixed commandline problem; generic ids<commit_after>/*************************************************************************
 *
 *  $RCSfile: rschash.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hjs $ $Date: 2004-06-26 20:24:18 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _RSCHASH_HXX
#define _RSCHASH_HXX

#ifndef _RSCTOOLS_HXX
#include <rsctools.hxx>
#endif

/****************** T Y P E S ********************************************/
/****************** C L A S S E S ****************************************/
/****************** S t r i n g - C o n **********************************/
class StringCon{
private:
    USHORT    nMax;     // sizeof character field
    USHORT    nFill;    // index after last entry
    char *    pField;   // pointer to character field
public:
           StringCon( USHORT nMaxEntries );  // maximum numbers of characters
           ~StringCon();
    char   *Put( const char * pStr ); // put string in field
};

/****************** H a s h - T a b e l **********************************/
class HashTabel{
private:
    USHORT      nMax;                 // size of hash-tabel
    USHORT      nFill;                // elements in hash-tabel
    ULONG       lAsk;                 // Anzahl der Anfragen
    ULONG       lTry;                 // Anzahl der Versuche
protected:
    HASHID  Test_Insert( const void *, BOOL bInsert );
public:
    HashTabel( USHORT nMaxEntries );    // max size of hash-tabel
    ~HashTabel();
    HASHID  Test( const void * );             // test of insert
    HASHID  Insert( const void * pElement );  // insert in hashtabel
    virtual USHORT HashFunc( const void * ){ return( 0 ); };
                                        // get hash value from subclass
    virtual BOOL IsEntry( HASHID ){ return( FALSE ); };
                                        // is field
                                        // nIndex = index in Field
    virtual COMPARE Compare( const void * , HASHID )
        { return( EQUAL ); };           // compare element with entry
};

/****************** H a s h - S t r i n g ********************************/
class HashString : public HashTabel{
private:
    StringCon * paSC;           // container of strings
    char * *    ppStr;          // pointer to char * array
public:
            HashString( USHORT nMaxEntries );   // max size of hash-tabel
            ~HashString();
    USHORT  HashFunc( const void * pElement );  // return hash value
    static USHORT  Hash_Func( const void * pElement );  // return hash value
    BOOL    IsEntry( HASHID nIndex );
    COMPARE Compare( const void * pElement, HASHID nIndex );
    HASHID  Insert( const char * pStr );        // test of insert string
    HASHID  Test( const char * pStr );          // insert string
    char *  Get( HASHID nIndex );               // return pointer to string
};

#endif // _RSCHASH_HXX

<|endoftext|>