<commit_before>/*
 * Copyright (c) 2011, The University of Oxford
 * 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 of the University of Oxford 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 HOLDER 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 <cuda_runtime_api.h>

#include "interferometry/oskar_interferometer.h"
#include "interferometry/oskar_correlate.h"
#include "interferometry/oskar_evaluate_jones_K.h"
#include "interferometry/oskar_evaluate_station_uvw.h"
#include "math/oskar_Jones.h"
#include "math/oskar_jones_join.h"
#include "sky/oskar_evaluate_jones_R.h"
#include "sky/oskar_mjd_to_gast_fast.h"
#include "sky/oskar_sky_model_horizon_clip.h"
#include "station/oskar_evaluate_jones_E.h"
#include <cstdio>

extern "C"
int oskar_interferometer(oskar_Mem* vis_amp, const oskar_SkyModel* sky,
        const oskar_TelescopeModel* telescope, const oskar_SimTime* times,
        double frequency)
{
    int err = 0;
    int device_id = 0;
    size_t mem_free, mem_total;
    cudaDeviceProp device_prop;

    // Copy telescope model and sky model for frequency scaling.
    oskar_TelescopeModel tel_gpu(telescope, OSKAR_LOCATION_GPU);
    oskar_SkyModel sky_gpu(sky, OSKAR_LOCATION_GPU);

    // Scale GPU telescope coordinates by wavenumber.
    err = tel_gpu.multiply_by_wavenumber(frequency); if (err) return err;

    // Scale by spectral index.
    err = sky_gpu.scale_by_spectral_index(frequency);
    if (err) return err;

    // Initialise blocks of Jones matrices and visibilities.
    int type = sky_gpu.type();
    int n_stations = tel_gpu.num_stations;
    int n_baselines = n_stations * (n_stations - 1) / 2;
    int n_sources = sky_gpu.num_sources;
    int complex_scalar = type | OSKAR_COMPLEX;
    int complex_matrix = type | OSKAR_COMPLEX | OSKAR_MATRIX;
    oskar_Jones J(complex_matrix, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Jones R(complex_matrix, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Jones E(complex_scalar, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Jones K(complex_scalar, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Mem vis(complex_matrix, OSKAR_LOCATION_GPU, n_baselines);
    oskar_Mem u(type, OSKAR_LOCATION_GPU, n_stations, true);
    oskar_Mem v(type, OSKAR_LOCATION_GPU, n_stations, true);
    oskar_Mem w(type, OSKAR_LOCATION_GPU, n_stations, true);
    oskar_Work work(type, OSKAR_LOCATION_GPU);

    // Get time increments.
    int num_vis_dumps        = times->num_vis_dumps;
    int num_vis_ave          = times->num_vis_ave;
    int num_fringe_ave       = times->num_fringe_ave;
    double obs_start_mjd_utc = times->obs_start_mjd_utc;
    double dt_dump           = times->dt_dump_days;
    double dt_ave            = times->dt_ave_days;
    double dt_fringe         = times->dt_fringe_days;

    cudaMemGetInfo(&mem_free, &mem_total);
    cudaGetDevice(&device_id);
    cudaGetDeviceProperties(&device_prop, device_id);
    printf("==> Device memory [%i, %s]: free %.1f MB, total %.1f MB.\n",
            device_id, device_prop.name, mem_free/(1024.*1024.),
            mem_total/(1024.*1024.));

    // Start simulation.
    for (int j = 0; j < num_vis_dumps; ++j)
    {
        // Start time for the visibility dump, in MJD(UTC).
        printf("--> Simulating snapshot (%i / %i) ", j+1, num_vis_dumps);
        double t_dump = obs_start_mjd_utc + j * dt_dump;
        double gast = oskar_mjd_to_gast_fast(t_dump + dt_dump / 2.0);

        // Initialise visibilities for the dump to zero.
        err = vis.clear_contents();
        if (err) return err;

        // Compact sky model to temporary.
        oskar_SkyModel local_sky(type, OSKAR_LOCATION_GPU);
        err = oskar_sky_model_horizon_clip(&local_sky, &sky_gpu, &tel_gpu,
                gast, &work);
        printf("[num sources = %i]\n", local_sky.num_sources);
        if (err == OSKAR_ERR_NO_VISIBLE_SOURCES)
        {
            // Skip iteration.
            printf("--> WARNING: No sources above horizon. Skipping.\n");
            continue;
        }
        else if (err != 0) return err;

//        oskar_SkyModel temp_sky(&local_sky, OSKAR_LOCATION_CPU);
//        printf("#### num_sources = %i (%i %i)\n", temp_sky.num_sources,
//                local_sky.num_sources, sky->num_sources);
//        typedef double real;
//        for (int source = 0; source < temp_sky.num_sources; ++source)
//        {
//            printf("##### source[%3i] %f %f | %f %f %f %f | %f %f\n", source,
//                    ((real*)temp_sky.RA.data)[source],
//                    ((real*)temp_sky.Dec.data)[source],
//                    ((real*)temp_sky.I.data)[source],
//                    ((real*)temp_sky.Q.data)[source],
//                    ((real*)temp_sky.U.data)[source],
//                    ((real*)temp_sky.V.data)[source],
//                    ((real*)temp_sky.reference_freq.data)[source],
//                    ((real*)temp_sky.spectral_index.data)[source]);
//        }

        // Set dimensions of Jones matrices (this is not a resize!).
        err = J.set_size(n_stations, local_sky.num_sources); if (err) return err;
        err = R.set_size(n_stations, local_sky.num_sources); if (err) return err;
        err = E.set_size(n_stations, local_sky.num_sources); if (err) return err;
        err = K.set_size(n_stations, local_sky.num_sources); if (err) return err;

        // Average snapshot.
        for (int i = 0; i < num_vis_ave; ++i)
        {
            // Evaluate Greenwich Apparent Sidereal Time.
            double t_ave = t_dump + i * dt_ave;
            double gast = oskar_mjd_to_gast_fast(t_ave + dt_ave / 2);

            // Evaluate parallactic angle rotation (Jones R).
            err = oskar_evaluate_jones_R(&R, &local_sky, &tel_gpu, gast);
            if (err) return err;

            // Evaluate station beam (Jones E).
            err = oskar_evaluate_jones_E(&E, &local_sky, &tel_gpu, gast, &work);
            if (err) return err;

            // Join Jones matrices (R = E * R).
            err = oskar_jones_join(&R, &E, &R);
            if (err) return err;

            for (int k = 0; k < num_fringe_ave; ++k)
            {
                // Evaluate Greenwich Apparent Sidereal Time.
                double t_fringe = t_ave + k * dt_fringe;
                double gast = oskar_mjd_to_gast_fast(t_fringe + dt_fringe / 2);

                // Evaluate station u,v,w coordinates.
                err = oskar_evaluate_station_uvw(&u, &v, &w, &tel_gpu, gast);
                if (err) return err;

                // Evaluate interferometer phase (Jones K).
                err = oskar_evaluate_jones_K(&K, &local_sky, &u, &v, &w);
                if (err) return err;

                // Join Jones matrices (J = K * R).
                err = oskar_jones_join(&J, &K, &R); if (err) return err;

                // Produce visibilities.
                err = oskar_correlate(&vis, &J, &tel_gpu, &local_sky, &u, &v);
                if (err) return err;
            }
        }

        // Divide visibilities by number of averages.
        err = vis.scale_real(1.0 / (num_fringe_ave * num_vis_ave));
        if (err) return err;

        // Add visibilities to global data.
        err = vis_amp->insert(&vis, j * n_baselines);
        if (err) return err;
    }

    return OSKAR_SUCCESS;
}
<commit_msg>updated debugging<commit_after>/*
 * Copyright (c) 2011, The University of Oxford
 * 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 of the University of Oxford 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 HOLDER 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 <cuda_runtime_api.h>

#include "interferometry/oskar_interferometer.h"
#include "interferometry/oskar_correlate.h"
#include "interferometry/oskar_evaluate_jones_K.h"
#include "interferometry/oskar_evaluate_station_uvw.h"
#include "math/oskar_Jones.h"
#include "math/oskar_jones_join.h"
#include "sky/oskar_evaluate_jones_R.h"
#include "sky/oskar_mjd_to_gast_fast.h"
#include "sky/oskar_sky_model_horizon_clip.h"
#include "station/oskar_evaluate_jones_E.h"
#include <cstdio>

extern "C"
int oskar_interferometer(oskar_Mem* vis_amp, const oskar_SkyModel* sky,
        const oskar_TelescopeModel* telescope, const oskar_SimTime* times,
        double frequency)
{
    int err = 0;
    int device_id = 0;
    size_t mem_free, mem_total;
    cudaDeviceProp device_prop;

    // Copy telescope model and sky model for frequency scaling.
    oskar_TelescopeModel tel_gpu(telescope, OSKAR_LOCATION_GPU);
    oskar_SkyModel sky_gpu(sky, OSKAR_LOCATION_GPU);

    // Scale GPU telescope coordinates by wavenumber.
    err = tel_gpu.multiply_by_wavenumber(frequency); if (err) return err;

    // Scale by spectral index.
    err = sky_gpu.scale_by_spectral_index(frequency);
    if (err) return err;

    // Initialise blocks of Jones matrices and visibilities.
    int type = sky_gpu.type();
    int n_stations = tel_gpu.num_stations;
    int n_baselines = n_stations * (n_stations - 1) / 2;
    int n_sources = sky_gpu.num_sources;
    int complex_scalar = type | OSKAR_COMPLEX;
    int complex_matrix = type | OSKAR_COMPLEX | OSKAR_MATRIX;
    oskar_Jones J(complex_matrix, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Jones R(complex_matrix, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Jones E(complex_scalar, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Jones K(complex_scalar, OSKAR_LOCATION_GPU, n_stations, n_sources);
    oskar_Mem vis(complex_matrix, OSKAR_LOCATION_GPU, n_baselines);
    oskar_Mem u(type, OSKAR_LOCATION_GPU, n_stations, true);
    oskar_Mem v(type, OSKAR_LOCATION_GPU, n_stations, true);
    oskar_Mem w(type, OSKAR_LOCATION_GPU, n_stations, true);
    oskar_Work work(type, OSKAR_LOCATION_GPU);

    // Get time increments.
    int num_vis_dumps        = times->num_vis_dumps;
    int num_vis_ave          = times->num_vis_ave;
    int num_fringe_ave       = times->num_fringe_ave;
    double obs_start_mjd_utc = times->obs_start_mjd_utc;
    double dt_dump           = times->dt_dump_days;
    double dt_ave            = times->dt_ave_days;
    double dt_fringe         = times->dt_fringe_days;

    cudaMemGetInfo(&mem_free, &mem_total);
    cudaGetDevice(&device_id);
    cudaGetDeviceProperties(&device_prop, device_id);
    printf("==> Device memory [%i, %s]: free %.1f MB, total %.1f MB.\n",
            device_id, device_prop.name, mem_free/(1024.*1024.),
            mem_total/(1024.*1024.));

    // Start simulation.
    for (int j = 0; j < num_vis_dumps; ++j)
    {
        // Start time for the visibility dump, in MJD(UTC).
        printf("--> Simulating snapshot (%i / %i) ", j+1, num_vis_dumps);
        double t_dump = obs_start_mjd_utc + j * dt_dump;
        double gast = oskar_mjd_to_gast_fast(t_dump + dt_dump / 2.0);

        // Initialise visibilities for the dump to zero.
        err = vis.clear_contents();
        if (err) return err;

        // Compact sky model to temporary.
        oskar_SkyModel local_sky(type, OSKAR_LOCATION_GPU);
        err = oskar_sky_model_horizon_clip(&local_sky, &sky_gpu, &tel_gpu,
                gast, &work);
        printf("[num sources = %i]\n", local_sky.num_sources);
        if (err == OSKAR_ERR_NO_VISIBLE_SOURCES)
        {
            // Skip iteration.
            printf("--> WARNING: No sources above horizon. Skipping.\n");
            continue;
        }
        else if (err != 0) return err;

        oskar_SkyModel temp_sky(&local_sky, OSKAR_LOCATION_CPU);
        printf("#### num_sources = %i (%i %i)\n", temp_sky.num_sources,
                local_sky.num_sources, sky->num_sources);
        typedef float real;
        for (int source = 0; source < temp_sky.num_sources; ++source)
        {
            printf("##### source[%3i] % .2f % .2f | %.2f %.2f %.2f %.2f | %.2f %.2f | % .2f % .2f % .2f\n",
                    source,
                    ((real*)temp_sky.RA.data)[source],
                    ((real*)temp_sky.Dec.data)[source],
                    ((real*)temp_sky.I.data)[source],
                    ((real*)temp_sky.Q.data)[source],
                    ((real*)temp_sky.U.data)[source],
                    ((real*)temp_sky.V.data)[source],
                    ((real*)temp_sky.reference_freq.data)[source],
                    ((real*)temp_sky.spectral_index.data)[source],
                    ((real*)temp_sky.rel_l.data)[source],
                    ((real*)temp_sky.rel_m.data)[source],
                    ((real*)temp_sky.rel_n.data)[source]);
        }

        // Set dimensions of Jones matrices (this is not a resize!).
        err = J.set_size(n_stations, local_sky.num_sources); if (err) return err;
        err = R.set_size(n_stations, local_sky.num_sources); if (err) return err;
        err = E.set_size(n_stations, local_sky.num_sources); if (err) return err;
        err = K.set_size(n_stations, local_sky.num_sources); if (err) return err;

        // Average snapshot.
        for (int i = 0; i < num_vis_ave; ++i)
        {
            // Evaluate Greenwich Apparent Sidereal Time.
            double t_ave = t_dump + i * dt_ave;
            double gast = oskar_mjd_to_gast_fast(t_ave + dt_ave / 2);

            // Evaluate parallactic angle rotation (Jones R).
            err = oskar_evaluate_jones_R(&R, &local_sky, &tel_gpu, gast);
            if (err) return err;

            // Evaluate station beam (Jones E).
            err = oskar_evaluate_jones_E(&E, &local_sky, &tel_gpu, gast, &work);
            if (err) return err;

            // Join Jones matrices (R = E * R).
            err = oskar_jones_join(&R, &E, &R);
            if (err) return err;

            for (int k = 0; k < num_fringe_ave; ++k)
            {
                // Evaluate Greenwich Apparent Sidereal Time.
                double t_fringe = t_ave + k * dt_fringe;
                double gast = oskar_mjd_to_gast_fast(t_fringe + dt_fringe / 2);

                // Evaluate station u,v,w coordinates.
                err = oskar_evaluate_station_uvw(&u, &v, &w, &tel_gpu, gast);
                if (err) return err;

                // Evaluate interferometer phase (Jones K).
                err = oskar_evaluate_jones_K(&K, &local_sky, &u, &v, &w);
                if (err) return err;

                // Join Jones matrices (J = K * R).
                err = oskar_jones_join(&J, &K, &R); if (err) return err;

                // Produce visibilities.
                err = oskar_correlate(&vis, &J, &tel_gpu, &local_sky, &u, &v);
                if (err) return err;
            }
        }

        // Divide visibilities by number of averages.
        err = vis.scale_real(1.0 / (num_fringe_ave * num_vis_ave));
        if (err) return err;

        // Add visibilities to global data.
        err = vis_amp->insert(&vis, j * n_baselines);
        if (err) return err;
    }

    return OSKAR_SUCCESS;
}
<|endoftext|>


<commit_before>#include "player.hpp"

Player::Player(int num) : Actor(num) {
    compas = Compas();
    error = false;
    time = 0;
}

Player::Player() : Actor() {
    compas = Compas();
    error = false;
    time = 0;
}

bool Player::updateLogic(float deltaTime, sf::RenderWindow *window) {
    time += deltaTime;
    if (time > BLACKVALUE) {
        compas.incraeseTime();
        time = 0;
    }
    return false;
}

bool Player::event(sf::Event e, bool def) {
    switch(e.type) {
    case (sf::Event::KeyPressed):
        if(e.key.code == sf::Keyboard::C) {
            compas.start();
            error = false;
        }
        if(e.key.code == sf::Keyboard::Space) {
            if (animate == PlayerState::idle && !error) {
                compas.add();
                if (compas.isPressed()) animate = PlayerState::attacking;
            }
            else {
                compas.end();
                error = true;
                if (animate != PlayerState::inMidle) animate = PlayerState::hurt;
            }
        }
        break;
    case (sf::Event::KeyReleased):
        if (e.key.code == sf::Keyboard::C) {
            compas.end();
            return false;
        }
        break;
    default:
        break;
    }
    return true;
}
<commit_msg>fix bugg, start shader<commit_after>#include "player.hpp"

Player::Player(int num) : Actor(num) {
    compas = Compas();
    error = false;
    time = 0;
}

Player::Player() : Actor() {
    compas = Compas();
    error = false;
    time = 0;
}

bool Player::updateLogic(float deltaTime, sf::RenderWindow *window) {
    time += deltaTime;
    if (time > BLACKVALUE) {
        compas.incraeseTime();
        time = 0;
    }
    return false;
}

bool Player::event(sf::Event e, bool def) {
    switch(e.type) {
    case (sf::Event::KeyPressed):
        if(e.key.code == sf::Keyboard::C) {
            compas.start();
            error = false;
        }
        if(e.key.code == sf::Keyboard::Space) {
            if (animate == PlayerState::idle && !error) {
                compas.add();
                if (compas.isPressed()) animate = PlayerState::attacking;
            }
            else {
                compas.end();
                if (!error || (error && animate == PlayerState::idle))
                    animate = PlayerState::hurt;
                error = true;
            }
        }
        break;
    case (sf::Event::KeyReleased):
        if (e.key.code == sf::Keyboard::C) {
            compas.end();
            return false;
        }
        break;
    default:
        break;
    }
    return true;
}
<|endoftext|>


<commit_before>/*
 +----------------------------------------------------------------------+
 | Swoole                                                               |
 +----------------------------------------------------------------------+
 | This source file is subject to version 2.0 of the Apache license,    |
 | that is bundled with this package in the file LICENSE, and is        |
 | available through the world-wide-web at the following url:           |
 | http://www.apache.org/licenses/LICENSE-2.0.html                      |
 | If you did not receive a copy of the Apache2.0 license and are unable|
 | to obtain it through the world-wide-web, please send a note to       |
 | license@swoole.com so we can mail you a copy immediately.            |
 +----------------------------------------------------------------------+
 | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
 +----------------------------------------------------------------------+
 */

