<commit_before>/* Unit tests for the locally optimal and penalty policy allocators. These
 * require a non-trivial amount of initialization of global shared state in
 * order to run, even if they do not use them, because they are tightly
 * integrated into the rest of XIOSim.
 *
 * Author: Sam Xi
 */

#define CATCH_CONFIG_RUNNER
#include "catch.hpp"

#include <algorithm>
#include <assert.h>
#include <cmath>
#include <fstream>
#include <map>
#include <pthread.h>
#include <sstream>
#include <string>
#include <sys/types.h>
#include <unistd.h>

#include "xiosim/synchronization.h"

#include "allocators_impl.h"
#include "multiprocess_shared.h"
#include "parse_speedup.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"

struct locally_optimal_args {
    int asid;
    int loop_num;
    xiosim::BaseAllocator* allocator;
    int num_cores_alloc;
    bool last_to_check_in;
};
pthread_mutex_t cout_lock;

std::map<int, bool> ackTestMessages;
pthread_mutex_t ackTestMessages_lock;

const int NUM_CORES_TEST = 16;
const size_t NUM_DATA_POINTS = 4;

/* Thread function that calls the Allocate() method in the locally optimal
 * allocator. Tests whether the allocator properly waits for all threads to
 * check in before making a decision.
 */
void* TestLocallyOptimalPolicyThread(void* arg) {
    locally_optimal_args* args = (locally_optimal_args*)arg;
    xiosim::BaseAllocator* allocator = args->allocator;
    int asid = args->asid;
    std::stringstream loop_name;
    loop_name << "art_loop_" << args->loop_num;
    std::vector<double> loop_scaling = GetHelixLoopScaling(loop_name.str());
    double serial_runtime = GetHelixFullLoopData(loop_name.str())->serial_runtime;
    // Push a blocking ack message on to the queue.
    pthread_mutex_lock(&ackTestMessages_lock);
    ackTestMessages[asid] = false;
    pthread_mutex_unlock(&ackTestMessages_lock);
    // Initiate the core allocation request.
    args->num_cores_alloc = allocator->AllocateCoresForProcess(asid, loop_scaling, serial_runtime);
    pthread_mutex_lock(&ackTestMessages_lock);
    // If you are the last thread to check in, unblock all others.
    if (args->num_cores_alloc != -1) {
        for (auto it = ackTestMessages.begin(); it != ackTestMessages.end(); ++it) {
            if (it->second == false)
                ackTestMessages[it->first] = true;
        }
        args->last_to_check_in = true;
    } else {
        // Wait for the final process to check in.
        while (ackTestMessages[asid] == false) {
            pthread_mutex_unlock(&ackTestMessages_lock);
            usleep(1000);
            pthread_mutex_lock(&ackTestMessages_lock);
        }
        // Now get the actual allocation.
        args->num_cores_alloc =
            allocator->AllocateCoresForProcess(asid, loop_scaling, serial_runtime);
        args->last_to_check_in = false;
    }
    pthread_mutex_unlock(&ackTestMessages_lock);
#ifdef DEBUG
    pthread_mutex_lock(&cout_lock);
    std::cout << "Process " << asid << " was allocated " << args->num_cores_alloc << " cores."
              << std::endl;
    pthread_mutex_unlock(&cout_lock);
#endif
    return NULL;
}

TEST_CASE("Penalty allocator", "penalty") {
    using namespace xiosim;
    SHARED_VAR_INIT(int, num_processes);
    *num_processes = 3;
    char* filepath = "xiosim/pintool/test_data/loop_speedup_data.csv";
    // We need a smaller allocation to actuall trigger the penalty policies.
    const int PENALTY_NUM_CORES = 8;
    PenaltyAllocator& core_allocator = reinterpret_cast<PenaltyAllocator&>(
        AllocatorParser::Get("penalty", "energy", "logarithmic", 1, 8, PENALTY_NUM_CORES));
    core_allocator.ResetState();
    LoadHelixSpeedupModelData(filepath);

    std::string loop_1("art_loop_2");
    std::string loop_2("art_loop_1");
    std::string loop_3("art_loop_1");
    int process_1 = 0;
    int process_2 = 1;
    int process_3 = 2;
    std::vector<double> scaling_1 = GetHelixLoopScaling(loop_1);
    double serial_runtime_1 = GetHelixFullLoopData(loop_1)->serial_runtime;
    std::vector<double> scaling_2 = GetHelixLoopScaling(loop_2);
    double serial_runtime_2 = GetHelixFullLoopData(loop_2)->serial_runtime;
    std::vector<double> scaling_3 = GetHelixLoopScaling(loop_3);
    double serial_runtime_3 = GetHelixFullLoopData(loop_3)->serial_runtime;

    REQUIRE(core_allocator.get_penalty_for_asid(process_1) == Approx(0));
    core_allocator.AllocateCoresForProcess(process_1, scaling_1, serial_runtime_1);
    REQUIRE(core_allocator.get_cores_for_asid(process_1) == 2);
    REQUIRE(core_allocator.get_processes_to_unblock(process_1).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_1)[0] == process_1);

    REQUIRE(core_allocator.get_penalty_for_asid(process_2) == Approx(0));
    core_allocator.AllocateCoresForProcess(process_2, scaling_2, serial_runtime_2);
    REQUIRE(core_allocator.get_cores_for_asid(process_2) == 4);
    REQUIRE(core_allocator.get_processes_to_unblock(process_2).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_2)[0] == process_2);

    REQUIRE(core_allocator.get_penalty_for_asid(process_3) == Approx(0));
    core_allocator.AllocateCoresForProcess(process_3, scaling_3, serial_runtime_3);
    REQUIRE(core_allocator.get_cores_for_asid(process_3) == 2);
    REQUIRE(core_allocator.get_processes_to_unblock(process_3).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_3)[0] == process_3);

    core_allocator.DeallocateCoresForProcess(process_1);
    REQUIRE(core_allocator.get_cores_for_asid(process_1) == 1);

    REQUIRE(core_allocator.get_penalty_for_asid(process_1) == Approx(1.666822));
    core_allocator.AllocateCoresForProcess(process_1, scaling_1, serial_runtime_1);
    REQUIRE(core_allocator.get_cores_for_asid(process_1) == 1);
    REQUIRE(core_allocator.get_penalty_for_asid(process_1) == Approx(-3.88100));
    REQUIRE(core_allocator.get_processes_to_unblock(process_1).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_1)[0] == process_1);
}

TEST_CASE("Locally optimal allocator, 1 process", "local_1") {
    using namespace xiosim;
    SHARED_VAR_INIT(int, num_processes);
    const int NUM_TEST_PROCESSES = 1;
    *num_processes = NUM_TEST_PROCESSES;
    char* filepath = "xiosim/pintool/test_data/loop_speedup_data.csv";
    LoadHelixSpeedupModelData(filepath);
#ifdef DEBUG
    std::cout << "Number of processes: " << *num_processes << std::endl;
#endif
    const int NUM_TESTS = 2;
    BaseAllocator& core_allocator =
        AllocatorParser::Get("local", "throughput", "linear", 1, 8, NUM_CORES_TEST);
    core_allocator.ResetState();

    // Initialize test data and correct output.
    int test_loops[NUM_TESTS] = { 1, 2 };
    int correct_output[NUM_TESTS] = { NUM_CORES_TEST, NUM_CORES_TEST };

    for (int i = 0; i < NUM_TESTS; i++) {
        locally_optimal_args arg;
        arg.allocator = &core_allocator;
        arg.loop_num = test_loops[i];
        arg.num_cores_alloc = 0;
        arg.asid = 0;
        TestLocallyOptimalPolicyThread(&arg);
        REQUIRE(arg.num_cores_alloc == correct_output[i]);
    }
}

TEST_CASE("Locally optimal allocator, 2 processes", "local_2") {
    using namespace xiosim;
    SHARED_VAR_INIT(int, num_processes);
    const int NUM_TEST_PROCESSES = 2;
    *num_processes = NUM_TEST_PROCESSES;
    char* filepath = "xiosim/pintool/test_data/loop_speedup_data.csv";
#ifdef DEBUG
    std::cout << "Number of processes: " << *num_processes << std::endl;
#endif
    const int NUM_TESTS = 4;
    BaseAllocator& core_allocator =
        AllocatorParser::Get("local", "throughput", "linear", 1, 8, NUM_CORES_TEST);
    core_allocator.ResetState();
    LoadHelixSpeedupModelData(filepath);

    /* Initialize test data and correct output.
     * test_loops: Each column determines which loops to run.
     * Example: 2nd column = {1,2} means process 0 runs loop_1 and process 1
     * runs loop_2.
     */
    int test_loops[NUM_TEST_PROCESSES][NUM_TESTS] = { { 1, 1, 2, 2 }, { 1, 2, 1, 2 } };
    /* correct_output: Each column is the correct number of cores to be
     * allocated to the process for that row.
     * Example: 2nd column
     */
    int correct_output[NUM_TEST_PROCESSES][NUM_TESTS] = { { 8, 1, 15, 8 }, { 8, 15, 1, 8 } };
    /* The correct contents of the unblock list. */
    int unblock_list[NUM_TEST_PROCESSES] = { 0, 1 };

    // Initialize thread variables.
    pthread_mutex_init(&cout_lock, NULL);
    pthread_mutex_init(&ackTestMessages_lock, NULL);
    pthread_t threads[*num_processes];
    locally_optimal_args args[*num_processes];
    for (int i = 0; i < NUM_TESTS; i++) {
        for (int j = 0; j < NUM_TEST_PROCESSES; j++) {
            args[j].allocator = &core_allocator;
            args[j].loop_num = test_loops[j][i];
            args[j].num_cores_alloc = 0;
            args[j].asid = j;
            pthread_create(&threads[j], NULL, TestLocallyOptimalPolicyThread, (void*)&args[j]);
        }
        void* status;
        for (int j = 0; j < NUM_TEST_PROCESSES; j++) {
            pthread_join(threads[j], &status);
        }
#ifdef DEBUG
        std::cout << "All threads have completed." << std::endl;
#endif
        // Check the unblock list.
        for (int j = 0; j < NUM_TEST_PROCESSES; j++) {
            REQUIRE(args[j].num_cores_alloc == correct_output[j][i]);
            std::vector<int> returned_unblock_list = core_allocator.get_processes_to_unblock(j);
            if (args[j].last_to_check_in) {
                for (int k = 0; k < NUM_TEST_PROCESSES; k++) {
                    REQUIRE(std::find(returned_unblock_list.begin(),
                                      returned_unblock_list.end(),
                                      unblock_list[k]) != returned_unblock_list.end());
                }
            } else {
                REQUIRE(returned_unblock_list.empty());
            }
        }
    }
}

/* We need to call InitSharedState() to set up all the shared memory state
 * required by XIOSIM, but that requires some initial setup from the harness.
 * That setup is mimicked here.
 */
void SetupSharedState() {
    using namespace xiosim::shared;
    using namespace boost::interprocess;
    std::stringstream pidstream;
    pid_t test_pid = getpid();
    pidstream << test_pid;
    std::string shared_memory_key = pidstream.str() + std::string(XIOSIM_SHARED_MEMORY_KEY);
    std::string init_lock_key = pidstream.str() + std::string(XIOSIM_INIT_SHARED_LOCK);
    std::string counter_lock_key = pidstream.str() + std::string(XIOSIM_INIT_COUNTER_KEY);

    // To be safe, we use xiosim::shared::DEFAULT_SHARED_MEMORY_SIZE, or we
    // might run out of shared memory space in InitSharedState().
    global_shm = new managed_shared_memory(
        open_or_create, shared_memory_key.c_str(), DEFAULT_SHARED_MEMORY_SIZE);
    // InitSharedState() will expect this lock and counter to exist.
    named_mutex init_lock(open_or_create, init_lock_key.c_str());
    int* process_counter = global_shm->find_or_construct<int>(counter_lock_key.c_str())();
    *process_counter = 1;
    std::cout << "===========================" << std::endl << " Initializing shared state "
              << std::endl << "===========================" << std::endl;
    InitSharedState(false, test_pid, NUM_CORES_TEST);
}