#include "swoole_api.h"
#include "async.h"

#include <thread>
#include <atomic>
#include <unordered_map>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>

using namespace std;

typedef swAio_event AsyncEvent;

static mutex init_lock;
static atomic<int> refcount(0);

static void aio_thread_release(AsyncEvent *event);

namespace swoole { namespace async {
//-------------------------------------------------------------------------------
class EventQueue
{
public:
    inline void push(AsyncEvent *event)
    {
        unique_lock<mutex> lock(_mutex);
        _queue.push(event);
    }

    inline AsyncEvent* pop()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return nullptr;
        }
        AsyncEvent* retval = _queue.front();
        _queue.pop();
        return retval;
    }

    inline double get_max_wait_time()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return 0;
        }
        else
        {
            AsyncEvent* event = _queue.front();
            return swoole_microtime() - event->timestamp;
        }
    }

    inline size_t count()
    {
        unique_lock<mutex> lock(_mutex);
        return _queue.size();
    }

private:
    queue<AsyncEvent *> _queue;
    mutex _mutex;
};

class ThreadPool
{
public:
    ThreadPool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time)
    {
        running = false;

        core_worker_num = _core_worker_num == 0 ? SW_CPU_NUM : SW_MAX(1, _core_worker_num);
        worker_num = _worker_num == 0 ? SW_CPU_NUM * SW_AIO_THREAD_NUM_MULTIPLE : SW_MAX(core_worker_num, _worker_num);
        max_wait_time = _max_wait_time == 0 ? SW_AIO_TASK_MAX_WAIT_TIME : _max_wait_time;
        max_idle_time = _max_idle_time == 0 ? SW_AIO_THREAD_MAX_IDLE_TIME : _max_idle_time;

        current_pid = getpid();
    }

    ~ThreadPool()
    {
        shutdown();
    }

    bool start()
    {
        running = true;
        current_task_id = 0;
        n_waiting = 0;
        n_closing = 0;
        for (size_t i = 0; i < core_worker_num; i++)
        {
            create_thread(true);
        }
        return true;
    }

    bool shutdown()
    {
        if (!running)
        {
            return false;
        }
        running = false;

        event_mutex.lock();
        _cv.notify_all();
        event_mutex.unlock();

        for (auto &i : threads)
        {
            thread *_thread = i.second;
            if (_thread->joinable())
            {
                _thread->join();
            }
            delete _thread;
        }

        return true;
    }

    void schedule()
    {
        if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0)
        {
            double _max_wait_time = _queue.get_max_wait_time();
            if (_max_wait_time > max_wait_time)
            {
                size_t n = 1;
                /**
                 * maybe we can find a better strategy
                 */
                if (threads.size() + n > worker_num)
                {
                    n = worker_num - threads.size();
                }
                swTraceLog(SW_TRACE_AIO, "Create %zu thread due to wait %fs, we will have %zu threads", n, _max_wait_time, threads.size() + n);
                while (n--)
                {
                    create_thread();
                }
            }
        }
    }

    AsyncEvent* dispatch(const AsyncEvent *request)
    {
        if (SwooleTG.aio_schedule)
        {
            schedule();
        }
        auto _event_copy = new AsyncEvent(*request);
        _event_copy->task_id = current_task_id++;
        _event_copy->timestamp = swoole_microtime();
        _event_copy->pipe_fd = SwooleTG.aio_pipe_write;
        _queue.push(_event_copy);
        _cv.notify_one();
        return _event_copy;
    }

    inline size_t worker_count()
    {
        return threads.size();
    }

    inline size_t queue_count()
    {
        return _queue.count();
    }

    pid_t current_pid;

    void release_thread(thread::id tid)
    {
        auto i = threads.find(tid);
        if (i == threads.end())
        {
            swWarn("AIO thread#%zu is missing", tid);
            return;
        }
        else
        {
            thread *_thread = i->second;
            swTraceLog(SW_TRACE_AIO, "release idle thread#%zu, we have %zu now", tid, threads.size() - 1);
            if (_thread->joinable())
            {
                _thread->join();
            }
            threads.erase(i);
            delete _thread;
        }
    }

private:
    void create_thread(const bool is_core_worker = false);

    size_t core_worker_num;
    size_t worker_num;
    double max_wait_time;
    double max_idle_time;

    bool running;

    atomic<size_t> n_waiting;
    atomic<size_t> n_closing;
    size_t current_task_id = 0;

    unordered_map<thread::id, thread *> threads;
    EventQueue _queue;
    mutex event_mutex;
    condition_variable _cv;
};
//-------------------------------------------------------------------------------
}};

static swoole::async::ThreadPool *pool = nullptr;

void swoole::async::ThreadPool::create_thread(const bool is_core_worker)
{
    try
    {
        thread *_thread = new thread([this, is_core_worker]()
        {
            bool exit_flag = false;

            SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE);
            if (SwooleTG.buffer_stack == nullptr)
            {
                return;
            }

            swSignal_none();

            while (running)
            {
                AsyncEvent *event = _queue.pop();
                if (event)
                {
                    if (sw_unlikely(event->handler == nullptr))
                    {
                        event->error = SW_ERROR_AIO_BAD_REQUEST;
                        event->ret = -1;
                    }
                    else if (sw_unlikely(event->canceled))
                    {
                        event->error = SW_ERROR_AIO_CANCELED;
                        event->ret = -1;
                    }
                    else
                    {
                        event->handler(event);
                    }

                    swTraceLog(SW_TRACE_AIO, "aio_thread %s. ret=%d, error=%d", event->ret > 0 ? "ok" : "failed", event->ret, event->error);

                    _send_event:
                    while (true)
                    {
                        int ret = write(event->pipe_fd, &event, sizeof(event));
                        if (ret < 0)
                        {
                            if (errno == EAGAIN)
                            {
                                swSocket_wait(SwooleTG.aio_pipe_write, 1000, SW_EVENT_WRITE);
                                continue;
                            }
                            else if (errno == EINTR)
                            {
                                continue;
                            }
                            else
                            {
                                swSysWarn("sendto swoole_aio_pipe_write failed");
                            }
                        }
                        break;
                    }

                    // exit
                    if (exit_flag)
                    {
                        n_closing--;
                        break;
                    }
                }
                else if (running)
                {
                    unique_lock<mutex> lock(event_mutex);
                    ++n_waiting;
                    if (is_core_worker || max_idle_time <= 0)
                    {
                        _cv.wait(lock);
                    }
                    else
                    {
                        while (true)
                        {
                            if (_cv.wait_for(lock, chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == cv_status::timeout)
                            {
                                if (running && n_closing != 0)
                                {
                                    // wait for the next round
                                    continue;
                                }
                                /* notifies the main thread to release this thread */
                                event = new AsyncEvent;
                                event->object = new thread::id(this_thread::get_id());
                                event->callback = aio_thread_release;
                                event->pipe_fd = SwooleG.aio_default_pipe_fd;

                                --n_waiting;
                                ++n_closing;
                                exit_flag = true;
                                goto _send_event;
                            }
                            break;
                        }
                    }
                    --n_waiting;
                }
            }
        });
        threads[_thread->get_id()] = _thread;
    }
    catch (const std::system_error& e)
    {
        swSysNotice("create aio thread failed, please check your system configuration or adjust aio_worker_num");
        return;
    }
}

static void aio_thread_release(swAio_event *event)
{
    thread::id *tid = static_cast<thread::id *>(event->object);
    pool->release_thread(*tid);
    delete tid;
    // balance
    SwooleTG.aio_task_num++;
}

static void swAio_free(void *private_data)
{
    if (!SwooleTG.aio_init)
    {
        return;
    }
    SwooleTG.aio_init = 0;
    swoole_event_del(SwooleTG.aio_pipe_read);
    SwooleTG.aio_pipe.close(&SwooleTG.aio_pipe);
    if (pool->current_pid == getpid())
    {
        if ((--refcount) == 0)
        {
            delete pool;
            pool = nullptr;
        }
    }
}

static int swAio_init()
{
    if (SwooleTG.aio_init)
    {
        swWarn("aio_thread_pool has already been initialized");
        return SW_ERR;
    }
    if (!SwooleTG.reactor)
    {
        swWarn("no event loop, cannot initialized");
        return SW_ERR;
    }

    if (swPipeBase_create(&SwooleTG.aio_pipe, 0) < 0)
    {
        swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL);
    }

    SwooleTG.aio_pipe_read = SwooleTG.aio_pipe.getFd(&SwooleTG.aio_pipe, 0);
    SwooleTG.aio_pipe_write = SwooleTG.aio_pipe.getFd(&SwooleTG.aio_pipe, 1);
    swoole_event_add(SwooleTG.aio_pipe_read, SW_EVENT_READ, SW_FD_AIO);
    swReactor_add_destroy_callback(SwooleTG.reactor, swAio_free, nullptr);

    init_lock.lock();
    if ((refcount++) == 0)
    {
        pool = new swoole::async::ThreadPool(SwooleG.aio_core_worker_num, SwooleG.aio_worker_num,
                SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time);
        pool->start();
        SwooleTG.aio_schedule = 1;
        SwooleG.aio_default_pipe_fd = SwooleTG.aio_pipe_write;
    }
    SwooleTG.aio_init = 1;
    init_lock.unlock();

    return SW_OK;
}

size_t swAio_thread_count()
{
    return pool ? pool->worker_count() : 0;
}

ssize_t swAio_dispatch(const swAio_event *request)
{
    AsyncEvent *event = swAio_dispatch2(request);
    if (event == nullptr)
    {
        return -1;
    }
    return event->task_id;
}

swAio_event* swAio_dispatch2(const swAio_event *request)
{
    if (sw_unlikely(!SwooleTG.aio_init))
    {
        swAio_init();
    }
    AsyncEvent *event = pool->dispatch(request);
    if (sw_likely(event))
    {
        SwooleTG.aio_task_num++;
    }
    return event;
}

int swAio_callback(swReactor *reactor, swEvent *event)
{
    if (SwooleTG.aio_schedule)
    {
        pool->schedule();
    }

    AsyncEvent *events[SW_AIO_EVENT_NUM];
    ssize_t n = read(event->fd, events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM);
    if (n < 0)
    {
        swSysWarn("read() aio events failed");
        return SW_ERR;
    }
    for (size_t i = 0; i < n / sizeof(AsyncEvent *); i++)
    {
        AsyncEvent *event = events[i];
        if (!event->canceled)
        {
            event->callback(events[i]);
        }
        SwooleTG.aio_task_num--;
        delete event;
    }

    return SW_OK;
}
<commit_msg>thread safe<commit_after>/*
 +----------------------------------------------------------------------+
 | Swoole                                                               |
 +----------------------------------------------------------------------+
 | This source file is subject to version 2.0 of the Apache license,    |
 | that is bundled with this package in the file LICENSE, and is        |
 | available through the world-wide-web at the following url:           |
 | http://www.apache.org/licenses/LICENSE-2.0.html                      |
 | If you did not receive a copy of the Apache2.0 license and are unable|
 | to obtain it through the world-wide-web, please send a note to       |
 | license@swoole.com so we can mail you a copy immediately.            |
 +----------------------------------------------------------------------+
 | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
 +----------------------------------------------------------------------+
 */

#include "swoole_api.h"
#include "async.h"

#include <thread>
#include <atomic>
#include <unordered_map>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>

using namespace std;

typedef swAio_event AsyncEvent;

static mutex init_lock;
static atomic<int> refcount(0);

static void aio_thread_release(AsyncEvent *event);

namespace swoole { namespace async {
//-------------------------------------------------------------------------------
class EventQueue
{
public:
    inline void push(AsyncEvent *event)
    {
        unique_lock<mutex> lock(_mutex);
        _queue.push(event);
    }

    inline AsyncEvent* pop()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return nullptr;
        }
        AsyncEvent* retval = _queue.front();
        _queue.pop();
        return retval;
    }

    inline double get_max_wait_time()
    {
        unique_lock<mutex> lock(_mutex);
        if (_queue.empty())
        {
            return 0;
        }
        else
        {
            AsyncEvent* event = _queue.front();
            return swoole_microtime() - event->timestamp;
        }
    }

    inline size_t count()
    {
        unique_lock<mutex> lock(_mutex);
        return _queue.size();
    }

private:
    queue<AsyncEvent *> _queue;
    mutex _mutex;
};

class ThreadPool
{
public:
    ThreadPool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time)
    {
        running = false;

        core_worker_num = _core_worker_num == 0 ? SW_CPU_NUM : SW_MAX(1, _core_worker_num);
        worker_num = _worker_num == 0 ? SW_CPU_NUM * SW_AIO_THREAD_NUM_MULTIPLE : SW_MAX(core_worker_num, _worker_num);
        max_wait_time = _max_wait_time == 0 ? SW_AIO_TASK_MAX_WAIT_TIME : _max_wait_time;
        max_idle_time = _max_idle_time == 0 ? SW_AIO_THREAD_MAX_IDLE_TIME : _max_idle_time;

        current_pid = getpid();
    }

    ~ThreadPool()
    {
        shutdown();
    }

    bool start()
    {
        running = true;
        current_task_id = 0;
        n_waiting = 0;
        n_closing = 0;
        for (size_t i = 0; i < core_worker_num; i++)
        {
            create_thread(true);
        }
        return true;
    }

    bool shutdown()
    {
        if (!running)
        {
            return false;
        }
        running = false;

        event_mutex.lock();
        _cv.notify_all();
        event_mutex.unlock();

        for (auto &i : threads)
        {
            thread *_thread = i.second;
            if (_thread->joinable())
            {
                _thread->join();
            }
            delete _thread;
        }

        return true;
    }

    void schedule()
    {
        if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0)
        {
            double _max_wait_time = _queue.get_max_wait_time();
            if (_max_wait_time > max_wait_time)
            {
                size_t n = 1;
                /**
                 * maybe we can find a better strategy
                 */
                if (threads.size() + n > worker_num)
                {
                    n = worker_num - threads.size();
                }
                swTraceLog(SW_TRACE_AIO, "Create %zu thread due to wait %fs, we will have %zu threads", n, _max_wait_time, threads.size() + n);
                while (n--)
                {
                    create_thread();
                }
            }
        }
    }

    AsyncEvent* dispatch(const AsyncEvent *request)
    {
        if (SwooleTG.aio_schedule)
        {
            schedule();
        }
        auto _event_copy = new AsyncEvent(*request);
        _event_copy->task_id = current_task_id++;
        _event_copy->timestamp = swoole_microtime();
        _event_copy->pipe_fd = SwooleTG.aio_pipe_write;
        event_mutex.lock();
        _queue.push(_event_copy);
        _cv.notify_one();
        event_mutex.unlock();
        return _event_copy;
    }

    inline size_t worker_count()
    {
        return threads.size();
    }

    inline size_t queue_count()
    {
        return _queue.count();
    }

    pid_t current_pid;

    void release_thread(thread::id tid)
    {
        auto i = threads.find(tid);
        if (i == threads.end())
        {
            swWarn("AIO thread#%zu is missing", tid);
            return;
        }
        else
        {
            thread *_thread = i->second;
            swTraceLog(SW_TRACE_AIO, "release idle thread#%zu, we have %zu now", tid, threads.size() - 1);
            if (_thread->joinable())
            {
                _thread->join();
            }
            threads.erase(i);
            delete _thread;
        }
    }