int main(int argc, char* const argv[]) {
    // Create the shared memory cleanup struct.
    using namespace xiosim::shared;
    using namespace boost::interprocess;
    struct shm_remove {
        shm_remove() { shared_memory_object::remove(XIOSIM_SHARED_MEMORY_KEY); }
        ~shm_remove() { shared_memory_object::remove(XIOSIM_SHARED_MEMORY_KEY); }
    } remover;
    SetupSharedState();

    std::cout << "===========================" << std::endl << "    Beginning unit tests   "
              << std::endl << "===========================" << std::endl;
    int result = Catch::Session().run(argc, argv);
    std::cout << std::endl << "REMEMBER TO CLEAN UP /dev/shm!!" << std::endl;
    delete global_shm;
    return result;
}
<commit_msg>Switch VLAs to vectors<commit_after>/* Unit tests for the locally optimal and penalty policy allocators. These
 * require a non-trivial amount of initialization of global shared state in
 * order to run, even if they do not use them, because they are tightly
 * integrated into the rest of XIOSim.
 *
 * Author: Sam Xi
 */

#define CATCH_CONFIG_RUNNER
#include "catch.hpp"

#include <algorithm>
#include <assert.h>
#include <cmath>
#include <fstream>
#include <map>
#include <pthread.h>
#include <sstream>
#include <string>
#include <sys/types.h>
#include <unistd.h>

#include "xiosim/synchronization.h"

#include "allocators_impl.h"
#include "multiprocess_shared.h"
#include "parse_speedup.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"

struct locally_optimal_args {
    int asid;
    int loop_num;
    xiosim::BaseAllocator* allocator;
    int num_cores_alloc;
    bool last_to_check_in;
};
pthread_mutex_t cout_lock;

std::map<int, bool> ackTestMessages;
pthread_mutex_t ackTestMessages_lock;

const int NUM_CORES_TEST = 16;
const size_t NUM_DATA_POINTS = 4;

/* Thread function that calls the Allocate() method in the locally optimal
 * allocator. Tests whether the allocator properly waits for all threads to
 * check in before making a decision.
 */
void* TestLocallyOptimalPolicyThread(void* arg) {
    locally_optimal_args* args = (locally_optimal_args*)arg;
    xiosim::BaseAllocator* allocator = args->allocator;
    int asid = args->asid;
    std::stringstream loop_name;
    loop_name << "art_loop_" << args->loop_num;
    std::vector<double> loop_scaling = GetHelixLoopScaling(loop_name.str());
    double serial_runtime = GetHelixFullLoopData(loop_name.str())->serial_runtime;
    // Push a blocking ack message on to the queue.
    pthread_mutex_lock(&ackTestMessages_lock);
    ackTestMessages[asid] = false;
    pthread_mutex_unlock(&ackTestMessages_lock);
    // Initiate the core allocation request.
    args->num_cores_alloc = allocator->AllocateCoresForProcess(asid, loop_scaling, serial_runtime);
    pthread_mutex_lock(&ackTestMessages_lock);
    // If you are the last thread to check in, unblock all others.
    if (args->num_cores_alloc != -1) {
        for (auto it = ackTestMessages.begin(); it != ackTestMessages.end(); ++it) {
            if (it->second == false)
                ackTestMessages[it->first] = true;
        }
        args->last_to_check_in = true;
    } else {
        // Wait for the final process to check in.
        while (ackTestMessages[asid] == false) {
            pthread_mutex_unlock(&ackTestMessages_lock);
            usleep(1000);
            pthread_mutex_lock(&ackTestMessages_lock);
        }
        // Now get the actual allocation.
        args->num_cores_alloc =
            allocator->AllocateCoresForProcess(asid, loop_scaling, serial_runtime);
        args->last_to_check_in = false;
    }
    pthread_mutex_unlock(&ackTestMessages_lock);
#ifdef DEBUG
    pthread_mutex_lock(&cout_lock);
    std::cout << "Process " << asid << " was allocated " << args->num_cores_alloc << " cores."
              << std::endl;
    pthread_mutex_unlock(&cout_lock);
#endif
    return NULL;
}

TEST_CASE("Penalty allocator", "penalty") {
    using namespace xiosim;
    SHARED_VAR_INIT(int, num_processes);
    *num_processes = 3;
    char* filepath = "xiosim/pintool/test_data/loop_speedup_data.csv";
    // We need a smaller allocation to actuall trigger the penalty policies.
    const int PENALTY_NUM_CORES = 8;
    PenaltyAllocator& core_allocator = reinterpret_cast<PenaltyAllocator&>(
        AllocatorParser::Get("penalty", "energy", "logarithmic", 1, 8, PENALTY_NUM_CORES));
    core_allocator.ResetState();
    LoadHelixSpeedupModelData(filepath);

    std::string loop_1("art_loop_2");
    std::string loop_2("art_loop_1");
    std::string loop_3("art_loop_1");
    int process_1 = 0;
    int process_2 = 1;
    int process_3 = 2;
    std::vector<double> scaling_1 = GetHelixLoopScaling(loop_1);
    double serial_runtime_1 = GetHelixFullLoopData(loop_1)->serial_runtime;
    std::vector<double> scaling_2 = GetHelixLoopScaling(loop_2);
    double serial_runtime_2 = GetHelixFullLoopData(loop_2)->serial_runtime;
    std::vector<double> scaling_3 = GetHelixLoopScaling(loop_3);
    double serial_runtime_3 = GetHelixFullLoopData(loop_3)->serial_runtime;

    REQUIRE(core_allocator.get_penalty_for_asid(process_1) == Approx(0));
    core_allocator.AllocateCoresForProcess(process_1, scaling_1, serial_runtime_1);
    REQUIRE(core_allocator.get_cores_for_asid(process_1) == 2);
    REQUIRE(core_allocator.get_processes_to_unblock(process_1).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_1)[0] == process_1);

    REQUIRE(core_allocator.get_penalty_for_asid(process_2) == Approx(0));
    core_allocator.AllocateCoresForProcess(process_2, scaling_2, serial_runtime_2);
    REQUIRE(core_allocator.get_cores_for_asid(process_2) == 4);
    REQUIRE(core_allocator.get_processes_to_unblock(process_2).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_2)[0] == process_2);

    REQUIRE(core_allocator.get_penalty_for_asid(process_3) == Approx(0));
    core_allocator.AllocateCoresForProcess(process_3, scaling_3, serial_runtime_3);
    REQUIRE(core_allocator.get_cores_for_asid(process_3) == 2);
    REQUIRE(core_allocator.get_processes_to_unblock(process_3).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_3)[0] == process_3);

    core_allocator.DeallocateCoresForProcess(process_1);
    REQUIRE(core_allocator.get_cores_for_asid(process_1) == 1);

    REQUIRE(core_allocator.get_penalty_for_asid(process_1) == Approx(1.666822));
    core_allocator.AllocateCoresForProcess(process_1, scaling_1, serial_runtime_1);
    REQUIRE(core_allocator.get_cores_for_asid(process_1) == 1);
    REQUIRE(core_allocator.get_penalty_for_asid(process_1) == Approx(-3.88100));
    REQUIRE(core_allocator.get_processes_to_unblock(process_1).size() == 1);
    REQUIRE(core_allocator.get_processes_to_unblock(process_1)[0] == process_1);
}

TEST_CASE("Locally optimal allocator, 1 process", "local_1") {
    using namespace xiosim;
    SHARED_VAR_INIT(int, num_processes);
    const int NUM_TEST_PROCESSES = 1;
    *num_processes = NUM_TEST_PROCESSES;
    char* filepath = "xiosim/pintool/test_data/loop_speedup_data.csv";
    LoadHelixSpeedupModelData(filepath);
#ifdef DEBUG
    std::cout << "Number of processes: " << *num_processes << std::endl;
#endif
    const int NUM_TESTS = 2;
    BaseAllocator& core_allocator =
        AllocatorParser::Get("local", "throughput", "linear", 1, 8, NUM_CORES_TEST);
    core_allocator.ResetState();

    // Initialize test data and correct output.
    int test_loops[NUM_TESTS] = { 1, 2 };
    int correct_output[NUM_TESTS] = { NUM_CORES_TEST, NUM_CORES_TEST };

    for (int i = 0; i < NUM_TESTS; i++) {
        locally_optimal_args arg;
        arg.allocator = &core_allocator;
        arg.loop_num = test_loops[i];
        arg.num_cores_alloc = 0;
        arg.asid = 0;
        TestLocallyOptimalPolicyThread(&arg);
        REQUIRE(arg.num_cores_alloc == correct_output[i]);
    }
}

TEST_CASE("Locally optimal allocator, 2 processes", "local_2") {
    using namespace xiosim;
    SHARED_VAR_INIT(int, num_processes);
    const int NUM_TEST_PROCESSES = 2;
    *num_processes = NUM_TEST_PROCESSES;
    char* filepath = "xiosim/pintool/test_data/loop_speedup_data.csv";
#ifdef DEBUG
    std::cout << "Number of processes: " << *num_processes << std::endl;
#endif
    const int NUM_TESTS = 4;
    BaseAllocator& core_allocator =
        AllocatorParser::Get("local", "throughput", "linear", 1, 8, NUM_CORES_TEST);
    core_allocator.ResetState();
    LoadHelixSpeedupModelData(filepath);

    /* Initialize test data and correct output.
     * test_loops: Each column determines which loops to run.
     * Example: 2nd column = {1,2} means process 0 runs loop_1 and process 1
     * runs loop_2.
     */
    int test_loops[NUM_TEST_PROCESSES][NUM_TESTS] = { { 1, 1, 2, 2 }, { 1, 2, 1, 2 } };
    /* correct_output: Each column is the correct number of cores to be
     * allocated to the process for that row.
     * Example: 2nd column
     */
    int correct_output[NUM_TEST_PROCESSES][NUM_TESTS] = { { 8, 1, 15, 8 }, { 8, 15, 1, 8 } };
    /* The correct contents of the unblock list. */
    int unblock_list[NUM_TEST_PROCESSES] = { 0, 1 };

    // Initialize thread variables.
    pthread_mutex_init(&cout_lock, NULL);
    pthread_mutex_init(&ackTestMessages_lock, NULL);
    std::vector<pthread_t> threads(*num_processes);
    std::vector<locally_optimal_args> args(*num_processes);
    for (int i = 0; i < NUM_TESTS; i++) {
        for (int j = 0; j < NUM_TEST_PROCESSES; j++) {
            args[j].allocator = &core_allocator;
            args[j].loop_num = test_loops[j][i];
            args[j].num_cores_alloc = 0;
            args[j].asid = j;
            pthread_create(&threads[j], NULL, TestLocallyOptimalPolicyThread, (void*)&args[j]);
        }
        void* status;
        for (int j = 0; j < NUM_TEST_PROCESSES; j++) {
            pthread_join(threads[j], &status);
        }
#ifdef DEBUG
        std::cout << "All threads have completed." << std::endl;
#endif
        // Check the unblock list.
        for (int j = 0; j < NUM_TEST_PROCESSES; j++) {
            REQUIRE(args[j].num_cores_alloc == correct_output[j][i]);
            std::vector<int> returned_unblock_list = core_allocator.get_processes_to_unblock(j);
            if (args[j].last_to_check_in) {
                for (int k = 0; k < NUM_TEST_PROCESSES; k++) {
                    REQUIRE(std::find(returned_unblock_list.begin(),
                                      returned_unblock_list.end(),
                                      unblock_list[k]) != returned_unblock_list.end());
                }
            } else {
                REQUIRE(returned_unblock_list.empty());
            }
        }
    }
}