private:
    void create_thread(const bool is_core_worker = false);

    size_t core_worker_num;
    size_t worker_num;
    double max_wait_time;
    double max_idle_time;

    bool running;

    atomic<size_t> n_waiting;
    atomic<size_t> n_closing;
    size_t current_task_id = 0;

    unordered_map<thread::id, thread *> threads;
    EventQueue _queue;
    mutex event_mutex;
    condition_variable _cv;
};
//-------------------------------------------------------------------------------
}};

static swoole::async::ThreadPool *pool = nullptr;

void swoole::async::ThreadPool::create_thread(const bool is_core_worker)
{
    try
    {
        thread *_thread = new thread([this, is_core_worker]()
        {
            bool exit_flag = false;

            SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE);
            if (SwooleTG.buffer_stack == nullptr)
            {
                return;
            }

            swSignal_none();

            while (running)
            {
                AsyncEvent *event = _queue.pop();
                if (event)
                {
                    if (sw_unlikely(event->handler == nullptr))
                    {
                        event->error = SW_ERROR_AIO_BAD_REQUEST;
                        event->ret = -1;
                    }
                    else if (sw_unlikely(event->canceled))
                    {
                        event->error = SW_ERROR_AIO_CANCELED;
                        event->ret = -1;
                    }
                    else
                    {
                        event->handler(event);
                    }

                    swTraceLog(SW_TRACE_AIO, "aio_thread %s. ret=%d, error=%d", event->ret > 0 ? "ok" : "failed", event->ret, event->error);

                    _send_event:
                    while (true)
                    {
                        int ret = write(event->pipe_fd, &event, sizeof(event));
                        if (ret < 0)
                        {
                            if (errno == EAGAIN)
                            {
                                swSocket_wait(SwooleTG.aio_pipe_write, 1000, SW_EVENT_WRITE);
                                continue;
                            }
                            else if (errno == EINTR)
                            {
                                continue;
                            }
                            else
                            {
                                swSysWarn("sendto swoole_aio_pipe_write failed");
                            }
                        }
                        break;
                    }

                    // exit
                    if (exit_flag)
                    {
                        n_closing--;
                        break;
                    }
                }
                else if (running)
                {
                    unique_lock<mutex> lock(event_mutex);
                    if (_queue.count() > 0)
                    {
                        continue;
                    }
                    ++n_waiting;
                    if (is_core_worker || max_idle_time <= 0)
                    {
                        _cv.wait(lock);
                    }
                    else
                    {
                        while (true)
                        {
                            if (_cv.wait_for(lock, chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == cv_status::timeout)
                            {
                                if (running && n_closing != 0)
                                {
                                    // wait for the next round
                                    continue;
                                }
                                /* notifies the main thread to release this thread */
                                event = new AsyncEvent;
                                event->object = new thread::id(this_thread::get_id());
                                event->callback = aio_thread_release;
                                event->pipe_fd = SwooleG.aio_default_pipe_fd;

                                --n_waiting;
                                ++n_closing;
                                exit_flag = true;
                                goto _send_event;
                            }
                            break;
                        }
                    }
                    --n_waiting;
                }
            }
        });
        threads[_thread->get_id()] = _thread;
    }
    catch (const std::system_error& e)
    {
        swSysNotice("create aio thread failed, please check your system configuration or adjust aio_worker_num");
        return;
    }
}

static void aio_thread_release(swAio_event *event)
{
    thread::id *tid = static_cast<thread::id *>(event->object);
    pool->release_thread(*tid);
    delete tid;
    // balance
    SwooleTG.aio_task_num++;
}

static void swAio_free(void *private_data)
{
    if (!SwooleTG.aio_init)
    {
        return;
    }
    SwooleTG.aio_init = 0;
    swoole_event_del(SwooleTG.aio_pipe_read);
    SwooleTG.aio_pipe.close(&SwooleTG.aio_pipe);
    if (pool->current_pid == getpid())
    {
        if ((--refcount) == 0)
        {
            delete pool;
            pool = nullptr;
        }
    }
}

static int swAio_init()
{
    if (SwooleTG.aio_init)
    {
        swWarn("aio_thread_pool has already been initialized");
        return SW_ERR;
    }
    if (!SwooleTG.reactor)
    {
        swWarn("no event loop, cannot initialized");
        return SW_ERR;
    }

    if (swPipeBase_create(&SwooleTG.aio_pipe, 0) < 0)
    {
        swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL);
    }

    SwooleTG.aio_pipe_read = SwooleTG.aio_pipe.getFd(&SwooleTG.aio_pipe, 0);
    SwooleTG.aio_pipe_write = SwooleTG.aio_pipe.getFd(&SwooleTG.aio_pipe, 1);
    swoole_event_add(SwooleTG.aio_pipe_read, SW_EVENT_READ, SW_FD_AIO);
    swReactor_add_destroy_callback(SwooleTG.reactor, swAio_free, nullptr);

    init_lock.lock();
    if ((refcount++) == 0)
    {
        pool = new swoole::async::ThreadPool(SwooleG.aio_core_worker_num, SwooleG.aio_worker_num,
                SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time);
        pool->start();
        SwooleTG.aio_schedule = 1;
        SwooleG.aio_default_pipe_fd = SwooleTG.aio_pipe_write;
    }
    SwooleTG.aio_init = 1;
    init_lock.unlock();

    return SW_OK;
}

size_t swAio_thread_count()
{
    return pool ? pool->worker_count() : 0;
}

ssize_t swAio_dispatch(const swAio_event *request)
{
    AsyncEvent *event = swAio_dispatch2(request);
    if (event == nullptr)
    {
        return -1;
    }
    return event->task_id;
}

swAio_event* swAio_dispatch2(const swAio_event *request)
{
    if (sw_unlikely(!SwooleTG.aio_init))
    {
        swAio_init();
    }
    AsyncEvent *event = pool->dispatch(request);
    if (sw_likely(event))
    {
        SwooleTG.aio_task_num++;
    }
    return event;
}

int swAio_callback(swReactor *reactor, swEvent *event)
{
    if (SwooleTG.aio_schedule)
    {
        pool->schedule();
    }

    AsyncEvent *events[SW_AIO_EVENT_NUM];
    ssize_t n = read(event->fd, events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM);
    if (n < 0)
    {
        swSysWarn("read() aio events failed");
        return SW_ERR;
    }
    for (size_t i = 0; i < n / sizeof(AsyncEvent *); i++)
    {
        AsyncEvent *event = events[i];
        if (!event->canceled)
        {
            event->callback(events[i]);
        }
        SwooleTG.aio_task_num--;
        delete event;
    }

    return SW_OK;
}
<|endoftext|>


<commit_before>#include <bitcoin/network/network.hpp>

#include <functional>
#include <algorithm>
#include <iostream>

#include <bitcoin/utility/logger.hpp>

namespace libbitcoin {

using std::placeholders::_1;
using std::placeholders::_2;

acceptor::acceptor(threadpool& pool, tcp_acceptor_ptr tcp_accept)
  : pool_(pool), tcp_accept_(tcp_accept)
{
}
void acceptor::accept(accept_handler handle_accept)
{
    socket_ptr socket =
        std::make_shared<tcp::socket>(pool_.service());
    tcp_accept_->async_accept(*socket,
        std::bind(&acceptor::call_handle_accept, shared_from_this(),
            _1, socket, handle_accept));
}
void acceptor::call_handle_accept(const boost::system::error_code& ec,
    socket_ptr socket, accept_handler handle_accept)
{
    if (ec)
    {
        handle_accept(error::accept_failed, nullptr);
        return;
    }
    auto proxy = std::make_shared<channel_proxy>(pool_, socket);
    proxy->start();
    channel_ptr channel_object = std::make_shared<channel>(proxy);
    handle_accept(std::error_code(), channel_object);
}

class perform_connect_with_timeout
  : public std::enable_shared_from_this<perform_connect_with_timeout>
{
public:
    perform_connect_with_timeout(threadpool& pool)
      : timer_(pool.service())
    {
        socket_ = std::make_shared<tcp::socket>(pool.service());
        proxy_ = std::make_shared<channel_proxy>(pool, socket_);
    }

    void start(tcp::resolver::iterator endpoint_iterator, size_t timeout,
        network::connect_handler handle_connect)
    {
        timer_.expires_from_now(boost::posix_time::seconds(timeout));
        timer_.async_wait(std::bind(
            &perform_connect_with_timeout::close, shared_from_this(), _1));

        boost::asio::async_connect(*socket_, endpoint_iterator,
            std::bind(&perform_connect_with_timeout::call_connect_handler,
                shared_from_this(), _1, _2, handle_connect));
    }

private:
    void call_connect_handler(const boost::system::error_code& ec,
        tcp::resolver::iterator next, network::connect_handler handle_connect)
    {
        if (ec)
        {
            handle_connect(error::network_unreachable, nullptr);
            return;
        }
        proxy_->start();
        channel_ptr channel_object = std::make_shared<channel>(proxy_);
        handle_connect(std::error_code(), channel_object);
    }

    void close(const boost::system::error_code& ec)
    {
        if (ec)
            // should be boost::asio::error::operation_aborted)
            proxy_->stop();
    }

    socket_ptr socket_;
    channel::channel_proxy_ptr proxy_;
    boost::asio::deadline_timer timer_;
};

network::network(threadpool& pool)
  : pool_(pool)
{
}

void network::resolve_handler(const boost::system::error_code& ec,
    tcp::resolver::iterator endpoint_iterator,
    connect_handler handle_connect, resolver_ptr, query_ptr)
{
    if (ec)
    {
        handle_connect(error::resolve_failed, nullptr);
        return;
    }
    auto connect =
        std::make_shared<perform_connect_with_timeout>(pool_);
    connect->start(endpoint_iterator, 5, handle_connect);
}

void network::connect(const std::string& hostname, uint16_t port,
    connect_handler handle_connect)
{
    resolver_ptr resolver =
        std::make_shared<tcp::resolver>(pool_.service());
    query_ptr query =
        std::make_shared<tcp::resolver::query>(hostname, std::to_string(port));
    resolver->async_resolve(*query,
        std::bind(&network::resolve_handler,
            this, _1, _2, handle_connect, resolver, query));
}

// I personally don't like how exceptions mess with the program flow
bool listen_error(const boost::system::error_code& ec,
    network::listen_handler handle_listen)
{
    if (ec == boost::system::errc::address_in_use)
    {
        handle_listen(error::address_in_use, nullptr);
        return true;
    }
    else if (ec)
    {
        handle_listen(error::listen_failed, nullptr);
        return true;
    }
    return false;
}
void network::listen(uint16_t port, listen_handler handle_listen)
{
    tcp::endpoint endpoint(tcp::v4(), port);
    acceptor::tcp_acceptor_ptr tcp_accept =
        std::make_shared<tcp::acceptor>(pool_.service());
    // Need to check error codes for functions
    boost::system::error_code ec;
    tcp_accept->open(endpoint.protocol(), ec);
    if (listen_error(ec, handle_listen))
        return;
    tcp_accept->set_option(tcp::acceptor::reuse_address(true), ec);
    if (listen_error(ec, handle_listen))
        return;
    tcp_accept->bind(endpoint, ec);
    if (listen_error(ec, handle_listen))
        return;
    tcp_accept->listen(boost::asio::socket_base::max_connections, ec);
    if (listen_error(ec, handle_listen))
        return;

    acceptor_ptr accept =
        std::make_shared<acceptor>(pool_, tcp_accept);
    handle_listen(std::error_code(), accept);
}

} // namespace libbitcoin

<commit_msg>logic error in timeout stuff.<commit_after>#include <bitcoin/network/network.hpp>

#include <functional>
#include <algorithm>
#include <iostream>

#include <bitcoin/utility/logger.hpp>

namespace libbitcoin {

using std::placeholders::_1;
using std::placeholders::_2;

acceptor::acceptor(threadpool& pool, tcp_acceptor_ptr tcp_accept)
  : pool_(pool), tcp_accept_(tcp_accept)
{
}
void acceptor::accept(accept_handler handle_accept)
{
    socket_ptr socket =
        std::make_shared<tcp::socket>(pool_.service());
    tcp_accept_->async_accept(*socket,
        std::bind(&acceptor::call_handle_accept, shared_from_this(),
            _1, socket, handle_accept));
}
void acceptor::call_handle_accept(const boost::system::error_code& ec,
    socket_ptr socket, accept_handler handle_accept)
{
    if (ec)
    {
        handle_accept(error::accept_failed, nullptr);
        return;
    }
    auto proxy = std::make_shared<channel_proxy>(pool_, socket);
    proxy->start();
    channel_ptr channel_object = std::make_shared<channel>(proxy);
    handle_accept(std::error_code(), channel_object);
}

class perform_connect_with_timeout
  : public std::enable_shared_from_this<perform_connect_with_timeout>
{
public:
    perform_connect_with_timeout(threadpool& pool)
      : timer_(pool.service())
    {
        socket_ = std::make_shared<tcp::socket>(pool.service());
        proxy_ = std::make_shared<channel_proxy>(pool, socket_);
    }

    void start(tcp::resolver::iterator endpoint_iterator, size_t timeout,
        network::connect_handler handle_connect)
    {
        timer_.expires_from_now(boost::posix_time::seconds(timeout));
        timer_.async_wait(std::bind(
            &perform_connect_with_timeout::close, shared_from_this(), _1));

        boost::asio::async_connect(*socket_, endpoint_iterator,
            std::bind(&perform_connect_with_timeout::call_connect_handler,
                shared_from_this(), _1, _2, handle_connect));
    }

private:
    void call_connect_handler(const boost::system::error_code& ec,
        tcp::resolver::iterator next, network::connect_handler handle_connect)
    {
        if (ec)
        {
            handle_connect(error::network_unreachable, nullptr);
            return;
        }
        proxy_->start();
        channel_ptr channel_object = std::make_shared<channel>(proxy_);
        handle_connect(std::error_code(), channel_object);
    }

    void close(const boost::system::error_code& ec)
    {
        // ec should be boost::asio::error::operation_aborted or nothing.
        if (!ec)
            proxy_->stop();
    }

    socket_ptr socket_;
    channel::channel_proxy_ptr proxy_;
    boost::asio::deadline_timer timer_;
};

network::network(threadpool& pool)
  : pool_(pool)
{
}

void network::resolve_handler(const boost::system::error_code& ec,
    tcp::resolver::iterator endpoint_iterator,
    connect_handler handle_connect, resolver_ptr, query_ptr)
{
    if (ec)
    {
        handle_connect(error::resolve_failed, nullptr);
        return;
    }
    auto connect =
        std::make_shared<perform_connect_with_timeout>(pool_);
    connect->start(endpoint_iterator, 5, handle_connect);
}

void network::connect(const std::string& hostname, uint16_t port,
    connect_handler handle_connect)
{
    resolver_ptr resolver =
        std::make_shared<tcp::resolver>(pool_.service());
    query_ptr query =
        std::make_shared<tcp::resolver::query>(hostname, std::to_string(port));
    resolver->async_resolve(*query,
        std::bind(&network::resolve_handler,
            this, _1, _2, handle_connect, resolver, query));
}

// I personally don't like how exceptions mess with the program flow
bool listen_error(const boost::system::error_code& ec,
    network::listen_handler handle_listen)
{
    if (ec == boost::system::errc::address_in_use)
    {
        handle_listen(error::address_in_use, nullptr);
        return true;
    }
    else if (ec)
    {
        handle_listen(error::listen_failed, nullptr);
        return true;
    }
    return false;
}
void network::listen(uint16_t port, listen_handler handle_listen)
{
    tcp::endpoint endpoint(tcp::v4(), port);
    acceptor::tcp_acceptor_ptr tcp_accept =
        std::make_shared<tcp::acceptor>(pool_.service());
    // Need to check error codes for functions
    boost::system::error_code ec;
    tcp_accept->open(endpoint.protocol(), ec);
    if (listen_error(ec, handle_listen))
        return;
    tcp_accept->set_option(tcp::acceptor::reuse_address(true), ec);
    if (listen_error(ec, handle_listen))
        return;
    tcp_accept->bind(endpoint, ec);
    if (listen_error(ec, handle_listen))
        return;
    tcp_accept->listen(boost::asio::socket_base::max_connections, ec);
    if (listen_error(ec, handle_listen))
        return;

    acceptor_ptr accept =
        std::make_shared<acceptor>(pool_, tcp_accept);
    handle_listen(std::error_code(), accept);
}

} // namespace libbitcoin

<|endoftext|>


<commit_before>#include "util.hh"
#include "get-drvs.hh"
#include "derivations.hh"
#include "store-api.hh"
#include "globals.hh"
#include "shared.hh"
#include "eval.hh"
#include "profiles.hh"

namespace nix {

static void readLegacyManifest(const Path & path, DrvInfos & elems);

DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
{
    DrvInfos elems;

    Path manifestFile = userEnv + "/manifest.nix";
    Path oldManifestFile = userEnv + "/manifest";

    if (pathExists(manifestFile)) {
        Value v;
        state.evalFile(manifestFile, v);
        Bindings bindings;
        getDerivations(state, v, "", bindings, elems, false);
    } else if (pathExists(oldManifestFile))
        readLegacyManifest(oldManifestFile, elems);

    return elems;
}

bool createUserEnv(EvalState & state, DrvInfos & elems,
    const Path & profile, bool keepDerivations,
    const string & lockToken)
{
    /* Build the components in the user environment, if they don't
       exist already. */
    PathSet drvsToBuild;
    foreach (DrvInfos::const_iterator, i, elems)
        if (i->queryDrvPath(state) != "")
            drvsToBuild.insert(i->queryDrvPath(state));

    debug(format("building user environment dependencies"));
    store->buildPaths(drvsToBuild, state.repair);

    /* Construct the whole top level derivation. */
    PathSet references;
    Value manifest;
    state.mkList(manifest, elems.size());
    unsigned int n = 0;
    foreach (DrvInfos::iterator, i, elems) {
        /* Create a pseudo-derivation containing the name, system,
           output paths, and optionally the derivation path, as well
           as the meta attributes. */
        Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";

        Value & v(*state.allocValue());
        manifest.list.elems[n++] = &v;
        state.mkAttrs(v, 16);

        mkString(*state.allocAttr(v, state.sType), "derivation");
        mkString(*state.allocAttr(v, state.sName), i->name);
        if (!i->system.empty())
            mkString(*state.allocAttr(v, state.sSystem), i->system);
        mkString(*state.allocAttr(v, state.sOutPath), i->queryOutPath(state));
        if (drvPath != "")
            mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath(state));

        // Copy each output.
        DrvInfo::Outputs outputs = i->queryOutputs(state);
        Value & vOutputs = *state.allocAttr(v, state.sOutputs);
        state.mkList(vOutputs, outputs.size());
        unsigned int m = 0;
        foreach (DrvInfo::Outputs::iterator, j, outputs) {
            mkString(*(vOutputs.list.elems[m++] = state.allocValue()), j->first);
            Value & vOutputs = *state.allocAttr(v, state.symbols.create(j->first));
            state.mkAttrs(vOutputs, 2);
            mkString(*state.allocAttr(vOutputs, state.sOutPath), j->second);

            /* This is only necessary when installing store paths, e.g.,
               `nix-env -i /nix/store/abcd...-foo'. */
            store->addTempRoot(j->second);
            store->ensurePath(j->second);

            references.insert(j->second);
        }

        // Copy the meta attributes.
        Value & vMeta = *state.allocAttr(v, state.sMeta);
        state.mkAttrs(vMeta, 16);

        MetaInfo meta = i->queryMetaInfo(state);

        foreach (MetaInfo::const_iterator, j, meta) {
            Value & v2(*state.allocAttr(vMeta, state.symbols.create(j->first)));
            switch (j->second.type) {
                case MetaValue::tpInt: mkInt(v2, j->second.intValue); break;
                case MetaValue::tpString: mkString(v2, j->second.stringValue); break;
                case MetaValue::tpStrings: {
                    state.mkList(v2, j->second.stringValues.size());
                    unsigned int m = 0;
                    foreach (Strings::const_iterator, k, j->second.stringValues) {
                        v2.list.elems[m] = state.allocValue();
                        mkString(*v2.list.elems[m++], *k);
                    }
                    break;
                }
                default: abort();
            }
        }

        vMeta.attrs->sort();
        v.attrs->sort();

        if (drvPath != "") references.insert(drvPath);
    }