/* We need to call InitSharedState() to set up all the shared memory state
 * required by XIOSIM, but that requires some initial setup from the harness.
 * That setup is mimicked here.
 */
void SetupSharedState() {
    using namespace xiosim::shared;
    using namespace boost::interprocess;
    std::stringstream pidstream;
    pid_t test_pid = getpid();
    pidstream << test_pid;
    std::string shared_memory_key = pidstream.str() + std::string(XIOSIM_SHARED_MEMORY_KEY);
    std::string init_lock_key = pidstream.str() + std::string(XIOSIM_INIT_SHARED_LOCK);
    std::string counter_lock_key = pidstream.str() + std::string(XIOSIM_INIT_COUNTER_KEY);

    // To be safe, we use xiosim::shared::DEFAULT_SHARED_MEMORY_SIZE, or we
    // might run out of shared memory space in InitSharedState().
    global_shm = new managed_shared_memory(
        open_or_create, shared_memory_key.c_str(), DEFAULT_SHARED_MEMORY_SIZE);
    // InitSharedState() will expect this lock and counter to exist.
    named_mutex init_lock(open_or_create, init_lock_key.c_str());
    int* process_counter = global_shm->find_or_construct<int>(counter_lock_key.c_str())();
    *process_counter = 1;
    std::cout << "===========================" << std::endl << " Initializing shared state "
              << std::endl << "===========================" << std::endl;
    InitSharedState(false, test_pid, NUM_CORES_TEST);
}

int main(int argc, char* const argv[]) {
    // Create the shared memory cleanup struct.
    using namespace xiosim::shared;
    using namespace boost::interprocess;
    struct shm_remove {
        shm_remove() { shared_memory_object::remove(XIOSIM_SHARED_MEMORY_KEY); }
        ~shm_remove() { shared_memory_object::remove(XIOSIM_SHARED_MEMORY_KEY); }
    } remover;
    SetupSharedState();

    std::cout << "===========================" << std::endl << "    Beginning unit tests   "
              << std::endl << "===========================" << std::endl;
    int result = Catch::Session().run(argc, argv);
    std::cout << std::endl << "REMEMBER TO CLEAN UP /dev/shm!!" << std::endl;
    delete global_shm;
    return result;
}
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (C) 2012 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file mixer.cpp
 *
 * Control channel input/output mixer and failsafe.
 */

#include <nuttx/config.h>

#include <sys/types.h>
#include <stdbool.h>
#include <string.h>

#include <drivers/drv_pwm_output.h>
#include <drivers/drv_hrt.h>

#include <systemlib/mixer/mixer.h>

extern "C" {
//#define DEBUG
#include "px4io.h"
}

/*
 * Maximum interval in us before FMU signal is considered lost
 */
#define FMU_INPUT_DROP_LIMIT_US		200000

/* XXX need to move the RC_CHANNEL_FUNCTION out of rc_channels.h and into systemlib */
#define ROLL     0
#define PITCH    1
#define YAW      2
#define THROTTLE 3
#define OVERRIDE 4

/* current servo arm/disarm state */
static bool mixer_servos_armed = false;

/* selected control values and count for mixing */
enum mixer_source {
	MIX_NONE,
	MIX_FMU,
	MIX_OVERRIDE,
	MIX_FAILSAFE
};
static mixer_source source;

static int	mixer_callback(uintptr_t handle,
			       uint8_t control_group,
			       uint8_t control_index,
			       float &control);

static MixerGroup mixer_group(mixer_callback, 0);

void
mixer_tick(void)
{
	/* check that we are receiving fresh data from the FMU */
	if ((hrt_absolute_time() - system_state.fmu_data_received_time) > FMU_INPUT_DROP_LIMIT_US) {

		/* too long without FMU input, time to go to failsafe */
		if (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) {
			debug("AP RX timeout");
		}
		r_status_flags |= PX4IO_P_STATUS_FLAGS_OVERRIDE;
		r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FMU_OK | PX4IO_P_STATUS_FLAGS_RAW_PWM);
		r_status_alarms |= PX4IO_P_STATUS_ALARMS_FMU_LOST;
	}

	source = MIX_FAILSAFE;

	/*
	 * Decide which set of controls we're using.
	 */
	if (r_status_flags & PX4IO_P_STATUS_FLAGS_RAW_PWM) {

		/* don't actually mix anything - we already have raw PWM values */
		source = MIX_NONE;

	} else {

		if (!(r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
		     (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK)) {

			/* mix from FMU controls */
			source = MIX_FMU;
		}

		if ( (r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
		     (r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK)) {

		 	/* if allowed, mix from RC inputs directly */
			source = MIX_OVERRIDE;
		}
	}

	/*
	 * Run the mixers.
	 */
	if (source == MIX_FAILSAFE) {

		/* copy failsafe values to the servo outputs */
		for (unsigned i = 0; i < IO_SERVO_COUNT; i++)
			r_page_servos[i] = r_page_servo_failsafe[i];

	} else if (source != MIX_NONE) {

		float	outputs[IO_SERVO_COUNT];
		unsigned mixed;

		/* mix */
		mixed = mixer_group.mix(&outputs[0], IO_SERVO_COUNT);

		/* scale to PWM and update the servo outputs as required */
		for (unsigned i = 0; i < mixed; i++) {

			/* save actuator values for FMU readback */
			r_page_actuators[i] = FLOAT_TO_REG(outputs[i]);

			/* scale to servo output */
			r_page_servos[i] = (outputs[i] * 500.0f) + 1500;

		}
		for (unsigned i = mixed; i < IO_SERVO_COUNT; i++)
			r_page_servos[i] = 0;
	}

#if 0
		/* if everything is ok */

		if (!system_state.mixer_manual_override && system_state.mixer_fmu_available) {
			/* we have recent control data from the FMU */
			control_count = PX4IO_CONTROL_CHANNELS;
			control_values = &system_state.fmu_channel_data[0];

		} else if (system_state.rc_channels > 0) {
			/* when override is on or the fmu is not available, but RC is present */
			control_count = system_state.rc_channels;

			sched_lock();

			/* remap roll, pitch, yaw and throttle from RC specific to internal ordering */
			rc_channel_data[ROLL]     = system_state.rc_channel_data[system_state.rc_map[ROLL] - 1];
			rc_channel_data[PITCH]    = system_state.rc_channel_data[system_state.rc_map[PITCH] - 1];
			rc_channel_data[YAW]      = system_state.rc_channel_data[system_state.rc_map[YAW] - 1];
			rc_channel_data[THROTTLE] = system_state.rc_channel_data[system_state.rc_map[THROTTLE] - 1];
			//rc_channel_data[OVERRIDE] = system_state.rc_channel_data[system_state.rc_map[OVERRIDE] - 1];

			/* get the remaining channels, no remapping needed */
			for (unsigned i = 4; i < system_state.rc_channels; i++) {
				rc_channel_data[i] = system_state.rc_channel_data[i];
			}

			/* scale the control inputs */
			rc_channel_data[THROTTLE] = ((float)(rc_channel_data[THROTTLE] - system_state.rc_min[THROTTLE]) /
				(float)(system_state.rc_max[THROTTLE] - system_state.rc_min[THROTTLE])) * 1000.0f + 1000;

			if (rc_channel_data[THROTTLE] > 2000) {
				rc_channel_data[THROTTLE] = 2000;
			}

			if (rc_channel_data[THROTTLE] < 1000) {
				rc_channel_data[THROTTLE] = 1000;
			}

			// lowsyslog("Tmin: %d Ttrim: %d Tmax: %d T: %d \n",
			// 	(int)(system_state.rc_min[THROTTLE]), (int)(system_state.rc_trim[THROTTLE]),
			// 	(int)(system_state.rc_max[THROTTLE]), (int)(rc_channel_data[THROTTLE]));

			control_values = &rc_channel_data[0];
			sched_unlock();
		} else {
			/* we have no control input (no FMU, no RC) */

			// XXX builtin failsafe would activate here
			control_count = 0;
		}
		//lowsyslog("R: %d P: %d Y: %d T: %d \n", control_values[0], control_values[1], control_values[2], control_values[3]);

	/* this is for multicopters, etc. where manual override does not make sense */
	} else {
		/* if the fmu is available whe are good */
		if (system_state.mixer_fmu_available) {
			control_count = PX4IO_CONTROL_CHANNELS;
			control_values = &system_state.fmu_channel_data[0];
		/* we better shut everything off */
		} else {
			control_count = 0;
		}
	}
#endif

	/*
	 * Decide whether the servos should be armed right now.
	 *
	 * We must be armed, and we must have a PWM source; either raw from
	 * FMU or from the mixer.
	 *
	 * XXX correct behaviour for failsafe may require an additional case
	 * here.
	 */
	bool should_arm = (/* FMU is armed */ (r_setup_arming & PX4IO_P_SETUP_ARMING_ARM_OK) &&
	 		   /* IO is armed */  (r_status_flags & PX4IO_P_STATUS_FLAGS_ARMED) &&
	/* there is valid input */ (r_status_flags & (PX4IO_P_STATUS_FLAGS_RAW_PWM | PX4IO_P_STATUS_FLAGS_MIXER_OK)));

	if (should_arm && !mixer_servos_armed) {
		/* need to arm, but not armed */
		up_pwm_servo_arm(true);
		mixer_servos_armed = true;

	} else if (!should_arm && mixer_servos_armed) {
		/* armed but need to disarm */
		up_pwm_servo_arm(false);
		mixer_servos_armed = false;
	}

	if (mixer_servos_armed) {
		/* update the servo outputs. */
		for (unsigned i = 0; i < IO_SERVO_COUNT; i++)
			up_pwm_servo_set(i, r_page_servos[i]);
	}
}

static int
mixer_callback(uintptr_t handle,
	       uint8_t control_group,
	       uint8_t control_index,
	       float &control)
{
	if (control_group != 0)
		return -1;

	switch (source) {
	case MIX_FMU:
		if (control_index < PX4IO_CONTROL_CHANNELS) {
			control = REG_TO_FLOAT(r_page_controls[control_index]);
			break;
		}
		return -1;

	case MIX_OVERRIDE:
		if (r_page_rc_input[PX4IO_P_RC_VALID] & (1 << control_index)) {
			control = REG_TO_FLOAT(r_page_rc_input[PX4IO_P_RC_BASE + control_index]);
			break;
		}
		return -1;

	case MIX_FAILSAFE:
	case MIX_NONE:
		/* XXX we could allow for configuration of per-output failsafe values */
		return -1;
	}

	return 0;
}

/*
 * XXX error handling here should be more aggressive; currently it is
 * possible to get STATUS_FLAGS_MIXER_OK set even though the mixer has
 * not loaded faithfully.
 */

static char mixer_text[256];		/* large enough for one mixer */
static unsigned mixer_text_length = 0;

void
mixer_handle_text(const void *buffer, size_t length)
{

	px4io_mixdata	*msg = (px4io_mixdata *)buffer;

	debug("mixer text %u", length);

	if (length < sizeof(px4io_mixdata))
		return;

	unsigned	text_length = length - sizeof(px4io_mixdata);

	switch (msg->action) {
	case F2I_MIXER_ACTION_RESET:
		debug("reset");
		mixer_group.reset();
		mixer_text_length = 0;
		r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;

		/* FALLTHROUGH */
	case F2I_MIXER_ACTION_APPEND:
		debug("append %d", length);

		/* check for overflow - this is really fatal */
		/* XXX could add just what will fit & try to parse, then repeat... */
		if ((mixer_text_length + text_length + 1) > sizeof(mixer_text)) {
			r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;
			return;
		}

		/* append mixer text and nul-terminate */
		memcpy(&mixer_text[mixer_text_length], msg->text, text_length);
		mixer_text_length += text_length;
		mixer_text[mixer_text_length] = '\0';
		debug("buflen %u", mixer_text_length);

		/* process the text buffer, adding new mixers as their descriptions can be parsed */
		unsigned resid = mixer_text_length;
		mixer_group.load_from_buf(&mixer_text[0], resid);

		/* if anything was parsed */
		if (resid != mixer_text_length) {

			/* ideally, this should test resid == 0 ? */
			r_status_flags |= PX4IO_P_STATUS_FLAGS_MIXER_OK;

			debug("used %u", mixer_text_length - resid);

			/* copy any leftover text to the base of the buffer for re-use */
			if (resid > 0)
				memcpy(&mixer_text[0], &mixer_text[mixer_text_length - resid], resid);

			mixer_text_length = resid;
		}

		break;
	}
}
<commit_msg>Switched to debug statement which is more efficient regarding stack usage, only printing at debug level 2 or higher.<commit_after>/****************************************************************************
 *
 *   Copyright (C) 2012 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file mixer.cpp
 *
 * Control channel input/output mixer and failsafe.
 */

#include <nuttx/config.h>

#include <sys/types.h>
#include <stdbool.h>
#include <string.h>

#include <drivers/drv_pwm_output.h>
#include <drivers/drv_hrt.h>

#include <systemlib/mixer/mixer.h>

extern "C" {
//#define DEBUG
#include "px4io.h"
}

/*
 * Maximum interval in us before FMU signal is considered lost
 */
#define FMU_INPUT_DROP_LIMIT_US		200000

/* XXX need to move the RC_CHANNEL_FUNCTION out of rc_channels.h and into systemlib */
#define ROLL     0
#define PITCH    1
#define YAW      2
#define THROTTLE 3
#define OVERRIDE 4

/* current servo arm/disarm state */
static bool mixer_servos_armed = false;

/* selected control values and count for mixing */
enum mixer_source {
	MIX_NONE,
	MIX_FMU,
	MIX_OVERRIDE,
	MIX_FAILSAFE
};
static mixer_source source;

static int	mixer_callback(uintptr_t handle,
			       uint8_t control_group,
			       uint8_t control_index,
			       float &control);

static MixerGroup mixer_group(mixer_callback, 0);

void
mixer_tick(void)
{
	/* check that we are receiving fresh data from the FMU */
	if ((hrt_absolute_time() - system_state.fmu_data_received_time) > FMU_INPUT_DROP_LIMIT_US) {

		/* too long without FMU input, time to go to failsafe */
		if (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) {
			debug("AP RX timeout");
		}
		r_status_flags |= PX4IO_P_STATUS_FLAGS_OVERRIDE;
		r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FMU_OK | PX4IO_P_STATUS_FLAGS_RAW_PWM);
		r_status_alarms |= PX4IO_P_STATUS_ALARMS_FMU_LOST;
	}

	source = MIX_FAILSAFE;

	/*
	 * Decide which set of controls we're using.
	 */
	if (r_status_flags & PX4IO_P_STATUS_FLAGS_RAW_PWM) {

		/* don't actually mix anything - we already have raw PWM values */
		source = MIX_NONE;

	} else {

		if (!(r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
		     (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK)) {

			/* mix from FMU controls */
			source = MIX_FMU;
		}

		if ( (r_status_flags & PX4IO_P_STATUS_FLAGS_OVERRIDE) &&
		     (r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK)) {

		 	/* if allowed, mix from RC inputs directly */
			source = MIX_OVERRIDE;
		}
	}

	/*
	 * Run the mixers.
	 */
	if (source == MIX_FAILSAFE) {

		/* copy failsafe values to the servo outputs */
		for (unsigned i = 0; i < IO_SERVO_COUNT; i++)
			r_page_servos[i] = r_page_servo_failsafe[i];

	} else if (source != MIX_NONE) {

		float	outputs[IO_SERVO_COUNT];
		unsigned mixed;

		/* mix */
		mixed = mixer_group.mix(&outputs[0], IO_SERVO_COUNT);

		/* scale to PWM and update the servo outputs as required */
		for (unsigned i = 0; i < mixed; i++) {

			/* save actuator values for FMU readback */
			r_page_actuators[i] = FLOAT_TO_REG(outputs[i]);

			/* scale to servo output */
			r_page_servos[i] = (outputs[i] * 500.0f) + 1500;

		}
		for (unsigned i = mixed; i < IO_SERVO_COUNT; i++)
			r_page_servos[i] = 0;
	}

#if 0
		/* if everything is ok */

		if (!system_state.mixer_manual_override && system_state.mixer_fmu_available) {
			/* we have recent control data from the FMU */
			control_count = PX4IO_CONTROL_CHANNELS;
			control_values = &system_state.fmu_channel_data[0];

		} else if (system_state.rc_channels > 0) {
			/* when override is on or the fmu is not available, but RC is present */
			control_count = system_state.rc_channels;

			sched_lock();

			/* remap roll, pitch, yaw and throttle from RC specific to internal ordering */
			rc_channel_data[ROLL]     = system_state.rc_channel_data[system_state.rc_map[ROLL] - 1];
			rc_channel_data[PITCH]    = system_state.rc_channel_data[system_state.rc_map[PITCH] - 1];
			rc_channel_data[YAW]      = system_state.rc_channel_data[system_state.rc_map[YAW] - 1];
			rc_channel_data[THROTTLE] = system_state.rc_channel_data[system_state.rc_map[THROTTLE] - 1];
			//rc_channel_data[OVERRIDE] = system_state.rc_channel_data[system_state.rc_map[OVERRIDE] - 1];

			/* get the remaining channels, no remapping needed */
			for (unsigned i = 4; i < system_state.rc_channels; i++) {
				rc_channel_data[i] = system_state.rc_channel_data[i];
			}

			/* scale the control inputs */
			rc_channel_data[THROTTLE] = ((float)(rc_channel_data[THROTTLE] - system_state.rc_min[THROTTLE]) /
				(float)(system_state.rc_max[THROTTLE] - system_state.rc_min[THROTTLE])) * 1000.0f + 1000;

			if (rc_channel_data[THROTTLE] > 2000) {
				rc_channel_data[THROTTLE] = 2000;
			}

			if (rc_channel_data[THROTTLE] < 1000) {
				rc_channel_data[THROTTLE] = 1000;
			}

			// lowsyslog("Tmin: %d Ttrim: %d Tmax: %d T: %d \n",
			// 	(int)(system_state.rc_min[THROTTLE]), (int)(system_state.rc_trim[THROTTLE]),
			// 	(int)(system_state.rc_max[THROTTLE]), (int)(rc_channel_data[THROTTLE]));

			control_values = &rc_channel_data[0];
			sched_unlock();
		} else {
			/* we have no control input (no FMU, no RC) */

			// XXX builtin failsafe would activate here
			control_count = 0;
		}
		//lowsyslog("R: %d P: %d Y: %d T: %d \n", control_values[0], control_values[1], control_values[2], control_values[3]);

	/* this is for multicopters, etc. where manual override does not make sense */
	} else {
		/* if the fmu is available whe are good */
		if (system_state.mixer_fmu_available) {
			control_count = PX4IO_CONTROL_CHANNELS;
			control_values = &system_state.fmu_channel_data[0];
		/* we better shut everything off */
		} else {
			control_count = 0;
		}
	}
#endif

	/*
	 * Decide whether the servos should be armed right now.
	 *
	 * We must be armed, and we must have a PWM source; either raw from
	 * FMU or from the mixer.
	 *
	 * XXX correct behaviour for failsafe may require an additional case
	 * here.
	 */
	bool should_arm = (/* FMU is armed */ (r_setup_arming & PX4IO_P_SETUP_ARMING_ARM_OK) &&
	 		   /* IO is armed */  (r_status_flags & PX4IO_P_STATUS_FLAGS_ARMED) &&
	/* there is valid input */ (r_status_flags & (PX4IO_P_STATUS_FLAGS_RAW_PWM | PX4IO_P_STATUS_FLAGS_MIXER_OK)));

	if (should_arm && !mixer_servos_armed) {
		/* need to arm, but not armed */
		up_pwm_servo_arm(true);
		mixer_servos_armed = true;

	} else if (!should_arm && mixer_servos_armed) {
		/* armed but need to disarm */
		up_pwm_servo_arm(false);
		mixer_servos_armed = false;
	}

	if (mixer_servos_armed) {
		/* update the servo outputs. */
		for (unsigned i = 0; i < IO_SERVO_COUNT; i++)
			up_pwm_servo_set(i, r_page_servos[i]);
	}
}

static int
mixer_callback(uintptr_t handle,
	       uint8_t control_group,
	       uint8_t control_index,
	       float &control)
{
	if (control_group != 0)
		return -1;

	switch (source) {
	case MIX_FMU:
		if (control_index < PX4IO_CONTROL_CHANNELS) {
			control = REG_TO_FLOAT(r_page_controls[control_index]);
			break;
		}
		return -1;

	case MIX_OVERRIDE:
		if (r_page_rc_input[PX4IO_P_RC_VALID] & (1 << control_index)) {
			control = REG_TO_FLOAT(r_page_rc_input[PX4IO_P_RC_BASE + control_index]);
			break;
		}
		return -1;

	case MIX_FAILSAFE:
	case MIX_NONE:
		/* XXX we could allow for configuration of per-output failsafe values */
		return -1;
	}

	return 0;
}

/*
 * XXX error handling here should be more aggressive; currently it is
 * possible to get STATUS_FLAGS_MIXER_OK set even though the mixer has
 * not loaded faithfully.
 */

static char mixer_text[256];		/* large enough for one mixer */
static unsigned mixer_text_length = 0;