    /* Also write a copy of the list of user environment elements to
       the store; we need it for future modifications of the
       environment. */
    Path manifestFile = store->addTextToStore("env-manifest.nix",
        (format("%1%") % manifest).str(), references);

    /* Get the environment builder expression. */
    Value envBuilder;
    state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);

    /* Construct a Nix expression that calls the user environment
       builder with the manifest as argument. */
    Value args, topLevel;
    state.mkAttrs(args, 3);
    mkString(*state.allocAttr(args, state.symbols.create("manifest")),
        manifestFile, singleton<PathSet>(manifestFile));
    args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
    args.attrs->sort();
    mkApp(topLevel, envBuilder, args);

    /* Evaluate it. */
    debug("evaluating user environment builder");
    DrvInfo topLevelDrv;
    if (!getDerivation(state, topLevel, topLevelDrv, false))
        abort();

    /* Realise the resulting store expression. */
    debug("building user environment");
    store->buildPaths(singleton<PathSet>(topLevelDrv.queryDrvPath(state)), state.repair);

    /* Switch the current user environment to the output path. */
    PathLocks lock;
    lockProfile(lock, profile);

    Path lockTokenCur = optimisticLockProfile(profile);
    if (lockToken != lockTokenCur) {
        printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile);
        return false;
    }

    debug(format("switching to new user environment"));
    Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state));
    switchLink(profile, generation);

    return true;
}

/* Code for parsing manifests in the old textual ATerm format. */

static string parseStr(std::istream & str)
{
    expect(str, "Str(");
    string s = parseString(str);
    expect(str, ",[])");
    return s;
}

static string parseWord(std::istream & str)
{
    string res;
    while (isalpha(str.peek()))
        res += str.get();
    return res;
}

static MetaInfo parseMeta(std::istream & str)
{
    MetaInfo meta;

    expect(str, "Attrs([");
    while (!endOfList(str)) {
        expect(str, "Bind(");

        MetaValue value;

        string name = parseString(str);
        expect(str, ",");

        string type = parseWord(str);

        if (type == "Str") {
            expect(str, "(");
            value.type = MetaValue::tpString;
            value.stringValue = parseString(str);
            expect(str, ",[])");
        }

        else if (type == "List") {
            expect(str, "([");
            value.type = MetaValue::tpStrings;
            while (!endOfList(str))
                value.stringValues.push_back(parseStr(str));
            expect(str, ")");
        }

        else throw Error(format("unexpected token `%1%'") % type);

        expect(str, ",NoPos)");
        meta[name] = value;
    }

    expect(str, ")");

    return meta;
}

static void readLegacyManifest(const Path & path, DrvInfos & elems)
{
    string manifest = readFile(path);
    std::istringstream str(manifest);
    expect(str, "List([");

    unsigned int n = 0;

    while (!endOfList(str)) {
        DrvInfo elem;
        expect(str, "Attrs([");

        while (!endOfList(str)) {
            expect(str, "Bind(");
            string name = parseString(str);
            expect(str, ",");

            if (name == "meta") elem.setMetaInfo(parseMeta(str));
            else {
                string value = parseStr(str);
                if (name == "name") elem.name = value;
                else if (name == "outPath") elem.setOutPath(value);
                else if (name == "drvPath") elem.setDrvPath(value);
                else if (name == "system") elem.system = value;
            }

            expect(str, ",NoPos)");
        }

        expect(str, ")");

        if (elem.name != "") {
            elem.attrPath = int2String(n++);
            elems.push_back(elem);
        }
    }

    expect(str, ")");
}

}
<commit_msg>Drop support for user environment manifests in ATerm format<commit_after>#include "util.hh"
#include "get-drvs.hh"
#include "derivations.hh"
#include "store-api.hh"
#include "globals.hh"
#include "shared.hh"
#include "eval.hh"
#include "profiles.hh"

namespace nix {

DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
{
    DrvInfos elems;
    Path manifestFile = userEnv + "/manifest.nix";
    if (pathExists(manifestFile)) {
        Value v;
        state.evalFile(manifestFile, v);
        Bindings bindings;
        getDerivations(state, v, "", bindings, elems, false);
    }
    return elems;
}

bool createUserEnv(EvalState & state, DrvInfos & elems,
    const Path & profile, bool keepDerivations,
    const string & lockToken)
{
    /* Build the components in the user environment, if they don't
       exist already. */
    PathSet drvsToBuild;
    foreach (DrvInfos::const_iterator, i, elems)
        if (i->queryDrvPath(state) != "")
            drvsToBuild.insert(i->queryDrvPath(state));

    debug(format("building user environment dependencies"));
    store->buildPaths(drvsToBuild, state.repair);

    /* Construct the whole top level derivation. */
    PathSet references;
    Value manifest;
    state.mkList(manifest, elems.size());
    unsigned int n = 0;
    foreach (DrvInfos::iterator, i, elems) {
        /* Create a pseudo-derivation containing the name, system,
           output paths, and optionally the derivation path, as well
           as the meta attributes. */
        Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";

        Value & v(*state.allocValue());
        manifest.list.elems[n++] = &v;
        state.mkAttrs(v, 16);

        mkString(*state.allocAttr(v, state.sType), "derivation");
        mkString(*state.allocAttr(v, state.sName), i->name);
        if (!i->system.empty())
            mkString(*state.allocAttr(v, state.sSystem), i->system);
        mkString(*state.allocAttr(v, state.sOutPath), i->queryOutPath(state));
        if (drvPath != "")
            mkString(*state.allocAttr(v, state.sDrvPath), i->queryDrvPath(state));

        // Copy each output.
        DrvInfo::Outputs outputs = i->queryOutputs(state);
        Value & vOutputs = *state.allocAttr(v, state.sOutputs);
        state.mkList(vOutputs, outputs.size());
        unsigned int m = 0;
        foreach (DrvInfo::Outputs::iterator, j, outputs) {
            mkString(*(vOutputs.list.elems[m++] = state.allocValue()), j->first);
            Value & vOutputs = *state.allocAttr(v, state.symbols.create(j->first));
            state.mkAttrs(vOutputs, 2);
            mkString(*state.allocAttr(vOutputs, state.sOutPath), j->second);

            /* This is only necessary when installing store paths, e.g.,
               `nix-env -i /nix/store/abcd...-foo'. */
            store->addTempRoot(j->second);
            store->ensurePath(j->second);

            references.insert(j->second);
        }

        // Copy the meta attributes.
        Value & vMeta = *state.allocAttr(v, state.sMeta);
        state.mkAttrs(vMeta, 16);

        MetaInfo meta = i->queryMetaInfo(state);

        foreach (MetaInfo::const_iterator, j, meta) {
            Value & v2(*state.allocAttr(vMeta, state.symbols.create(j->first)));
            switch (j->second.type) {
                case MetaValue::tpInt: mkInt(v2, j->second.intValue); break;
                case MetaValue::tpString: mkString(v2, j->second.stringValue); break;
                case MetaValue::tpStrings: {
                    state.mkList(v2, j->second.stringValues.size());
                    unsigned int m = 0;
                    foreach (Strings::const_iterator, k, j->second.stringValues) {
                        v2.list.elems[m] = state.allocValue();
                        mkString(*v2.list.elems[m++], *k);
                    }
                    break;
                }
                default: abort();
            }
        }

        vMeta.attrs->sort();
        v.attrs->sort();

        if (drvPath != "") references.insert(drvPath);
    }

    /* Also write a copy of the list of user environment elements to
       the store; we need it for future modifications of the
       environment. */
    Path manifestFile = store->addTextToStore("env-manifest.nix",
        (format("%1%") % manifest).str(), references);

    /* Get the environment builder expression. */
    Value envBuilder;
    state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);

    /* Construct a Nix expression that calls the user environment
       builder with the manifest as argument. */
    Value args, topLevel;
    state.mkAttrs(args, 3);
    mkString(*state.allocAttr(args, state.symbols.create("manifest")),
        manifestFile, singleton<PathSet>(manifestFile));
    args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
    args.attrs->sort();
    mkApp(topLevel, envBuilder, args);

    /* Evaluate it. */
    debug("evaluating user environment builder");
    DrvInfo topLevelDrv;
    if (!getDerivation(state, topLevel, topLevelDrv, false))
        abort();

    /* Realise the resulting store expression. */
    debug("building user environment");
    store->buildPaths(singleton<PathSet>(topLevelDrv.queryDrvPath(state)), state.repair);

    /* Switch the current user environment to the output path. */
    PathLocks lock;
    lockProfile(lock, profile);

    Path lockTokenCur = optimisticLockProfile(profile);
    if (lockToken != lockTokenCur) {
        printMsg(lvlError, format("profile `%1%' changed while we were busy; restarting") % profile);
        return false;
    }

    debug(format("switching to new user environment"));
    Path generation = createGeneration(profile, topLevelDrv.queryOutPath(state));
    switchLink(profile, generation);

    return true;
}

}
<|endoftext|>


<commit_before>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "generator.h"
#include "device.h"

#include <QDBusReply>
#include <QDBusMessage>
#include <QDBusConnection>

#include <kdebug.h>
#include <kscreen/config.h>

Generator* Generator::instance = 0;

Generator* Generator::self()
{
    if (!Generator::instance) {
        Generator::instance = new Generator();
    }
    return Generator::instance;
}

Generator::Generator()
 : QObject()
 , m_isReady(false)
 , m_forceLaptop(false)
 , m_forceLidClosed(false)
 , m_forceDocked(false)
{
    connect(Device::self(), SIGNAL(ready()), SIGNAL(ready()));
}

void Generator::destroy()
{
    delete Generator::instance;
    Generator::instance = 0;
}

Generator::~Generator()
{
}

KScreen::Config* Generator::idealConfig()
{
    KDebug::Block idealBlock("Ideal Config");
    KScreen::Config* config = KScreen::Config::current();
    if (!config) {
        kDebug() << "Unable get current config";
        return 0;
    }

    disableAllDisconnectedOutputs(config->outputs());

    KScreen::OutputList outputs = config->connectedOutputs();

    kDebug() << "Connected outputs: " << outputs.count();
    if (outputs.count() == 1) {
        singleOutput(outputs);
        return config;
    }

    if (isLaptop()) {
        laptop(outputs);
        return config;
    }

    extendToRight(outputs);

    return config;
}