void
mixer_handle_text(const void *buffer, size_t length)
{

	px4io_mixdata	*msg = (px4io_mixdata *)buffer;

	isr_debug(2, "mixer text %u", length);

	if (length < sizeof(px4io_mixdata))
		return;

	unsigned	text_length = length - sizeof(px4io_mixdata);

	switch (msg->action) {
	case F2I_MIXER_ACTION_RESET:
		isr_debug(2, "reset");
		mixer_group.reset();
		mixer_text_length = 0;
		r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;

		/* FALLTHROUGH */
	case F2I_MIXER_ACTION_APPEND:
		isr_debug(2, "append %d", length);

		/* check for overflow - this is really fatal */
		/* XXX could add just what will fit & try to parse, then repeat... */
		if ((mixer_text_length + text_length + 1) > sizeof(mixer_text)) {
			r_status_flags &= ~PX4IO_P_STATUS_FLAGS_MIXER_OK;
			return;
		}

		/* append mixer text and nul-terminate */
		memcpy(&mixer_text[mixer_text_length], msg->text, text_length);
		mixer_text_length += text_length;
		mixer_text[mixer_text_length] = '\0';
		isr_debug(2, "buflen %u", mixer_text_length);

		/* process the text buffer, adding new mixers as their descriptions can be parsed */
		unsigned resid = mixer_text_length;
		mixer_group.load_from_buf(&mixer_text[0], resid);

		/* if anything was parsed */
		if (resid != mixer_text_length) {

			/* ideally, this should test resid == 0 ? */
			r_status_flags |= PX4IO_P_STATUS_FLAGS_MIXER_OK;

			isr_debug(2, "used %u", mixer_text_length - resid);

			/* copy any leftover text to the base of the buffer for re-use */
			if (resid > 0)
				memcpy(&mixer_text[0], &mixer_text[mixer_text_length - resid], resid);

			mixer_text_length = resid;
		}

		break;
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "arch/alpha/system.hh"
#include "base/remote_gdb.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"
#include "mem/functional/memory_control.hh"
#include "mem/functional/physical.hh"
#include "sim/byteswap.hh"
#include "sim/builder.hh"
#include "targetarch/vtophys.hh"

using namespace LittleEndianGuest;

AlphaSystem::AlphaSystem(Params *p)
    : System(p)
{
    consoleSymtab = new SymbolTable;
    palSymtab = new SymbolTable;

    /**
     * Load the pal, and console code into memory
     */
    // Load Console Code
    console = createObjectFile(params()->console_path);
    if (console == NULL)
        fatal("Could not load console file %s", params()->console_path);

    // Load pal file
    pal = createObjectFile(params()->palcode);
    if (pal == NULL)
        fatal("Could not load PALcode file %s", params()->palcode);

    // Load program sections into memory
    pal->loadSections(physmem, true);
    console->loadSections(physmem, true);

    // load symbols
    if (!console->loadGlobalSymbols(consoleSymtab))
        panic("could not load console symbols\n");

    if (!pal->loadGlobalSymbols(palSymtab))
        panic("could not load pal symbols\n");

    if (!pal->loadLocalSymbols(palSymtab))
        panic("could not load pal symbols\n");

    if (!console->loadGlobalSymbols(debugSymbolTable))
        panic("could not load console symbols\n");

    if (!pal->loadGlobalSymbols(debugSymbolTable))
        panic("could not load pal symbols\n");

    if (!pal->loadLocalSymbols(debugSymbolTable))
        panic("could not load pal symbols\n");

     Addr addr = 0;
#ifndef NDEBUG
    consolePanicEvent = addConsoleFuncEvent<BreakPCEvent>("panic");
#endif

    /**
     * Copy the osflags (kernel arguments) into the consoles
     * memory. (Presently Linux does not use the console service
     * routine to get these command line arguments, but Tru64 and
     * others do.)
     */
    if (consoleSymtab->findAddress("env_booted_osflags", addr)) {
        Addr paddr = vtophys(physmem, addr);
        char *osflags = (char *)physmem->dma_addr(paddr, sizeof(uint32_t));

        if (osflags)
              strcpy(osflags, params()->boot_osflags.c_str());
    }

    /**
     * Set the hardware reset parameter block system type and revision
     * information to Tsunami.
     */
    if (consoleSymtab->findAddress("m5_rpb", addr)) {
        Addr paddr = vtophys(physmem, addr);
        char *hwrpb = (char *)physmem->dma_addr(paddr, sizeof(uint64_t));

        if (!hwrpb)
            panic("could not translate hwrpb addr\n");

        *(uint64_t*)(hwrpb+0x50) = htog(params()->system_type);
        *(uint64_t*)(hwrpb+0x58) = htog(params()->system_rev);
    } else
        panic("could not find hwrpb\n");

}

AlphaSystem::~AlphaSystem()
{
    delete consoleSymtab;
    delete console;
    delete pal;
#ifdef DEBUG
    delete consolePanicEvent;
#endif
}

/**
 * This function fixes up addresses that are used to match PCs for
 * hooking simulator events on to target function executions.
 *
 * Alpha binaries may have multiple global offset table (GOT)
 * sections.  A function that uses the GOT starts with a
 * two-instruction prolog which sets the global pointer (gp == r29) to
 * the appropriate GOT section.  The proper gp value is calculated
 * based on the function address, which must be passed by the caller
 * in the procedure value register (pv aka t12 == r27).  This sequence
 * looks like the following:
 *
 *			opcode Ra Rb offset
 *	ldah gp,X(pv)     09   29 27   X
 *	lda  gp,Y(gp)     08   29 29   Y
 *
 * for some constant offsets X and Y.  The catch is that the linker
 * (or maybe even the compiler, I'm not sure) may recognize that the
 * caller and callee are using the same GOT section, making this
 * prolog redundant, and modify the call target to skip these
 * instructions.  If we check for execution of the first instruction
 * of a function (the one the symbol points to) to detect when to skip
 * it, we'll miss all these modified calls.  It might work to
 * unconditionally check for the third instruction, but not all
 * functions have this prolog, and there's some chance that those
 * first two instructions could have undesired consequences.  So we do
 * the Right Thing and pattern-match the first two instructions of the
 * function to decide where to patch.
 *
 * Eventually this code should be moved into an ISA-specific file.
 */
Addr
AlphaSystem::fixFuncEventAddr(Addr addr)
{
    // mask for just the opcode, Ra, and Rb fields (not the offset)
    const uint32_t inst_mask = 0xffff0000;
    // ldah gp,X(pv): opcode 9, Ra = 29, Rb = 27
    const uint32_t gp_ldah_pattern = (9 << 26) | (29 << 21) | (27 << 16);
    // lda  gp,Y(gp): opcode 8, Ra = 29, rb = 29
    const uint32_t gp_lda_pattern  = (8 << 26) | (29 << 21) | (29 << 16);
    // instruction size
    const int sz = sizeof(uint32_t);

    Addr paddr = vtophys(physmem, addr);
    uint32_t i1 = *(uint32_t *)physmem->dma_addr(paddr, sz);
    uint32_t i2 = *(uint32_t *)physmem->dma_addr(paddr+sz, sz);

    if ((i1 & inst_mask) == gp_ldah_pattern &&
        (i2 & inst_mask) == gp_lda_pattern) {
        Addr new_addr = addr + 2*sz;
        DPRINTF(Loader, "fixFuncEventAddr: %p -> %p", addr, new_addr);
        return new_addr;
    } else {
        return addr;
    }
}

void
AlphaSystem::setAlphaAccess(Addr access)
{
    Addr addr = 0;
    if (consoleSymtab->findAddress("m5AlphaAccess", addr)) {
        Addr paddr = vtophys(physmem, addr);
        uint64_t *m5AlphaAccess =
            (uint64_t *)physmem->dma_addr(paddr, sizeof(uint64_t));

        if (!m5AlphaAccess)
            panic("could not translate m5AlphaAccess addr\n");

        *m5AlphaAccess = htog(EV5::Phys2K0Seg(access));
    } else
        panic("could not find m5AlphaAccess\n");
}

bool
AlphaSystem::breakpoint()
{
    return remoteGDB[0]->trap(ALPHA_KENTRY_INT);
}

void
AlphaSystem::serialize(std::ostream &os)
{
    System::serialize(os);
    consoleSymtab->serialize("console_symtab", os);
    palSymtab->serialize("pal_symtab", os);
}

void
AlphaSystem::unserialize(Checkpoint *cp, const std::string &section)
{
    System::unserialize(cp,section);
    consoleSymtab->unserialize("console_symtab", cp, section);
    palSymtab->unserialize("pal_symtab", cp, section);
}

BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem)

    Param<Tick> boot_cpu_frequency;
    SimObjectParam<MemoryController *> memctrl;
    SimObjectParam<PhysicalMemory *> physmem;

    Param<std::string> kernel;
    Param<std::string> console;
    Param<std::string> pal;

    Param<std::string> boot_osflags;
    Param<std::string> readfile;
    Param<unsigned int> init_param;

    Param<uint64_t> system_type;
    Param<uint64_t> system_rev;

    Param<bool> bin;
    VectorParam<std::string> binned_fns;
    Param<bool> bin_int;

END_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem)

BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem)

    INIT_PARAM(boot_cpu_frequency, "Frequency of the boot CPU"),
    INIT_PARAM(memctrl, "memory controller"),
    INIT_PARAM(physmem, "phsyical memory"),
    INIT_PARAM(kernel, "file that contains the kernel code"),
    INIT_PARAM(console, "file that contains the console code"),
    INIT_PARAM(pal, "file that contains palcode"),
    INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot",
                    "a"),
    INIT_PARAM_DFLT(readfile, "file to read startup script from", ""),
    INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0),
    INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34),
    INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10),
    INIT_PARAM_DFLT(bin, "is this system to be binned", false),
    INIT_PARAM(binned_fns, "functions to be broken down and binned"),
    INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true)

END_INIT_SIM_OBJECT_PARAMS(AlphaSystem)

CREATE_SIM_OBJECT(AlphaSystem)
{
    AlphaSystem::Params *p = new AlphaSystem::Params;
    p->name = getInstanceName();
    p->boot_cpu_frequency = boot_cpu_frequency;
    p->memctrl = memctrl;
    p->physmem = physmem;
    p->kernel_path = kernel;
    p->console_path = console;
    p->palcode = pal;
    p->boot_osflags = boot_osflags;
    p->init_param = init_param;
    p->readfile = readfile;
    p->system_type = system_type;
    p->system_rev = system_rev;
    p->bin = bin;
    p->binned_fns = binned_fns;
    p->bin_int = bin_int;
    return new AlphaSystem(p);
}

REGISTER_SIM_OBJECT("AlphaSystem", AlphaSystem)

<commit_msg>Changed targetarch to arch<commit_after>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "arch/alpha/system.hh"
#include "base/remote_gdb.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"
#include "mem/functional/memory_control.hh"
#include "mem/functional/physical.hh"
#include "sim/byteswap.hh"
#include "sim/builder.hh"
#include "arch/vtophys.hh"

using namespace LittleEndianGuest;

AlphaSystem::AlphaSystem(Params *p)
    : System(p)
{
    consoleSymtab = new SymbolTable;
    palSymtab = new SymbolTable;

    /**
     * Load the pal, and console code into memory
     */
    // Load Console Code
    console = createObjectFile(params()->console_path);
    if (console == NULL)
        fatal("Could not load console file %s", params()->console_path);

    // Load pal file
    pal = createObjectFile(params()->palcode);
    if (pal == NULL)
        fatal("Could not load PALcode file %s", params()->palcode);

    // Load program sections into memory
    pal->loadSections(physmem, true);
    console->loadSections(physmem, true);

    // load symbols
    if (!console->loadGlobalSymbols(consoleSymtab))
        panic("could not load console symbols\n");

    if (!pal->loadGlobalSymbols(palSymtab))
        panic("could not load pal symbols\n");

    if (!pal->loadLocalSymbols(palSymtab))
        panic("could not load pal symbols\n");

    if (!console->loadGlobalSymbols(debugSymbolTable))
        panic("could not load console symbols\n");

    if (!pal->loadGlobalSymbols(debugSymbolTable))
        panic("could not load pal symbols\n");

    if (!pal->loadLocalSymbols(debugSymbolTable))
        panic("could not load pal symbols\n");

     Addr addr = 0;
#ifndef NDEBUG
    consolePanicEvent = addConsoleFuncEvent<BreakPCEvent>("panic");
#endif

    /**
     * Copy the osflags (kernel arguments) into the consoles
     * memory. (Presently Linux does not use the console service
     * routine to get these command line arguments, but Tru64 and
     * others do.)
     */
    if (consoleSymtab->findAddress("env_booted_osflags", addr)) {
        Addr paddr = vtophys(physmem, addr);
        char *osflags = (char *)physmem->dma_addr(paddr, sizeof(uint32_t));

        if (osflags)
              strcpy(osflags, params()->boot_osflags.c_str());
    }

    /**
     * Set the hardware reset parameter block system type and revision
     * information to Tsunami.
     */
    if (consoleSymtab->findAddress("m5_rpb", addr)) {
        Addr paddr = vtophys(physmem, addr);
        char *hwrpb = (char *)physmem->dma_addr(paddr, sizeof(uint64_t));

        if (!hwrpb)
            panic("could not translate hwrpb addr\n");

        *(uint64_t*)(hwrpb+0x50) = htog(params()->system_type);
        *(uint64_t*)(hwrpb+0x58) = htog(params()->system_rev);
    } else
        panic("could not find hwrpb\n");

}

AlphaSystem::~AlphaSystem()
{
    delete consoleSymtab;
    delete console;
    delete pal;
#ifdef DEBUG
    delete consolePanicEvent;
#endif
}

/**
 * This function fixes up addresses that are used to match PCs for
 * hooking simulator events on to target function executions.
 *
 * Alpha binaries may have multiple global offset table (GOT)
 * sections.  A function that uses the GOT starts with a
 * two-instruction prolog which sets the global pointer (gp == r29) to
 * the appropriate GOT section.  The proper gp value is calculated
 * based on the function address, which must be passed by the caller
 * in the procedure value register (pv aka t12 == r27).  This sequence
 * looks like the following:
 *
 *			opcode Ra Rb offset
 *	ldah gp,X(pv)     09   29 27   X
 *	lda  gp,Y(gp)     08   29 29   Y
 *
 * for some constant offsets X and Y.  The catch is that the linker
 * (or maybe even the compiler, I'm not sure) may recognize that the
 * caller and callee are using the same GOT section, making this
 * prolog redundant, and modify the call target to skip these
 * instructions.  If we check for execution of the first instruction
 * of a function (the one the symbol points to) to detect when to skip
 * it, we'll miss all these modified calls.  It might work to
 * unconditionally check for the third instruction, but not all
 * functions have this prolog, and there's some chance that those
 * first two instructions could have undesired consequences.  So we do
 * the Right Thing and pattern-match the first two instructions of the
 * function to decide where to patch.
 *
 * Eventually this code should be moved into an ISA-specific file.
 */
Addr
AlphaSystem::fixFuncEventAddr(Addr addr)
{
    // mask for just the opcode, Ra, and Rb fields (not the offset)
    const uint32_t inst_mask = 0xffff0000;
    // ldah gp,X(pv): opcode 9, Ra = 29, Rb = 27
    const uint32_t gp_ldah_pattern = (9 << 26) | (29 << 21) | (27 << 16);
    // lda  gp,Y(gp): opcode 8, Ra = 29, rb = 29
    const uint32_t gp_lda_pattern  = (8 << 26) | (29 << 21) | (29 << 16);
    // instruction size
    const int sz = sizeof(uint32_t);

    Addr paddr = vtophys(physmem, addr);
    uint32_t i1 = *(uint32_t *)physmem->dma_addr(paddr, sz);
    uint32_t i2 = *(uint32_t *)physmem->dma_addr(paddr+sz, sz);

    if ((i1 & inst_mask) == gp_ldah_pattern &&
        (i2 & inst_mask) == gp_lda_pattern) {
        Addr new_addr = addr + 2*sz;
        DPRINTF(Loader, "fixFuncEventAddr: %p -> %p", addr, new_addr);
        return new_addr;
    } else {
        return addr;
    }
}

void
AlphaSystem::setAlphaAccess(Addr access)
{
    Addr addr = 0;
    if (consoleSymtab->findAddress("m5AlphaAccess", addr)) {
        Addr paddr = vtophys(physmem, addr);
        uint64_t *m5AlphaAccess =
            (uint64_t *)physmem->dma_addr(paddr, sizeof(uint64_t));

        if (!m5AlphaAccess)
            panic("could not translate m5AlphaAccess addr\n");

        *m5AlphaAccess = htog(EV5::Phys2K0Seg(access));
    } else
        panic("could not find m5AlphaAccess\n");
}

bool
AlphaSystem::breakpoint()
{
    return remoteGDB[0]->trap(ALPHA_KENTRY_INT);
}

void
AlphaSystem::serialize(std::ostream &os)
{
    System::serialize(os);
    consoleSymtab->serialize("console_symtab", os);
    palSymtab->serialize("pal_symtab", os);
}

void
AlphaSystem::unserialize(Checkpoint *cp, const std::string &section)
{
    System::unserialize(cp,section);
    consoleSymtab->unserialize("console_symtab", cp, section);
    palSymtab->unserialize("pal_symtab", cp, section);
}

BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem)

    Param<Tick> boot_cpu_frequency;
    SimObjectParam<MemoryController *> memctrl;
    SimObjectParam<PhysicalMemory *> physmem;

    Param<std::string> kernel;
    Param<std::string> console;
    Param<std::string> pal;

    Param<std::string> boot_osflags;
    Param<std::string> readfile;
    Param<unsigned int> init_param;

    Param<uint64_t> system_type;
    Param<uint64_t> system_rev;

    Param<bool> bin;
    VectorParam<std::string> binned_fns;
    Param<bool> bin_int;

END_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem)

BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem)

    INIT_PARAM(boot_cpu_frequency, "Frequency of the boot CPU"),
    INIT_PARAM(memctrl, "memory controller"),
    INIT_PARAM(physmem, "phsyical memory"),
    INIT_PARAM(kernel, "file that contains the kernel code"),
    INIT_PARAM(console, "file that contains the console code"),
    INIT_PARAM(pal, "file that contains palcode"),
    INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot",
                    "a"),
    INIT_PARAM_DFLT(readfile, "file to read startup script from", ""),
    INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0),
    INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34),
    INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10),
    INIT_PARAM_DFLT(bin, "is this system to be binned", false),
    INIT_PARAM(binned_fns, "functions to be broken down and binned"),
    INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true)

END_INIT_SIM_OBJECT_PARAMS(AlphaSystem)

CREATE_SIM_OBJECT(AlphaSystem)
{
    AlphaSystem::Params *p = new AlphaSystem::Params;
    p->name = getInstanceName();
    p->boot_cpu_frequency = boot_cpu_frequency;
    p->memctrl = memctrl;
    p->physmem = physmem;
    p->kernel_path = kernel;
    p->console_path = console;
    p->palcode = pal;
    p->boot_osflags = boot_osflags;
    p->init_param = init_param;
    p->readfile = readfile;
    p->system_type = system_type;
    p->system_rev = system_rev;
    p->bin = bin;
    p->binned_fns = binned_fns;
    p->bin_int = bin_int;
    return new AlphaSystem(p);
}

REGISTER_SIM_OBJECT("AlphaSystem", AlphaSystem)

<|endoftext|>


<commit_before>#ifndef MANIFOLDS_FUNCTIONS_FUNCTION_MATRIX_HH
#define MANIFOLDS_FUNCTIONS_FUNCTION_MATRIX_HH

#include "function.hh"
#include "matrix.hh"

namespace manifolds {

  template <class ... Functions>
  struct RowHolder
  {
    RowHolder(Functions...fs):functions(fs...){}
    std::tuple<Functions...> functions;
    auto GetFunctions() const
    {
      return functions;
    }
  };

  template <class ... Functions>
  auto Row(Functions...fs)
  {
    return RowHolder<Functions...>(fs...);
  }

  template <class rows, class cols,
	    class ... Functions>
  struct FunctionMatrixImpl :
    Function<max<Functions::input_dim...>::value,
	     max<Functions::output_dim...>::value>
  {
    static const int num_rows = rows::value;
    static const int num_cols = cols::value;
    std::tuple<Functions...> functions;

    template <class ... Rows>
    FunctionMatrixImpl(Rows...rs):
      functions(std::tuple_cat(rs.GetFunctions()...)){}

    FunctionMatrixImpl(std::tuple<Functions...> f):
      functions(f){}

    template <class ... Args, std::size_t ... indices>
    auto eval(std::integer_sequence<std::size_t,indices...>,
	      Args...args) const
    {
      return GetMatrix<rows::value,cols::value>
	(std::get<indices>(functions)(args...)...);
    }

    template <class ... Args>
    auto operator()(Args...args) const
    {
      return eval(std::index_sequence_for<Functions...>(), args...);
    }

    auto GetFunctions() const
    {
      return functions;
    }

    auto GetOutputs() const
    {
      return functions;
    }
  };

  DEF_FF_TEMPLATE(FunctionMatrix)

  template <std::size_t rows, std::size_t cols,
	    class ... Functions>
  auto GetFunctionMatrix(std::tuple<Functions...> functions)
  {
    return FunctionMatrix<
      int_<rows>,int_<cols>,Functions...>(functions);
  }

  template <class ... Rows>
  auto GetFunctionMatrix(Rows...rows)
  {
    return GetFunctionMatrix<
      sizeof...(Rows),
      std::tuple_size<
	decltype(std::declval<
		 typename first<Rows...>::type>().
		 GetFunctions())>::value>
      (std::tuple_cat(rows.GetFunctions()...));
  }

  template <class rows, class cols, class ... Functions>
  std::ostream & operator<<
  (std::ostream & s,
   FunctionMatrix<rows, cols, Functions...> f)
  {
    s << "FunctionMatrix<" << rows::value << ", "
      << cols::value << ">";
    StreamVariadic("", f, s);
    return s;
  }

  template <class ... Functions>
  auto GetRow(Functions ... functions)
  {
    return Row<Functions...>(functions...);
  }

}

#include "variables.hh"
#include "trig.hh"
#include "polynomial.hh"

namespace manifolds {