KScreen::Config* Generator::displaySwitch(int iteration)
{
    KDebug::Block switchBlock("Display Switch");
    KScreen::Config* config = KScreen::Config::current();
    if (!config) {
        kDebug() << "Unable to get current config";
        return 0;
    }

    KScreen::OutputList outputs = config->connectedOutputs();

    if (outputs.count() < 2) {
        singleOutput(outputs);
        return config;
    }

    if (outputs.count() > 2) {
        extendToRight(outputs);
        return config;
    }

    KScreen::Output* embedded, *external;
    embedded = embeddedOutput(outputs);
    outputs.remove(embedded->id());
    external = outputs.value(outputs.keys().first());

    if (iteration == 1) {
        kDebug() << "Cloning";
        KScreen::ModeList modes = embedded->modes();
        QMap<QString, QSize> embeddedModeSize;
        Q_FOREACH(KScreen::Mode* mode, modes) {
            embeddedModeSize.insert(mode->id(), mode->size());
        }

        QList<QString> embeddedKeys;
        KScreen::ModeList externalCommon;
        KScreen::ModeList externalModes = external->modes();
        Q_FOREACH(KScreen::Mode* mode, externalModes) {
            if (!embeddedModeSize.keys(mode->size()).isEmpty()) {
                externalCommon.insert(mode->id(), mode);
                embeddedKeys.append(embeddedModeSize.keys(mode->size()));
            }
        }

        KScreen::ModeList embeddedCommon;
        Q_FOREACH(const QString& key, embeddedKeys) {
            embeddedCommon.insert(key, modes[key]);
        }

        KScreen::Mode* biggestEmbedded = biggestMode(embeddedCommon);
        KScreen::Mode* biggestExternal = biggestMode(externalCommon);

        embedded->setEnabled(true);
        embedded->setPos(QPoint(0,0));
        embedded->setCurrentModeId(biggestEmbedded->id());
        external->setEnabled(true);
        external->setPos(QPoint(0,0));
        external->setCurrentModeId(biggestExternal->id());

        return config;
    }

    if (iteration == 2) {
        kDebug() << "Extend to left";
        external->setEnabled(true);
        external->setPos(QPoint(0,0));
        external->setCurrentModeId(external->preferredModeId());

        QSize size = external->currentMode()->size();
        embedded->setPos(QPoint(size.width(), 0));
        embedded->setEnabled(true);
        embedded->setCurrentModeId(embedded->preferredModeId());
        embedded->setPrimary(true);
        return config;
    }

    if (iteration == 3) {
        kDebug() << "Turn of embedded (laptop)";
        embedded->setEnabled(false);
        embedded->setPrimary(false);

        external->setEnabled(true);
        external->setPrimary(true);
        external->setCurrentModeId(external->preferredModeId());
        return config;
    }

    if (iteration == 4) {
        kDebug() << "Turn off external screen";
        embedded->setEnabled(true);
        embedded->setPrimary(true);
        embedded->setPos(QPoint(0,0));
        embedded->setCurrentModeId(embedded->preferredModeId());

        external->setEnabled(false);
        external->setPrimary(false);
        return config;
    }

    if (iteration == 5) {
        kDebug() << "Extend to the right";
        embedded->setPos(QPoint(0,0));
        embedded->setCurrentModeId(embedded->preferredModeId());
        embedded->setPrimary(true);
        embedded->setEnabled(true);

        QSize size = embedded->currentMode()->size();
        external->setPos(QPoint(size.width(), 0));
        external->setEnabled(true);
        external->setCurrentModeId(external->preferredModeId());
        external->setPrimary(false);

        return config;
    }

    return config;
}

void Generator::singleOutput(KScreen::OutputList& outputs)
{
    Q_ASSERT(!outputs.isEmpty());

    KScreen::Output* output = outputs.take(outputs.keys().first());
    Q_ASSERT(output);

    output->setCurrentModeId(output->preferredModeId());
    output->setEnabled(true);
    output->setPrimary(true);
    output->setPos(QPoint(0,0));
}

void Generator::laptop(KScreen::OutputList& outputs)
{
    Q_ASSERT(!outputs.isEmpty());

    KDebug::Block laptopBlock("Laptop config");

    KScreen::Output* embedded = embeddedOutput(outputs);
    Q_ASSERT(embedded);

    outputs.remove(embedded->id());

    if (outputs.isEmpty()) {
        kWarning() << "No external outputs found, going for singleOutput()";
        outputs.insert(embedded->id(), embedded);
        return singleOutput(outputs);
    }

    if (isLidClosed() && outputs.count() == 1) {
        kDebug() << "With lid closed";
        embedded->setEnabled(false);
        embedded->setPrimary(false);

        KScreen::Output* external = outputs.value(outputs.keys().first());
        external->setEnabled(true);
        external->setPrimary(true);
        external->setCurrentModeId(external->preferredModeId());
        external->setPos(QPoint(0, 0));

        return;
    }

    if (isLidClosed() && outputs.count() > 1) {
        kDebug() << "Lid is closed, and more than one output";
        embedded->setEnabled(false);
        embedded->setPrimary(false);

        extendToRight(outputs);
        return;
    }

    kDebug() << "Lid is open";
    //If lid is open, laptop screen shuold be primary
    embedded->setPos(QPoint(0,0));
    embedded->setCurrentModeId(embedded->preferredModeId());
    embedded->setPrimary(true);
    embedded->setEnabled(true);

    int globalWidth;
    if (embedded->isHorizontal()) {
        globalWidth = embedded->preferredMode()->size().width();
    } else {
        globalWidth = embedded->preferredMode()->size().height();
    }
    KScreen::Output* biggest = biggestOutput(outputs);
    outputs.remove(biggest->id());

    biggest->setPos(QPoint(globalWidth, 0));
    biggest->setEnabled(true);
    biggest->setCurrentModeId(biggest->preferredModeId());
    biggest->setPrimary(false);

    if (biggest->isHorizontal()) {
        globalWidth += biggest->currentMode()->size().width();
    } else {
        globalWidth += biggest->currentMode()->size().height();
    }
    Q_FOREACH(KScreen::Output* output, outputs) {
        output->setEnabled(true);
        output->setCurrentModeId(output->preferredModeId());
        output->setPos(QPoint(globalWidth, 0));
        output->setPrimary(false);

        if (output->isHorizontal()) {
            globalWidth += output->currentMode()->size().width();
        } else {
            globalWidth += output->currentMode()->size().height();
        }
    }

    if (isDocked()) {
        kDebug() << "Docked";
        embedded->setPrimary(false);
        biggest->setPrimary(true);
    }
}

void Generator::extendToRight(KScreen::OutputList& outputs)
{
    Q_ASSERT(!outputs.isEmpty());

    kDebug() << "Extending to the right";
    KScreen::Output* biggest = biggestOutput(outputs);
    Q_ASSERT(biggest);

    outputs.remove(biggest->id());

    biggest->setEnabled(true);
    biggest->setPrimary(true);
    biggest->setCurrentModeId(biggest->preferredModeId());
    biggest->setPos(QPoint(0,0));

    int globalWidth;
    if (biggest->isHorizontal()) {
        globalWidth = biggest->currentMode()->size().width();
    } else {
        globalWidth = biggest->currentMode()->size().height();
    }

    Q_FOREACH(KScreen::Output* output, outputs) {
        output->setEnabled(true);
        output->setPrimary(false);
        output->setCurrentModeId(output->preferredModeId());
        output->setPos(QPoint(globalWidth, 0));

        if (output->isHorizontal()) {
            globalWidth += output->currentMode()->size().width();
        } else {
            globalWidth += output->currentMode()->size().height();
        }
    }
}

KScreen::Mode* Generator::biggestMode(const KScreen::ModeList& modes)
{
    int area, total = 0;
    KScreen::Mode* biggest = 0;
    Q_FOREACH(KScreen::Mode* mode, modes) {
        area = mode->size().width() * mode->size().height();
        if (area < total) {
            continue;
        }
        if (area == total && mode->refreshRate() > biggest->refreshRate()) {
            biggest = mode;
            continue;
        }

        total = area;
        biggest = mode;
    }

    return biggest;
}

KScreen::Output* Generator::biggestOutput(const KScreen::OutputList &outputs)
{
    int area, total = 0;
    KScreen::Output* biggest = 0;
    Q_FOREACH(KScreen::Output* output, outputs) {
        KScreen::Mode* mode = output->preferredMode();
        area = mode->size().width() * mode->size().height();
        if (area < total) {
            continue;
        }

        total = area;
        biggest = output;
    }

    return biggest;
}

void Generator::disableAllDisconnectedOutputs(const KScreen::OutputList& outputs)
{
    KDebug::Block disableBlock("Disabling disconnected screens");
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!output->isConnected()) {
            kDebug() << output->name() << " Disabled";
            output->setEnabled(false);
            output->setPrimary(false);
        }
    }
}

KScreen::Output* Generator::embeddedOutput(const KScreen::OutputList& outputs)
{
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!isEmbedded(output->name())) {
            continue;
        }

        return output;
    }

    return 0;
}

bool Generator::isLaptop()
{
    if (m_forceLaptop) {
        return true;
    }

    return Device::self()->isLaptop();
}

bool Generator::isLidClosed()
{
    if (m_forceLidClosed) {
        return true;
    }

    return Device::self()->isLidClosed();
}

bool Generator::isDocked()
{
    if (m_forceDocked) {
        return true;
    }

    return Device::self()->isDocked();
}

bool Generator::isEmbedded(const QString &name)
{
    QStringList embedded;
    embedded << "LVDS";
    embedded << "IDP";
    embedded << "EDP";

    Q_FOREACH(const QString &pre, embedded) {
        if (name.toUpper().startsWith(pre)) {
            return true;
        }
    }

    return false;
}

void Generator::setForceLaptop(bool force)
{
    m_forceLaptop = force;
}

void Generator::setForceLidClosed(bool force)
{
    m_forceLidClosed = force;
}

void Generator::setForceDocked(bool force)
{
    m_forceDocked = force;
}
<commit_msg>If the mode area is the same but the refresh is smaller, continue<commit_after>/*************************************************************************************
 *  Copyright (C) 2012 by Alejandro Fiestas Olivares <afiestas@kde.org>              *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or                    *
 *  modify it under the terms of the GNU General Public License                      *
 *  as published by the Free Software Foundation; either version 2                   *
 *  of the License, or (at your option) any later version.                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,                  *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
 *  GNU General Public License for more details.                                     *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License                *
 *  along with this program; if not, write to the Free Software                      *
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
 *************************************************************************************/

#include "generator.h"
#include "device.h"

#include <QDBusReply>
#include <QDBusMessage>
#include <QDBusConnection>

#include <kdebug.h>
#include <kscreen/config.h>

Generator* Generator::instance = 0;

Generator* Generator::self()
{
    if (!Generator::instance) {
        Generator::instance = new Generator();
    }
    return Generator::instance;
}

Generator::Generator()
 : QObject()
 , m_isReady(false)
 , m_forceLaptop(false)
 , m_forceLidClosed(false)
 , m_forceDocked(false)
{
    connect(Device::self(), SIGNAL(ready()), SIGNAL(ready()));
}

void Generator::destroy()
{
    delete Generator::instance;
    Generator::instance = 0;
}