  template <class r1, class c1, class r2, class c2,
	    class ... F1s, class ... F2s>
  struct Simplification<
    Composition<FunctionMatrix<r1,c1,F1s...>,
		FunctionMatrix<r2,c2,F2s...>>>
  {
    typedef FunctionMatrix<r1,c1,F1s...> FMOut;
    typedef FunctionMatrix<r2,c2,F2s...> FMIn;
    static_assert(r2::value == 1 || c2::value == 1,
		  "Inner function of composition must "
		  "be a row or column vector.");
    static const int dims =
      r2::value == 1 ? c2::value : r2::value;
    static_assert(dims == FMOut::input_dim,
		  "Dimensionality of inner vector "
		  "must match input dimensionality of "
		  "outer vector.");

    template <class TupleO, class TupleI,
	      std::size_t ... iouts,
	      std::size_t ... iins>
    static auto CombineHelper(TupleO t1, TupleI t2,
			      std::integer_sequence<
			      std::size_t, iouts...>,
			      std::integer_sequence<
			      std::size_t, iins...>)
    {
      return std::make_tuple(std::get<iouts>(t1)
			     (std::get<iins>(t2)...)...);
    }

    typedef Composition<FunctionMatrix<r1,c1,F1s...>,
			FunctionMatrix<r2,c2,F2s...>> in_type;

    static auto Combine(in_type c)
    {
#ifdef PRINT_SIMPLIFIES
      std::cout << "Simplifying composition of function matrices\n";
#endif
      auto t = c.GetFunctions();
      return GetFunctionMatrix<r1::value, c1::value>
	(CombineHelper(std::get<0>(t).GetFunctions(),
		       std::get<1>(t).GetFunctions(),
		       std::index_sequence_for<F1s...>(),
		       std::index_sequence_for<F2s...>()));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };

  template <int i, bool a, class ... FMFuncs>
  struct Simplification<
    Composition<Variable<i,a>,
		FunctionMatrix<FMFuncs...>>>
  {
    typedef typename nth<i+2,FMFuncs...>::type type;

    static type Combine(Composition<Variable<i,a>,
			FunctionMatrix<FMFuncs...>> c)
    {
#ifdef PRINT_SIMPLIFIES
      std::cout << "Simplifying composition of "
	"variable and function matrix\n";
#endif
      return std::get<i>(std::get<1>(c.GetFunctions()).
			 GetFunctions());
    }
  };

  template <class r, class c, class ... Fs>
  struct Simplification<
    FunctionMatrix<r,c, Fs...>, 1>
  {
    typedef FunctionMatrix<r,c, Fs...> in_type;
    static auto Combine(in_type m)
    {
#ifdef PRINT_SIMPLIFIES
      std::cout << "Simplifying individual elements "
	"of function matrix\n";
#endif
      auto t = m.GetFunctions();
      return GetFunctionMatrix<r::value, c::value>
	(SimplifyIndividuals(t,
			     std::make_index_sequence<
			     std::tuple_size<
			     decltype(t)>::value>()));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };

  template <class F, class P, class r, class c>
  struct Simplification<
    Multiplication<
    F, Composition<Pow,
		   FunctionMatrix<r,c,F,P>>>, 0,
    typename std::enable_if<is_stateless<F>::value>::type>
  {
    typedef Multiplication<
      F, Composition<Pow,
		     FunctionMatrix<r,c,F,P>>> in_type;
    static auto Combine(in_type f)
    {
      auto p = std::get<1>(std::get<1>
			   (std::get<1>(f.GetFunctions()).
			    GetFunctions()).GetFunctions());
      return Pow()(F(), Add(p, 1_c));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };
}

#endif
<commit_msg>Adding simplification to multiply function matrices<commit_after>#ifndef MANIFOLDS_FUNCTIONS_FUNCTION_MATRIX_HH
#define MANIFOLDS_FUNCTIONS_FUNCTION_MATRIX_HH

#include "function.hh"
#include "matrix.hh"

namespace manifolds {

  template <class ... Functions>
  struct RowHolder
  {
    RowHolder(Functions...fs):functions(fs...){}
    std::tuple<Functions...> functions;
    auto GetFunctions() const
    {
      return functions;
    }
  };

  template <class ... Functions>
  auto Row(Functions...fs)
  {
    return RowHolder<Functions...>(fs...);
  }

  template <class rows, class cols,
	    class ... Functions>
  struct FunctionMatrixImpl :
    Function<max<Functions::input_dim...>::value,
	     max<Functions::output_dim...>::value>
  {
    static const int num_rows = rows::value;
    static const int num_cols = cols::value;
    std::tuple<Functions...> functions;

    template <class ... Rows>
    FunctionMatrixImpl(Rows...rs):
      functions(std::tuple_cat(rs.GetFunctions()...)){}

    FunctionMatrixImpl(std::tuple<Functions...> f):
      functions(f){}

    template <class ... Args, std::size_t ... indices>
    auto eval(std::integer_sequence<std::size_t,indices...>,
	      Args...args) const
    {
      return GetMatrix<rows::value,cols::value>
	(std::get<indices>(functions)(args...)...);
    }

    template <class ... Args>
    auto operator()(Args...args) const
    {
      return eval(std::index_sequence_for<Functions...>(), args...);
    }

    auto GetFunctions() const
    {
      return functions;
    }

    auto GetOutputs() const
    {
      return functions;
    }
  };

  DEF_FF_TEMPLATE(FunctionMatrix)

  template <std::size_t rows, std::size_t cols,
	    class ... Functions>
  auto GetFunctionMatrix(std::tuple<Functions...> functions)
  {
    return FunctionMatrix<
      int_<rows>,int_<cols>,Functions...>(functions);
  }

  template <class ... Rows>
  auto GetFunctionMatrix(Rows...rows)
  {
    return GetFunctionMatrix<
      sizeof...(Rows),
      std::tuple_size<
	decltype(std::declval<
		 typename first<Rows...>::type>().
		 GetFunctions())>::value>
      (std::tuple_cat(rows.GetFunctions()...));
  }

  template <class rows, class cols, class ... Functions>
  std::ostream & operator<<
  (std::ostream & s,
   FunctionMatrix<rows, cols, Functions...> f)
  {
    s << "FunctionMatrix<" << rows::value << ", "
      << cols::value << ">";
    StreamVariadic("", f, s);
    return s;
  }

  template <class ... Functions>
  auto GetRow(Functions ... functions)
  {
    return Row<Functions...>(functions...);
  }

}

#include "variables.hh"
#include "trig.hh"
#include "polynomial.hh"

namespace manifolds {

  template <class r1, class c1, class r2, class c2,
	    class ... F1s, class ... F2s>
  struct Simplification<
    Composition<FunctionMatrix<r1,c1,F1s...>,
		FunctionMatrix<r2,c2,F2s...>>>
  {
    typedef FunctionMatrix<r1,c1,F1s...> FMOut;
    typedef FunctionMatrix<r2,c2,F2s...> FMIn;
    static_assert(r2::value == 1 || c2::value == 1,
		  "Inner function of composition must "
		  "be a row or column vector.");
    static const int dims =
      r2::value == 1 ? c2::value : r2::value;
    static_assert(dims == FMOut::input_dim,
		  "Dimensionality of inner vector "
		  "must match input dimensionality of "
		  "outer vector.");

    template <class TupleO, class TupleI,
	      std::size_t ... iouts,
	      std::size_t ... iins>
    static auto CombineHelper(TupleO t1, TupleI t2,
			      std::integer_sequence<
			      std::size_t, iouts...>,
			      std::integer_sequence<
			      std::size_t, iins...>)
    {
      return std::make_tuple(std::get<iouts>(t1)
			     (std::get<iins>(t2)...)...);
    }

    typedef Composition<FunctionMatrix<r1,c1,F1s...>,
			FunctionMatrix<r2,c2,F2s...>> in_type;

    static auto Combine(in_type c)
    {
#ifdef PRINT_SIMPLIFIES
      std::cout << "Simplifying composition of function matrices\n";
#endif
      auto t = c.GetFunctions();
      return GetFunctionMatrix<r1::value, c1::value>
	(CombineHelper(std::get<0>(t).GetFunctions(),
		       std::get<1>(t).GetFunctions(),
		       std::index_sequence_for<F1s...>(),
		       std::index_sequence_for<F2s...>()));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };

  template <int i, bool a, class ... FMFuncs>
  struct Simplification<
    Composition<Variable<i,a>,
		FunctionMatrix<FMFuncs...>>>
  {
    typedef typename nth<i+2,FMFuncs...>::type type;

    static type Combine(Composition<Variable<i,a>,
			FunctionMatrix<FMFuncs...>> c)
    {
#ifdef PRINT_SIMPLIFIES
      std::cout << "Simplifying composition of "
	"variable and function matrix\n";
#endif
      return std::get<i>(std::get<1>(c.GetFunctions()).
			 GetFunctions());
    }
  };

  template <class r, class c, class ... Fs>
  struct Simplification<
    FunctionMatrix<r,c, Fs...>, 1>
  {
    typedef FunctionMatrix<r,c, Fs...> in_type;
    static auto Combine(in_type m)
    {
#ifdef PRINT_SIMPLIFIES
      std::cout << "Simplifying individual elements "
	"of function matrix\n";
#endif
      auto t = m.GetFunctions();
      return GetFunctionMatrix<r::value, c::value>
	(SimplifyIndividuals(t,
			     std::make_index_sequence<
			     std::tuple_size<
			     decltype(t)>::value>()));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };

  template <class F, class P, class r, class c>
  struct Simplification<
    Multiplication<
    F, Composition<Pow,
		   FunctionMatrix<r,c,F,P>>>, 0,
    typename std::enable_if<is_stateless<F>::value>::type>
  {
    typedef Multiplication<
      F, Composition<Pow,
		     FunctionMatrix<r,c,F,P>>> in_type;
    static auto Combine(in_type f)
    {
      auto p = std::get<1>(std::get<1>
			   (std::get<1>(f.GetFunctions()).
			    GetFunctions()).GetFunctions());
      return Pow()(F(), Add(p, 1_c));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };

  template <class r1, class c1, class r2, class c2,
	    class ... F1s, class ... F2s>
  struct Simplification<
    Multiplication<FunctionMatrix<r1,c1,F1s...>,
		   FunctionMatrix<r2,c2,F2s...>>>
  {
    typedef Multiplication<
      FunctionMatrix<r1,c1,F1s...>,
      FunctionMatrix<r2,c2,F2s...>> in_type;
    template <std::size_t row>
      struct Inner1
    {
      template <std::size_t col>
      struct Inner2
      {
	template <std::size_t ... is>
	static auto apply(in_type m, std::integer_sequence<
			  std::size_t, is...>)
	{
	  auto l = std::get<0>(m.GetFunctions()).GetFunctions();
	  auto r = std::get<1>(m.GetFunctions()).GetFunctions();
	  return Add((std::get<row*c1::value+is>(l) *
		      std::get<is*c2::value+col>(r))...);
	}
      };
      template <std::size_t ... is>
      static auto apply(in_type m, std::integer_sequence<
			std::size_t, is...>)
      {
	std::make_index_sequence<r2::value> s;
	return std::make_tuple(Inner2<is>::apply(m, s)...);
      }
    };

    template <std::size_t ... is>
      static auto apply(in_type m, std::integer_sequence<
			std::size_t, is...>)
    {
      std::make_index_sequence<c2::value> s;
      return std::tuple_cat(Inner1<is>::apply(m, s)...);
    }

    static auto Combine(in_type m)
    {
      std::make_index_sequence<r1::value> s;
      return
	GetFunctionMatrix<r1::value,c2::value>(apply(m,s));
    }

    typedef decltype(Combine(std::declval<in_type>())) type;
  };

  template <class F>
  struct Simplification<
    FunctionMatrix<int_<1>,int_<1>,F>>
  {
    typedef F type;
    static type Combine(FunctionMatrix<int_<1>,int_<1>,F> f)
    {
      return std::get<0>(f.GetFunctions());
    }
  };
}

#endif
<|endoftext|>


<commit_before>#include "gmock/gmock.h"
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <set>
#include <vector>

#include "ThreadPool.h"
#include "TestTimer.h"

using namespace testing;

class ThreadPoolTest : public Test {
    public:
        ThreadPool pool;
        std::condition_variable wasExecuted;
        std::mutex m;
        std::vector<std::shared_ptr<std::thread>> threads;

        unsigned int count{0};

        void incrementCountAndNotify() {
            std::unique_lock<std::mutex> lock(m);
            ++count;
            wasExecuted.notify_all();
        }

        void waitForNotificationOrFailOnTimeout(unsigned expectedCount, int milliseconds=100) {
            std::unique_lock<std::mutex> lock(m);
            ASSERT_THAT(wasExecuted.wait_for(lock, std::chrono::milliseconds(milliseconds), [&] { return count == expectedCount; }), Eq(true));

        }

        void TearDown() override {
            for (auto& t: threads) t->join();
        }
};

TEST_F(ThreadPoolTest,PoolHasWorkAfterAdd) {
    pool.add([]{});
    ASSERT_THAT(pool.hasWork(), Eq(1));
}

TEST_F(ThreadPoolTest,PoolHasNoWorkAfterCreation) {
    ASSERT_THAT(pool.hasWork(), Eq(0));
}

TEST_F(ThreadPoolTest,HasNoWorkAfterLastWorkIsPulled) {
    pool.add([]{});
    pool.add([]{});
    auto work1 = pool.pull();
    auto work2 = pool.pull();
    ASSERT_THAT(pool.hasWork(), Eq(0));
}

TEST_F(ThreadPoolTest,HasWorkAfterOneWorkIsPulled) {
    pool.add([]{});
    pool.add([]{});
    auto work = pool.pull();
    ASSERT_THAT(pool.hasWork(), Eq(1));
}

TEST_F(ThreadPoolTest, PullsWorkInAThread) {
    pool.start(4);
    bool wasWorked{0};
    std::function<void()> work = [&]() {
         std::unique_lock<std::mutex> lock(m);
         wasWorked = true;
         wasExecuted.notify_all();
    };

    pool.add(work);
    std::unique_lock<std::mutex> lock(m);
    ASSERT_THAT(wasExecuted.wait_for(lock, std::chrono::milliseconds(100), [&] { return wasWorked; }), Eq(true));
}

TEST_F(ThreadPoolTest, ExecutesMultipleWork) {
    pool.start(4);
    unsigned int NumberOfWorkItems{3};
    std::function<void()> work = [&]() { incrementCountAndNotify(); };

    for(unsigned int i{0}; i < NumberOfWorkItems ; i++) {
        pool.add(work);
    }

    waitForNotificationOrFailOnTimeout(NumberOfWorkItems);
}

TEST_F(ThreadPoolTest, DispatchMultipleClientThreads) {
    pool.start(4);
    unsigned int NumberOfWorkItems{10};
    unsigned int NumberOfThreads{10};

    std::function<void()> work = [&]() { incrementCountAndNotify(); };

    for(unsigned int i{0}; i < NumberOfThreads; i++) {
        threads.push_back(std::make_shared<std::thread>([&] {
            for(unsigned int j{0}; j < NumberOfWorkItems; j++)
                pool.add(work);
        }));
    }

    waitForNotificationOrFailOnTimeout(NumberOfThreads * NumberOfWorkItems);

}

TEST_F(ThreadPoolTest, MakesSureAllThreadsWorkToRetrieveFromQueue) {
    unsigned int NumberOfThreads=4;
    pool.start(NumberOfThreads);
    std::set<std::thread::id> threadIds;

    std::function<void()> work = [&]() {
       threadIds.insert(std::this_thread::get_id());
       incrementCountAndNotify();
    };

    unsigned int NumberOfWorkItems{500};
    for(unsigned int j{0}; j < NumberOfWorkItems; j++) {
        pool.add(work);
    }
    waitForNotificationOrFailOnTimeout(NumberOfWorkItems);
    ASSERT_THAT(threadIds.size(),Eq(NumberOfThreads));

}

TEST_F(ThreadPoolTest, FutureReturningInt) {
    pool.start(4);

    auto work = []() -> int { return 1000; } ;
    auto result = pool.submit(work);
    ASSERT_THAT(result.get(), Eq(1000));

}

TEST_F(ThreadPoolTest, FutureReturningString) {
    pool.start(4);

    auto work = []() -> std::string { return "1000"; } ;
    auto result = pool.submit(work);
    ASSERT_THAT(result.get(), StrEq("1000"));
}

TEST_F(ThreadPoolTest,FactorialTest) {
    pool.start(4);
    auto work = [](int n) {
      unsigned long long factorial = 1;
      for(int i = 1; i <=n; ++i) {
        factorial *= i;
      }

      return factorial;

    };

    auto result = pool.submit(work,12);

    ASSERT_THAT(result.get(), Eq(479001600));
}

TEST_F(ThreadPoolTest,TimingTestWithFuture) {
    pool.start(4);
    std::vector<std::future<unsigned long long>> results;
    auto work = [](int n) {
      unsigned long long factorial = 1;
      for(int i = 1; i <=n; ++i) {
        factorial *= i;
      }

      return factorial;

    };

    TestTimer timer("4-sized-TP with Future",0);
    for (int i = 5; i < 60 ; i++) {
        results.push_back(pool.submit(work,i));
    }

    for(unsigned int i = 0; i< results.size(); i++) {
        results.at(i).get();
    }
}

TEST_F(ThreadPoolTest,TimingTestWithCallback) {
    pool.start(4);
    // core dumps
    std::vector<unsigned long long> results;
    TestTimer timer("4-sized-TP-Callback",0);
    for (int n = 5; n < 60 ; n++) {
        auto work = [&]() {
            unsigned long long factorial = 1;
            for(int i = 1; i <=n; ++i) {
              factorial *= i;
            }
            {
                std::lock_guard<std::mutex> guard(m);
                results.push_back(factorial);
            }
            incrementCountAndNotify();
        };

        pool.add(work);
    }

    waitForNotificationOrFailOnTimeout(55);
}

TEST_F(ThreadPoolTest,TimingTestWithoutTP) {

    std::vector<unsigned long long> results;
    auto work = [](int n) {
      unsigned long long factorial = 1;
      for(int i = 1; i <=n; ++i) {
        factorial *= i;
      }

      return factorial;

    };

    TestTimer timer("In Sequence",0);
    for (int i = 5; i < 60 ; i++) {
        results.push_back(work(i));
    }

     for(unsigned int i = 0; i< results.size(); i++) {
        results.at(i);
    }

}

<commit_msg>Remove core dump message, as it was fixed<commit_after>#include "gmock/gmock.h"
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <set>
#include <vector>

#include "ThreadPool.h"
#include "TestTimer.h"

using namespace testing;

class ThreadPoolTest : public Test {
    public:
        ThreadPool pool;
        std::condition_variable wasExecuted;
        std::mutex m;
        std::vector<std::shared_ptr<std::thread>> threads;

        unsigned int count{0};

        void incrementCountAndNotify() {
            std::unique_lock<std::mutex> lock(m);
            ++count;
            wasExecuted.notify_all();
        }

        void waitForNotificationOrFailOnTimeout(unsigned expectedCount, int milliseconds=100) {
            std::unique_lock<std::mutex> lock(m);
            ASSERT_THAT(wasExecuted.wait_for(lock, std::chrono::milliseconds(milliseconds), [&] { return count == expectedCount; }), Eq(true));

        }

        void TearDown() override {
            for (auto& t: threads) t->join();
        }
};

TEST_F(ThreadPoolTest,PoolHasWorkAfterAdd) {
    pool.add([]{});
    ASSERT_THAT(pool.hasWork(), Eq(1));
}

TEST_F(ThreadPoolTest,PoolHasNoWorkAfterCreation) {
    ASSERT_THAT(pool.hasWork(), Eq(0));
}

TEST_F(ThreadPoolTest,HasNoWorkAfterLastWorkIsPulled) {
    pool.add([]{});
    pool.add([]{});
    auto work1 = pool.pull();
    auto work2 = pool.pull();
    ASSERT_THAT(pool.hasWork(), Eq(0));
}

TEST_F(ThreadPoolTest,HasWorkAfterOneWorkIsPulled) {
    pool.add([]{});
    pool.add([]{});
    auto work = pool.pull();
    ASSERT_THAT(pool.hasWork(), Eq(1));
}

TEST_F(ThreadPoolTest, PullsWorkInAThread) {
    pool.start(4);
    bool wasWorked{0};
    std::function<void()> work = [&]() {
         std::unique_lock<std::mutex> lock(m);
         wasWorked = true;
         wasExecuted.notify_all();
    };

    pool.add(work);
    std::unique_lock<std::mutex> lock(m);
    ASSERT_THAT(wasExecuted.wait_for(lock, std::chrono::milliseconds(100), [&] { return wasWorked; }), Eq(true));
}

TEST_F(ThreadPoolTest, ExecutesMultipleWork) {
    pool.start(4);
    unsigned int NumberOfWorkItems{3};
    std::function<void()> work = [&]() { incrementCountAndNotify(); };

    for(unsigned int i{0}; i < NumberOfWorkItems ; i++) {
        pool.add(work);
    }

    waitForNotificationOrFailOnTimeout(NumberOfWorkItems);
}

TEST_F(ThreadPoolTest, DispatchMultipleClientThreads) {
    pool.start(4);
    unsigned int NumberOfWorkItems{10};
    unsigned int NumberOfThreads{10};

    std::function<void()> work = [&]() { incrementCountAndNotify(); };

    for(unsigned int i{0}; i < NumberOfThreads; i++) {
        threads.push_back(std::make_shared<std::thread>([&] {
            for(unsigned int j{0}; j < NumberOfWorkItems; j++)
                pool.add(work);
        }));
    }

    waitForNotificationOrFailOnTimeout(NumberOfThreads * NumberOfWorkItems);

}

TEST_F(ThreadPoolTest, MakesSureAllThreadsWorkToRetrieveFromQueue) {
    unsigned int NumberOfThreads=4;
    pool.start(NumberOfThreads);
    std::set<std::thread::id> threadIds;

    std::function<void()> work = [&]() {
       threadIds.insert(std::this_thread::get_id());
       incrementCountAndNotify();
    };

    unsigned int NumberOfWorkItems{500};
    for(unsigned int j{0}; j < NumberOfWorkItems; j++) {
        pool.add(work);
    }
    waitForNotificationOrFailOnTimeout(NumberOfWorkItems);
    ASSERT_THAT(threadIds.size(),Eq(NumberOfThreads));

}

TEST_F(ThreadPoolTest, FutureReturningInt) {
    pool.start(4);

    auto work = []() -> int { return 1000; } ;
    auto result = pool.submit(work);
    ASSERT_THAT(result.get(), Eq(1000));

}

TEST_F(ThreadPoolTest, FutureReturningString) {
    pool.start(4);

    auto work = []() -> std::string { return "1000"; } ;
    auto result = pool.submit(work);
    ASSERT_THAT(result.get(), StrEq("1000"));
}

TEST_F(ThreadPoolTest,FactorialTest) {
    pool.start(4);
    auto work = [](int n) {
      unsigned long long factorial = 1;
      for(int i = 1; i <=n; ++i) {
        factorial *= i;
      }

      return factorial;

    };

    auto result = pool.submit(work,12);

    ASSERT_THAT(result.get(), Eq(479001600));
}

TEST_F(ThreadPoolTest,TimingTestWithFuture) {
    pool.start(4);
    std::vector<std::future<unsigned long long>> results;
    auto work = [](int n) {
      unsigned long long factorial = 1;
      for(int i = 1; i <=n; ++i) {
        factorial *= i;
      }

      return factorial;

    };

    TestTimer timer("4-sized-TP with Future",0);
    for (int i = 5; i < 60 ; i++) {
        results.push_back(pool.submit(work,i));
    }

    for(unsigned int i = 0; i< results.size(); i++) {
        results.at(i).get();
    }
}

TEST_F(ThreadPoolTest,TimingTestWithCallback) {
    pool.start(4);
    std::vector<unsigned long long> results;
    TestTimer timer("4-sized-TP-Callback",0);
    for (int n = 5; n < 60 ; n++) {
        auto work = [&]() {
            unsigned long long factorial = 1;
            for(int i = 1; i <=n; ++i) {
              factorial *= i;
            }
            {
                std::lock_guard<std::mutex> guard(m);
                results.push_back(factorial);
            }
            incrementCountAndNotify();
        };

        pool.add(work);
    }

    waitForNotificationOrFailOnTimeout(55);
}

TEST_F(ThreadPoolTest,TimingTestWithoutTP) {

    std::vector<unsigned long long> results;
    auto work = [](int n) {
      unsigned long long factorial = 1;
      for(int i = 1; i <=n; ++i) {
        factorial *= i;
      }

      return factorial;

    };

    TestTimer timer("In Sequence",0);
    for (int i = 5; i < 60 ; i++) {
        results.push_back(work(i));
    }

     for(unsigned int i = 0; i< results.size(); i++) {
        results.at(i);
    }

}

<|endoftext|>


<commit_before><commit_msg>Remove unused file<commit_after><|endoftext|>