Generator::~Generator()
{
}

KScreen::Config* Generator::idealConfig()
{
    KDebug::Block idealBlock("Ideal Config");
    KScreen::Config* config = KScreen::Config::current();
    if (!config) {
        kDebug() << "Unable get current config";
        return 0;
    }

    disableAllDisconnectedOutputs(config->outputs());

    KScreen::OutputList outputs = config->connectedOutputs();

    kDebug() << "Connected outputs: " << outputs.count();
    if (outputs.count() == 1) {
        singleOutput(outputs);
        return config;
    }

    if (isLaptop()) {
        laptop(outputs);
        return config;
    }

    extendToRight(outputs);

    return config;
}

KScreen::Config* Generator::displaySwitch(int iteration)
{
    KDebug::Block switchBlock("Display Switch");
    KScreen::Config* config = KScreen::Config::current();
    if (!config) {
        kDebug() << "Unable to get current config";
        return 0;
    }

    KScreen::OutputList outputs = config->connectedOutputs();

    if (outputs.count() < 2) {
        singleOutput(outputs);
        return config;
    }

    if (outputs.count() > 2) {
        extendToRight(outputs);
        return config;
    }

    KScreen::Output* embedded, *external;
    embedded = embeddedOutput(outputs);
    outputs.remove(embedded->id());
    external = outputs.value(outputs.keys().first());

    if (iteration == 1) {
        kDebug() << "Cloning";
        KScreen::ModeList modes = embedded->modes();
        QMap<QString, QSize> embeddedModeSize;
        Q_FOREACH(KScreen::Mode* mode, modes) {
            embeddedModeSize.insert(mode->id(), mode->size());
        }

        QList<QString> embeddedKeys;
        KScreen::ModeList externalCommon;
        KScreen::ModeList externalModes = external->modes();
        Q_FOREACH(KScreen::Mode* mode, externalModes) {
            if (!embeddedModeSize.keys(mode->size()).isEmpty()) {
                externalCommon.insert(mode->id(), mode);
                embeddedKeys.append(embeddedModeSize.keys(mode->size()));
            }
        }

        KScreen::ModeList embeddedCommon;
        Q_FOREACH(const QString& key, embeddedKeys) {
            embeddedCommon.insert(key, modes[key]);
        }

        KScreen::Mode* biggestEmbedded = biggestMode(embeddedCommon);
        KScreen::Mode* biggestExternal = biggestMode(externalCommon);

        embedded->setEnabled(true);
        embedded->setPos(QPoint(0,0));
        embedded->setCurrentModeId(biggestEmbedded->id());
        external->setEnabled(true);
        external->setPos(QPoint(0,0));
        external->setCurrentModeId(biggestExternal->id());

        return config;
    }

    if (iteration == 2) {
        kDebug() << "Extend to left";
        external->setEnabled(true);
        external->setPos(QPoint(0,0));
        external->setCurrentModeId(external->preferredModeId());

        QSize size = external->currentMode()->size();
        embedded->setPos(QPoint(size.width(), 0));
        embedded->setEnabled(true);
        embedded->setCurrentModeId(embedded->preferredModeId());
        embedded->setPrimary(true);
        return config;
    }

    if (iteration == 3) {
        kDebug() << "Turn of embedded (laptop)";
        embedded->setEnabled(false);
        embedded->setPrimary(false);

        external->setEnabled(true);
        external->setPrimary(true);
        external->setCurrentModeId(external->preferredModeId());
        return config;
    }

    if (iteration == 4) {
        kDebug() << "Turn off external screen";
        embedded->setEnabled(true);
        embedded->setPrimary(true);
        embedded->setPos(QPoint(0,0));
        embedded->setCurrentModeId(embedded->preferredModeId());

        external->setEnabled(false);
        external->setPrimary(false);
        return config;
    }

    if (iteration == 5) {
        kDebug() << "Extend to the right";
        embedded->setPos(QPoint(0,0));
        embedded->setCurrentModeId(embedded->preferredModeId());
        embedded->setPrimary(true);
        embedded->setEnabled(true);

        QSize size = embedded->currentMode()->size();
        external->setPos(QPoint(size.width(), 0));
        external->setEnabled(true);
        external->setCurrentModeId(external->preferredModeId());
        external->setPrimary(false);

        return config;
    }

    return config;
}

void Generator::singleOutput(KScreen::OutputList& outputs)
{
    Q_ASSERT(!outputs.isEmpty());

    KScreen::Output* output = outputs.take(outputs.keys().first());
    Q_ASSERT(output);

    output->setCurrentModeId(output->preferredModeId());
    output->setEnabled(true);
    output->setPrimary(true);
    output->setPos(QPoint(0,0));
}

void Generator::laptop(KScreen::OutputList& outputs)
{
    Q_ASSERT(!outputs.isEmpty());

    KDebug::Block laptopBlock("Laptop config");

    KScreen::Output* embedded = embeddedOutput(outputs);
    Q_ASSERT(embedded);

    outputs.remove(embedded->id());

    if (outputs.isEmpty()) {
        kWarning() << "No external outputs found, going for singleOutput()";
        outputs.insert(embedded->id(), embedded);
        return singleOutput(outputs);
    }

    if (isLidClosed() && outputs.count() == 1) {
        kDebug() << "With lid closed";
        embedded->setEnabled(false);
        embedded->setPrimary(false);

        KScreen::Output* external = outputs.value(outputs.keys().first());
        external->setEnabled(true);
        external->setPrimary(true);
        external->setCurrentModeId(external->preferredModeId());
        external->setPos(QPoint(0, 0));

        return;
    }

    if (isLidClosed() && outputs.count() > 1) {
        kDebug() << "Lid is closed, and more than one output";
        embedded->setEnabled(false);
        embedded->setPrimary(false);

        extendToRight(outputs);
        return;
    }

    kDebug() << "Lid is open";
    //If lid is open, laptop screen shuold be primary
    embedded->setPos(QPoint(0,0));
    embedded->setCurrentModeId(embedded->preferredModeId());
    embedded->setPrimary(true);
    embedded->setEnabled(true);

    int globalWidth;
    if (embedded->isHorizontal()) {
        globalWidth = embedded->preferredMode()->size().width();
    } else {
        globalWidth = embedded->preferredMode()->size().height();
    }
    KScreen::Output* biggest = biggestOutput(outputs);
    outputs.remove(biggest->id());

    biggest->setPos(QPoint(globalWidth, 0));
    biggest->setEnabled(true);
    biggest->setCurrentModeId(biggest->preferredModeId());
    biggest->setPrimary(false);

    if (biggest->isHorizontal()) {
        globalWidth += biggest->currentMode()->size().width();
    } else {
        globalWidth += biggest->currentMode()->size().height();
    }
    Q_FOREACH(KScreen::Output* output, outputs) {
        output->setEnabled(true);
        output->setCurrentModeId(output->preferredModeId());
        output->setPos(QPoint(globalWidth, 0));
        output->setPrimary(false);

        if (output->isHorizontal()) {
            globalWidth += output->currentMode()->size().width();
        } else {
            globalWidth += output->currentMode()->size().height();
        }
    }

    if (isDocked()) {
        kDebug() << "Docked";
        embedded->setPrimary(false);
        biggest->setPrimary(true);
    }
}

void Generator::extendToRight(KScreen::OutputList& outputs)
{
    Q_ASSERT(!outputs.isEmpty());

    kDebug() << "Extending to the right";
    KScreen::Output* biggest = biggestOutput(outputs);
    Q_ASSERT(biggest);

    outputs.remove(biggest->id());

    biggest->setEnabled(true);
    biggest->setPrimary(true);
    biggest->setCurrentModeId(biggest->preferredModeId());
    biggest->setPos(QPoint(0,0));

    int globalWidth;
    if (biggest->isHorizontal()) {
        globalWidth = biggest->currentMode()->size().width();
    } else {
        globalWidth = biggest->currentMode()->size().height();
    }

    Q_FOREACH(KScreen::Output* output, outputs) {
        output->setEnabled(true);
        output->setPrimary(false);
        output->setCurrentModeId(output->preferredModeId());
        output->setPos(QPoint(globalWidth, 0));

        if (output->isHorizontal()) {
            globalWidth += output->currentMode()->size().width();
        } else {
            globalWidth += output->currentMode()->size().height();
        }
    }
}

KScreen::Mode* Generator::biggestMode(const KScreen::ModeList& modes)
{
    int area, total = 0;
    KScreen::Mode* biggest = 0;
    Q_FOREACH(KScreen::Mode* mode, modes) {
        area = mode->size().width() * mode->size().height();
        if (area < total) {
            continue;
        }
        if (area == total && mode->refreshRate() < biggest->refreshRate()) {
            continue;
        }
        if (area == total && mode->refreshRate() > biggest->refreshRate()) {
            biggest = mode;
            continue;
        }

        total = area;
        biggest = mode;
    }

    return biggest;
}

KScreen::Output* Generator::biggestOutput(const KScreen::OutputList &outputs)
{
    int area, total = 0;
    KScreen::Output* biggest = 0;
    Q_FOREACH(KScreen::Output* output, outputs) {
        KScreen::Mode* mode = output->preferredMode();
        area = mode->size().width() * mode->size().height();
        if (area < total) {
            continue;
        }

        total = area;
        biggest = output;
    }

    return biggest;
}

void Generator::disableAllDisconnectedOutputs(const KScreen::OutputList& outputs)
{
    KDebug::Block disableBlock("Disabling disconnected screens");
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!output->isConnected()) {
            kDebug() << output->name() << " Disabled";
            output->setEnabled(false);
            output->setPrimary(false);
        }
    }
}

KScreen::Output* Generator::embeddedOutput(const KScreen::OutputList& outputs)
{
    Q_FOREACH(KScreen::Output* output, outputs) {
        if (!isEmbedded(output->name())) {
            continue;
        }

        return output;
    }

    return 0;
}

bool Generator::isLaptop()
{
    if (m_forceLaptop) {
        return true;
    }

    return Device::self()->isLaptop();
}

bool Generator::isLidClosed()
{
    if (m_forceLidClosed) {
        return true;
    }

    return Device::self()->isLidClosed();
}

bool Generator::isDocked()
{
    if (m_forceDocked) {
        return true;
    }

    return Device::self()->isDocked();
}

bool Generator::isEmbedded(const QString &name)
{
    QStringList embedded;
    embedded << "LVDS";
    embedded << "IDP";
    embedded << "EDP";

    Q_FOREACH(const QString &pre, embedded) {
        if (name.toUpper().startsWith(pre)) {
            return true;
        }
    }

    return false;
}

void Generator::setForceLaptop(bool force)
{
    m_forceLaptop = force;
}

void Generator::setForceLidClosed(bool force)
{
    m_forceLidClosed = force;
}

void Generator::setForceDocked(bool force)
{
    m_forceDocked = force;
}
<|endoftext|>