<commit_before>/*
 * DISTRHO Plugin Framework (DPF)
 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any purpose with
 * or without fee is hereby granted, provided that the above copyright notice and this
 * permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

#include "../DistrhoUI.hpp"
#include "../../dgl/Application.hpp"

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../../dgl/src/WindowPrivateData.hpp"
# include "../../dgl/src/pugl.hpp"
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_UI_IS_STANDALONE 1
#else
# define DISTRHO_UI_IS_STANDALONE 0
#endif

#if defined(DISTRHO_PLUGIN_TARGET_VST2)
# undef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
#endif

// -----------------------------------------------------------------------

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

class PluginApplication : public Application
{
public:
    explicit PluginApplication()
        : Application(DISTRHO_UI_IS_STANDALONE)
    {
        const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
            DISTRHO_PLUGIN_BRAND
#else
            DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
            "-" DISTRHO_PLUGIN_NAME
        );
        setClassName(className);
    }

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};

// -----------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO external ui stuff
class PluginWindow
{
    DISTRHO_NAMESPACE::UI* const ui;

public:
    explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr,
                          PluginApplication& app,
                          const uintptr_t parentWindowHandle,
                          const uint width,
                          const uint height,
                          const double scaleFactor)
        : Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
          ui(uiPtr) {}

    uint getWidth() const noexcept
    {
        return ui->getWidth();
    }

    uint getHeight() const noexcept
    {
        return ui->getHeight();
    }

    bool isVisible() const noexcept
    {
        return ui->isRunning();
    }

    uintptr_t getNativeWindowHandle() const noexcept
    {
        return 0;
    }

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#else
class PluginWindow : public Window
{
    DISTRHO_NAMESPACE::UI* const ui;

public:
    explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr,
                          PluginApplication& app,
                          const uintptr_t parentWindowHandle,
                          const uint width,
                          const uint height,
                          const double scaleFactor)
        : Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
          ui(uiPtr)
    {
        puglBackendEnter(pData->view);
    }

    void leaveContext()
    {
        puglBackendLeave(pData->view);
    }

protected:
    void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override
    {
        DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

        ui->uiFocus(focus, mode);
    }

    void onReshape(const uint width, const uint height) override
    {
        DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

        ui->uiReshape(width, height);
    }

    void onScaleFactorChanged(const double scaleFactor) override
    {
        DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

        ui->uiScaleFactorChanged(scaleFactor);
    }

# ifndef DGL_FILE_BROWSER_DISABLED
    void onFileSelected(const char* filename) override;
# endif

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

END_NAMESPACE_DGL

// -----------------------------------------------------------------------

START_NAMESPACE_DISTRHO

using DGL_NAMESPACE::PluginApplication;
using DGL_NAMESPACE::PluginWindow;

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc)   (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc)    (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc)    (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc)    (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc)     (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
    // DGL
    PluginApplication app;
    ScopedPointer<PluginWindow> window;

    // DSP
    double   sampleRate;
    uint32_t parameterOffset;
    void*    dspPtr;

    // UI
    uint bgColor;
    uint fgColor;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    double scaleFactor;
    uintptr_t winId;
# ifndef DGL_FILE_BROWSER_DISABLED
    char* uiStateFileKeyRequest;
# endif
#endif

    // Callbacks
    void*           callbacksPtr;
    editParamFunc   editParamCallbackFunc;
    setParamFunc    setParamCallbackFunc;
    setStateFunc    setStateCallbackFunc;
    sendNoteFunc    sendNoteCallbackFunc;
    setSizeFunc     setSizeCallbackFunc;
    fileRequestFunc fileRequestCallbackFunc;

    PrivateData() noexcept
        : app(),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
          window(nullptr),
#endif
          sampleRate(0),
          parameterOffset(0),
          dspPtr(nullptr),
          bgColor(0),
          fgColor(0xffffffff),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
          scaleFactor(1.0),
          winId(0),
# ifndef DGL_FILE_BROWSER_DISABLED
          uiStateFileKeyRequest(nullptr),
# endif
#endif
          callbacksPtr(nullptr),
          editParamCallbackFunc(nullptr),
          setParamCallbackFunc(nullptr),
          setStateCallbackFunc(nullptr),
          sendNoteCallbackFunc(nullptr),
          setSizeCallbackFunc(nullptr),
          fileRequestCallbackFunc(nullptr)
    {
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
        parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
        parameterOffset += 1;
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
        parameterOffset += 1;
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
        parameterOffset += 1;
# endif
#endif
    }

    ~PrivateData() noexcept
    {
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
        std::free(uiStateFileKeyRequest);
#endif
    }

    void editParamCallback(const uint32_t rindex, const bool started)
    {
        if (editParamCallbackFunc != nullptr)
            editParamCallbackFunc(callbacksPtr, rindex, started);
    }

    void setParamCallback(const uint32_t rindex, const float value)
    {
        if (setParamCallbackFunc != nullptr)
            setParamCallbackFunc(callbacksPtr, rindex, value);
    }

    void setStateCallback(const char* const key, const char* const value)
    {
        if (setStateCallbackFunc != nullptr)
            setStateCallbackFunc(callbacksPtr, key, value);
    }

    void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
    {
        if (sendNoteCallbackFunc != nullptr)
            sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
    }

    void setSizeCallback(const uint width, const uint height)
    {
        if (setSizeCallbackFunc != nullptr)
            setSizeCallbackFunc(callbacksPtr, width, height);
    }

    // implemented below, after PluginWindow
    bool fileRequestCallback(const char* const key);

    static UI::PrivateData* s_nextPrivateData;
    static PluginWindow& createNextWindow(UI* ui, uint width, uint height);
};

// -----------------------------------------------------------------------
// UI private data fileRequestCallback, which requires PluginWindow definitions

inline bool UI::PrivateData::fileRequestCallback(const char* const key)
{
    if (fileRequestCallbackFunc != nullptr)
        return fileRequestCallbackFunc(callbacksPtr, key);

#if DISTRHO_PLUGIN_WANT_STATEFILES && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
    std::free(uiStateFileKeyRequest);
    uiStateFileKeyRequest = strdup(key);
    DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false);

    char title[0xff];
    snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key);
    title[sizeof(title)-1u] = '\0';

    DGL_NAMESPACE::Window::FileBrowserOptions opts;
    opts.title = title;
    return window->openFileBrowser(opts);
#endif

    return false;
}

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// PluginWindow onFileSelected that require UI::PrivateData definitions

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
START_NAMESPACE_DGL

inline void PluginWindow::onFileSelected(const char* const filename)
{
    DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

# if DISTRHO_PLUGIN_WANT_STATEFILES
    if (char* const key = ui->uiData->uiStateFileKeyRequest)
    {
        ui->uiData->uiStateFileKeyRequest = nullptr;
        // notify DSP
        ui->setState(key, filename != nullptr ? filename : "");
        // notify UI
        ui->stateChanged(key, filename != nullptr ? filename : "");
        std::free(key);
        return;
    }
# endif

    ui->uiFileBrowserSelected(filename);
}

END_NAMESPACE_DGL
#endif

// -----------------------------------------------------------------------

#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
<commit_msg>Corrections to d85add3a4c3434b51b822b416cfb963c6f878037<commit_after>/*
 * DISTRHO Plugin Framework (DPF)
 * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any purpose with
 * or without fee is hereby granted, provided that the above copyright notice and this
 * permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
#define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED

#include "../DistrhoUI.hpp"
#include "../../dgl/Application.hpp"

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../../dgl/src/WindowPrivateData.hpp"
# include "../../dgl/src/pugl.hpp"
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_UI_IS_STANDALONE 1
#else
# define DISTRHO_UI_IS_STANDALONE 0
#endif

#if defined(DISTRHO_PLUGIN_TARGET_VST2)
# undef DISTRHO_UI_USER_RESIZABLE
# define DISTRHO_UI_USER_RESIZABLE 0
#endif

// -----------------------------------------------------------------------

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

class PluginApplication : public Application
{
public:
    explicit PluginApplication()
        : Application(DISTRHO_UI_IS_STANDALONE)
    {
        const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
            DISTRHO_PLUGIN_BRAND
#else
            DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
            "-" DISTRHO_PLUGIN_NAME
        );
        setClassName(className);
    }

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};

// -----------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO external ui stuff
class PluginWindow
{
    DISTRHO_NAMESPACE::UI* const ui;

public:
    explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr,
                          PluginApplication& app,
                          const uintptr_t parentWindowHandle,
                          const uint width,
                          const uint height,
                          const double scaleFactor)
        : Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
          ui(uiPtr) {}

    uint getWidth() const noexcept
    {
        return ui->getWidth();
    }

    uint getHeight() const noexcept
    {
        return ui->getHeight();
    }

    bool isVisible() const noexcept
    {
        return ui->isRunning();
    }

    uintptr_t getNativeWindowHandle() const noexcept
    {
        return 0;
    }

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#else
class PluginWindow : public Window
{
    DISTRHO_NAMESPACE::UI* const ui;

public:
    explicit PluginWindow(DISTRHO_NAMESPACE::UI* const uiPtr,
                          PluginApplication& app,
                          const uintptr_t parentWindowHandle,
                          const uint width,
                          const uint height,
                          const double scaleFactor)
        : Window(app, parentWindowHandle, width, height, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
          ui(uiPtr)
    {
        puglBackendEnter(pData->view);
    }

    void leaveContext()
    {
        puglBackendLeave(pData->view);
    }

protected:
    void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override
    {
        DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

        ui->uiFocus(focus, mode);
    }

    void onReshape(const uint width, const uint height) override
    {
        DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

        ui->uiReshape(width, height);
    }

    void onScaleFactorChanged(const double scaleFactor) override
    {
        DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

        ui->uiScaleFactorChanged(scaleFactor);
    }

# ifndef DGL_FILE_BROWSER_DISABLED
    void onFileSelected(const char* filename) override;
# endif

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

END_NAMESPACE_DGL

// -----------------------------------------------------------------------

START_NAMESPACE_DISTRHO

using DGL_NAMESPACE::PluginApplication;
using DGL_NAMESPACE::PluginWindow;

// -----------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc)   (void* ptr, uint32_t rindex, bool started);
typedef void (*setParamFunc)    (void* ptr, uint32_t rindex, float value);
typedef void (*setStateFunc)    (void* ptr, const char* key, const char* value);
typedef void (*sendNoteFunc)    (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
typedef void (*setSizeFunc)     (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
    // DGL
    PluginApplication app;
    ScopedPointer<PluginWindow> window;

    // DSP
    double   sampleRate;
    uint32_t parameterOffset;
    void*    dspPtr;

    // UI
    uint bgColor;
    uint fgColor;
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
    double scaleFactor;
    uintptr_t winId;
# ifndef DGL_FILE_BROWSER_DISABLED
    char* uiStateFileKeyRequest;
# endif
#endif

    // Callbacks
    void*           callbacksPtr;
    editParamFunc   editParamCallbackFunc;
    setParamFunc    setParamCallbackFunc;
    setStateFunc    setStateCallbackFunc;
    sendNoteFunc    sendNoteCallbackFunc;
    setSizeFunc     setSizeCallbackFunc;
    fileRequestFunc fileRequestCallbackFunc;

    PrivateData() noexcept
        : app(),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
          window(nullptr),
#endif
          sampleRate(0),
          parameterOffset(0),
          dspPtr(nullptr),
          bgColor(0),
          fgColor(0xffffffff),
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
          scaleFactor(1.0),
          winId(0),
# ifndef DGL_FILE_BROWSER_DISABLED
          uiStateFileKeyRequest(nullptr),
# endif
#endif
          callbacksPtr(nullptr),
          editParamCallbackFunc(nullptr),
          setParamCallbackFunc(nullptr),
          setStateCallbackFunc(nullptr),
          sendNoteCallbackFunc(nullptr),
          setSizeCallbackFunc(nullptr),
          fileRequestCallbackFunc(nullptr)
    {
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
        parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
        parameterOffset += 1;
# endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
        parameterOffset += 1;
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
        parameterOffset += 1;
# endif
#endif
    }

    ~PrivateData() noexcept
    {
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
        std::free(uiStateFileKeyRequest);
#endif
    }

    void editParamCallback(const uint32_t rindex, const bool started)
    {
        if (editParamCallbackFunc != nullptr)
            editParamCallbackFunc(callbacksPtr, rindex, started);
    }

    void setParamCallback(const uint32_t rindex, const float value)
    {
        if (setParamCallbackFunc != nullptr)
            setParamCallbackFunc(callbacksPtr, rindex, value);
    }

    void setStateCallback(const char* const key, const char* const value)
    {
        if (setStateCallbackFunc != nullptr)
            setStateCallbackFunc(callbacksPtr, key, value);
    }

    void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
    {
        if (sendNoteCallbackFunc != nullptr)
            sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
    }

    void setSizeCallback(const uint width, const uint height)
    {
        if (setSizeCallbackFunc != nullptr)
            setSizeCallbackFunc(callbacksPtr, width, height);
    }

    // implemented below, after PluginWindow
    bool fileRequestCallback(const char* const key);

    static UI::PrivateData* s_nextPrivateData;
    static PluginWindow& createNextWindow(UI* ui, uint width, uint height);
};

// -----------------------------------------------------------------------
// UI private data fileRequestCallback, which requires PluginWindow definitions

inline bool UI::PrivateData::fileRequestCallback(const char* const key)
{
    if (fileRequestCallbackFunc != nullptr)
        return fileRequestCallbackFunc(callbacksPtr, key);

#if DISTRHO_PLUGIN_WANT_STATEFILES && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
    std::free(uiStateFileKeyRequest);
    uiStateFileKeyRequest = strdup(key);
    DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false);

    char title[0xff];
    snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key);
    title[sizeof(title)-1u] = '\0';

    DGL_NAMESPACE::Window::FileBrowserOptions opts;
    opts.title = title;
    return window->openFileBrowser(opts);
#endif

    return false;
}

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// PluginWindow onFileSelected that require UI::PrivateData definitions

#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED)
START_NAMESPACE_DGL

inline void PluginWindow::onFileSelected(const char* const filename)
{
    DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);

# if DISTRHO_PLUGIN_WANT_STATEFILES
    if (char* const key = ui->uiData->uiStateFileKeyRequest)
    {
        ui->uiData->uiStateFileKeyRequest = nullptr;
        if (filename != nullptr)
        {
            // notify DSP
            ui->setState(key, filename);
            // notify UI
            ui->stateChanged(key, filename);
        }
        std::free(key);
        return;
    }
# endif

    ui->uiFileBrowserSelected(filename);
}

END_NAMESPACE_DGL
#endif

// -----------------------------------------------------------------------

#endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
<|endoftext|>


<commit_before>// Copyright (C) 2018 Elviss Strazdins
// This file is part of the Ouzel engine.

#pragma once

#include <cstdint>
#include <set>
#include <string>
#include <vector>
#include "graphics/DataType.hpp"
#include "graphics/Vertex.hpp"

namespace ouzel
{
    namespace graphics
    {
        class ShaderResource;

        class Shader final
        {
        public:
            struct ConstantInfo
            {
                ConstantInfo(const std::string& aName, DataType aDataType):
                    name(aName), dataType(aDataType), size(getDataTypeSize(aDataType)) {}

                std::string name;
                DataType dataType;
                uint32_t size;
            };

            Shader();
            virtual ~Shader();

            Shader(const Shader&) = delete;
            Shader& operator=(const Shader&) = delete;

            Shader(Shader&&) = delete;
            Shader& operator=(Shader&&) = delete;

            bool init(const std::vector<uint8_t>& newPixelShader,
                      const std::vector<uint8_t>& newVertexShader,
                      const std::set<Vertex::Attribute::Usage>& newVertexAttributes,
                      const std::vector<ConstantInfo>& newPixelShaderConstantInfo,
                      const std::vector<ConstantInfo>& newVertexShaderConstantInfo,
                      uint32_t newPixelShaderDataAlignment = 0,
                      uint32_t newVertexShaderDataAlignment = 0,
                      const std::string& pixelShaderFunction = "",
                      const std::string& vertexShaderFunction = "");
            bool init(const std::string& newPixelShader,
                      const std::string& newVertexShader,
                      const std::set<Vertex::Attribute::Usage>& newVertexAttributes,
                      const std::vector<ConstantInfo>& newPixelShaderConstantInfo,
                      const std::vector<ConstantInfo>& newVertexShaderConstantInfo,
                      uint32_t newPixelShaderDataAlignment = 0,
                      uint32_t newVertexShaderDataAlignment = 0,
                      const std::string& newPixelShaderFunction = "",
                      const std::string& newVertexShaderFunction = "");

            inline ShaderResource* getResource() const { return resource; }

            const std::set<Vertex::Attribute::Usage>& getVertexAttributes() const;

        private:
            ShaderResource* resource = nullptr;

            std::set<Vertex::Attribute::Usage> vertexAttributes;

            std::string pixelShaderFilename;
            std::string vertexShaderFilename;
        };
    } // namespace graphics
} // namespace ouzel
<commit_msg>Promote the ConstantInfo struct to a class<commit_after>// Copyright (C) 2018 Elviss Strazdins
// This file is part of the Ouzel engine.

#pragma once

#include <cstdint>
#include <set>
#include <string>
#include <vector>
#include "graphics/DataType.hpp"
#include "graphics/Vertex.hpp"

namespace ouzel
{
    namespace graphics
    {
        class ShaderResource;

        class Shader final
        {
        public:
            class ConstantInfo
            {
            public:
                ConstantInfo(const std::string& aName, DataType aDataType):
                    name(aName), dataType(aDataType), size(getDataTypeSize(aDataType)) {}

                std::string name;
                DataType dataType;
                uint32_t size;
            };

            Shader();
            virtual ~Shader();

            Shader(const Shader&) = delete;
            Shader& operator=(const Shader&) = delete;

            Shader(Shader&&) = delete;
            Shader& operator=(Shader&&) = delete;

            bool init(const std::vector<uint8_t>& newPixelShader,
                      const std::vector<uint8_t>& newVertexShader,
                      const std::set<Vertex::Attribute::Usage>& newVertexAttributes,
                      const std::vector<ConstantInfo>& newPixelShaderConstantInfo,
                      const std::vector<ConstantInfo>& newVertexShaderConstantInfo,
                      uint32_t newPixelShaderDataAlignment = 0,
                      uint32_t newVertexShaderDataAlignment = 0,
                      const std::string& pixelShaderFunction = "",
                      const std::string& vertexShaderFunction = "");
            bool init(const std::string& newPixelShader,
                      const std::string& newVertexShader,
                      const std::set<Vertex::Attribute::Usage>& newVertexAttributes,
                      const std::vector<ConstantInfo>& newPixelShaderConstantInfo,
                      const std::vector<ConstantInfo>& newVertexShaderConstantInfo,
                      uint32_t newPixelShaderDataAlignment = 0,
                      uint32_t newVertexShaderDataAlignment = 0,
                      const std::string& newPixelShaderFunction = "",
                      const std::string& newVertexShaderFunction = "");

            inline ShaderResource* getResource() const { return resource; }

            const std::set<Vertex::Attribute::Usage>& getVertexAttributes() const;

        private:
            ShaderResource* resource = nullptr;

            std::set<Vertex::Attribute::Usage> vertexAttributes;

            std::string pixelShaderFilename;
            std::string vertexShaderFilename;
        };
    } // namespace graphics
} // namespace ouzel
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2011.  All rights reserved
 */
#ifndef	_Stroia_Foundation_Execution_Exceptions_inl_
#define	_Stroia_Foundation_Execution_Exceptions_inl_	1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
namespace	Stroika {
	namespace	Foundation {
		namespace	Execution {

		#if		qPlatform_Windows
		//	class	Win32ErrorException
			inline	Win32ErrorException::Win32ErrorException (DWORD error):
				fError (error)
				{
				}
			inline	Win32ErrorException::operator DWORD () const
				{
					return fError;
				}
			inline	TString	Win32ErrorException::LookupMessage () const
				{
					return LookupMessage (fError);
				}
		#endif

		//	class	Win32StructuredException
		#if		qPlatform_Windows
			inline	Win32StructuredException::Win32StructuredException (unsigned int seCode):
				fSECode (seCode)
				{
				}
			inline	Win32StructuredException::operator unsigned int () const
				{
					return fSECode;
				}
			inline	TString	Win32StructuredException::LookupMessage () const
				{
					return LookupMessage (fSECode);
				}
		#endif

		//	class	HRESULTErrorException
		#if		qPlatform_Windows
			inline	HRESULTErrorException::HRESULTErrorException (HRESULT hresult):
				fHResult (hresult)
				{
				}
			inline	HRESULTErrorException::operator HRESULT () const
				{
					return fHResult;
				}
			inline	TString	HRESULTErrorException::LookupMessage () const
				{
					return LookupMessage (fHResult);
				}
		#endif

		//	class	errno_ErrorException
		#if		qPlatform_Windows
			inline	errno_ErrorException::operator errno_t () const
				{
					return fError;
				}
			inline	TString	errno_ErrorException::LookupMessage () const
				{
					return LookupMessage (fError);
				}
		#endif

			template	<typename T>
				inline		void	 DoThrow (const T& e2Throw)
					{
						DbgTrace ("Throwing exception: %s", typeid (T).name ());
						throw e2Throw;
					}
			template	<typename T>
				inline	void		DoThrow (const T& e2Throw, const char* traceMsg)
					{
						DbgTrace ("%s", traceMsg);
						throw e2Throw;
					}
			template	<typename T>
				inline	void		DoThrow (const T& e2Throw, const wchar_t* traceMsg)
					{
						DbgTrace (L"%s", traceMsg);
						throw e2Throw;
					}
			inline	void	DoReThrow ()
				{
					DbgTrace ("DoReThrow");
					throw;
				}
			inline	void	DoReThrow (const char* traceMsg)
				{
					DbgTrace ("DoReThrow: %s", traceMsg);
					throw;
				}
			inline	void	DoReThrow (const wchar_t* traceMsg)
				{
					DbgTrace (L"DoReThrow: %s", traceMsg);
					throw;
				}

			#if		qPlatform_Windows
			template	<>
				inline	void	DoThrow (const Win32ErrorException& e2Throw)
					{
						DbgTrace ("Throwing Win32ErrorException: DWORD = 0x%x", static_cast<DWORD> (e2Throw));
						throw e2Throw;
					}
			#endif
			template	<>
				inline	void	DoThrow (const StringException& e2Throw)
					{
						DbgTrace (L"Throwing StringException: '%s'", static_cast<wstring> (e2Throw).substr (0, 20).c_str ());
						throw e2Throw;
					}
		#if		qPlatform_Windows
			template	<>
				inline	void	DoThrow (const Win32StructuredException& e2Throw)
					{
						DbgTrace ("Throwing Win32StructuredException: fSECode = 0x%x", static_cast<int> (e2Throw));
						throw e2Throw;
					}
		#endif
		#if		qPlatform_Windows
			template	<>
				inline	void	DoThrow (const HRESULTErrorException& e2Throw)
					{
						DbgTrace ("Throwing HRESULTErrorException: HRESULT = 0x%x", static_cast<HRESULT> (e2Throw));
						throw e2Throw;
					}
		#endif
			template	<>
				inline	void	DoThrow (const IO::FileFormatException& e2Throw)
					{
						DbgTrace (_T ("Throwing FileFormatException: fFileName = '%s'"), e2Throw.fFileName.c_str ());
						throw e2Throw;
					}
			template	<>
				inline	void	DoThrow (const SilentException& e2Throw)
					{
						DbgTrace (_T ("Throwing SilentException"));
						throw e2Throw;
					}
			template	<>
				inline	void	DoThrow (const UserCanceledException& e2Throw)
					{
						DbgTrace (_T ("Throwing UserCanceledException"));
						throw e2Throw;
					}
			template	<>
				inline	void	DoThrow (const IO::FileBusyException& e2Throw)
					{
						DbgTrace (_T ("Throwing FileBusyException: fFileName = '%s'"), e2Throw.fFileName.c_str ());
						throw e2Throw;
					}

		#if		qPlatform_Windows
			inline	void	ThrowIfFalseGetLastError (bool test)
				{
					if (not test) {
						Win32ErrorException::DoThrow (::GetLastError ());
					}
				}
		#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfFalseGetLastError (BOOL test)
				{
					if (not test) {
						Win32ErrorException::DoThrow (::GetLastError ());
					}
				}
			#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfNotERROR_SUCCESS (DWORD win32ErrCode)
				{
					if (win32ErrCode != ERROR_SUCCESS) {
						Win32ErrorException::DoThrow (win32ErrCode);
					}
				}
			#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfErrorHRESULT (HRESULT hr)
				{
					if (not SUCCEEDED (hr)) {
						DoThrow (HRESULTErrorException (hr));
					}
				}
			#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfError_errno_t (errno_t e)
				{
					if (e != 0) {
						errno_ErrorException::DoThrow (e);
					}
				}
			#endif
			inline	void	ThrowIfNull (const void* p)
				{
					if (p == NULL) {
						DoThrow (bad_alloc (), _T ("ThrowIfNull (NULL) - throwing bad_alloc"));
					}
				}
			template	<typename E>
				inline	void	ThrowIfNull (const void* p, const E& e)
					{
						if (p == NULL) {
							DoThrow (e, _T ("ThrowIfNull (NULL,X) - throwing X"));
						}
					}
			#if		qPlatform_Windows
			template<>
				inline	void	ThrowIfNull<HRESULT> (const void* p, const HRESULT& hr)
					{
						ThrowIfNull (p, HRESULTErrorException (hr));
					}
			#endif
		}
	}
}

#endif	/*_Stroia_Foundation_Execution_Exceptions_inl_*/
<commit_msg>_NoReturn_ stuff needed on DEFINITIONS as well as DECLARATIONS for DoThrow/DoRethrow() functions (at least on MSVC - still not tested UNIX)<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2011.  All rights reserved
 */
#ifndef	_Stroia_Foundation_Execution_Exceptions_inl_
#define	_Stroia_Foundation_Execution_Exceptions_inl_	1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
namespace	Stroika {
	namespace	Foundation {
		namespace	Execution {

		#if		qPlatform_Windows
		//	class	Win32ErrorException
			inline	Win32ErrorException::Win32ErrorException (DWORD error):
				fError (error)
				{
				}
			inline	Win32ErrorException::operator DWORD () const
				{
					return fError;
				}
			inline	TString	Win32ErrorException::LookupMessage () const
				{
					return LookupMessage (fError);
				}
		#endif

		//	class	Win32StructuredException
		#if		qPlatform_Windows
			inline	Win32StructuredException::Win32StructuredException (unsigned int seCode):
				fSECode (seCode)
				{
				}
			inline	Win32StructuredException::operator unsigned int () const
				{
					return fSECode;
				}
			inline	TString	Win32StructuredException::LookupMessage () const
				{
					return LookupMessage (fSECode);
				}
		#endif

		//	class	HRESULTErrorException
		#if		qPlatform_Windows
			inline	HRESULTErrorException::HRESULTErrorException (HRESULT hresult):
				fHResult (hresult)
				{
				}
			inline	HRESULTErrorException::operator HRESULT () const
				{
					return fHResult;
				}
			inline	TString	HRESULTErrorException::LookupMessage () const
				{
					return LookupMessage (fHResult);
				}
		#endif

		//	class	errno_ErrorException
		#if		qPlatform_Windows
			inline	errno_ErrorException::operator errno_t () const
				{
					return fError;
				}
			inline	TString	errno_ErrorException::LookupMessage () const
				{
					return LookupMessage (fError);
				}
		#endif

			template	<typename T>
				inline		void	 _NoReturn_	DoThrow (const T& e2Throw)
					{
						DbgTrace ("Throwing exception: %s", typeid (T).name ());
						throw e2Throw;
					}
			template	<typename T>
				inline	void		_NoReturn_	DoThrow (const T& e2Throw, const char* traceMsg)
					{
						DbgTrace ("%s", traceMsg);
						throw e2Throw;
					}
			template	<typename T>
				inline	void		_NoReturn_	DoThrow (const T& e2Throw, const wchar_t* traceMsg)
					{
						DbgTrace (L"%s", traceMsg);
						throw e2Throw;
					}
			inline	void	_NoReturn_	DoReThrow ()
				{
					DbgTrace ("DoReThrow");
					throw;
				}
			inline	void	_NoReturn_	DoReThrow (const char* traceMsg)
				{
					DbgTrace ("DoReThrow: %s", traceMsg);
					throw;
				}
			inline	void	_NoReturn_	DoReThrow (const wchar_t* traceMsg)
				{
					DbgTrace (L"DoReThrow: %s", traceMsg);
					throw;
				}

			#if		qPlatform_Windows
			template	<>
				inline	void	_NoReturn_	DoThrow (const Win32ErrorException& e2Throw)
					{
						DbgTrace ("Throwing Win32ErrorException: DWORD = 0x%x", static_cast<DWORD> (e2Throw));
						throw e2Throw;
					}
			#endif
			template	<>
				inline	void	_NoReturn_	DoThrow (const StringException& e2Throw)
					{
						DbgTrace (L"Throwing StringException: '%s'", static_cast<wstring> (e2Throw).substr (0, 20).c_str ());
						throw e2Throw;
					}
		#if		qPlatform_Windows
			template	<>
				inline	void	_NoReturn_	DoThrow (const Win32StructuredException& e2Throw)
					{
						DbgTrace ("Throwing Win32StructuredException: fSECode = 0x%x", static_cast<int> (e2Throw));
						throw e2Throw;
					}
		#endif
		#if		qPlatform_Windows
			template	<>
				inline	void	_NoReturn_	DoThrow (const HRESULTErrorException& e2Throw)
					{
						DbgTrace ("Throwing HRESULTErrorException: HRESULT = 0x%x", static_cast<HRESULT> (e2Throw));
						throw e2Throw;
					}
		#endif
			template	<>
				inline	void	_NoReturn_	DoThrow (const IO::FileFormatException& e2Throw)
					{
						DbgTrace (_T ("Throwing FileFormatException: fFileName = '%s'"), e2Throw.fFileName.c_str ());
						throw e2Throw;
					}
			template	<>
				inline	void	_NoReturn_	DoThrow (const SilentException& e2Throw)
					{
						DbgTrace (_T ("Throwing SilentException"));
						throw e2Throw;
					}
			template	<>
				inline	void	_NoReturn_	DoThrow (const UserCanceledException& e2Throw)
					{
						DbgTrace (_T ("Throwing UserCanceledException"));
						throw e2Throw;
					}
			template	<>
				inline	void	_NoReturn_	DoThrow (const IO::FileBusyException& e2Throw)
					{
						DbgTrace (_T ("Throwing FileBusyException: fFileName = '%s'"), e2Throw.fFileName.c_str ());
						throw e2Throw;
					}

		#if		qPlatform_Windows
			inline	void	ThrowIfFalseGetLastError (bool test)
				{
					if (not test) {
						Win32ErrorException::DoThrow (::GetLastError ());
					}
				}
		#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfFalseGetLastError (BOOL test)
				{
					if (not test) {
						Win32ErrorException::DoThrow (::GetLastError ());
					}
				}
			#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfNotERROR_SUCCESS (DWORD win32ErrCode)
				{
					if (win32ErrCode != ERROR_SUCCESS) {
						Win32ErrorException::DoThrow (win32ErrCode);
					}
				}
			#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfErrorHRESULT (HRESULT hr)
				{
					if (not SUCCEEDED (hr)) {
						DoThrow (HRESULTErrorException (hr));
					}
				}
			#endif
			#if		qPlatform_Windows
			inline	void	ThrowIfError_errno_t (errno_t e)
				{
					if (e != 0) {
						errno_ErrorException::DoThrow (e);
					}
				}
			#endif
			inline	void	ThrowIfNull (const void* p)
				{
					if (p == NULL) {
						DoThrow (bad_alloc (), _T ("ThrowIfNull (NULL) - throwing bad_alloc"));
					}
				}
			template	<typename E>
				inline	void	ThrowIfNull (const void* p, const E& e)
					{
						if (p == NULL) {
							DoThrow (e, _T ("ThrowIfNull (NULL,X) - throwing X"));
						}
					}
			#if		qPlatform_Windows
			template<>
				inline	void	ThrowIfNull<HRESULT> (const void* p, const HRESULT& hr)
					{
						ThrowIfNull (p, HRESULTErrorException (hr));
					}
			#endif
		}
	}
}

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


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#include    "../Characters/Format.h"
#include    "../Characters/CString/Utilities.h"
#include    "../Characters/StringBuilder.h"
#include    "../Characters/ToString.h"
#include    "../Memory/BlockAllocated.h"

#include    "Common.h"
#include    "Sleep.h"
#include    "TimeOutException.h"

#include    "ThreadPool.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Containers;
using   namespace   Stroika::Foundation::Execution;

using   Characters::String;
using   Characters::StringBuilder;

class   ThreadPool::MyRunnable_  {
public:
    MyRunnable_ (ThreadPool& threadPool)
        : fCurTaskUpdateCritSection_ ()
        , fThreadPool_ (threadPool)
        , fCurTask_ ()
        , fNextTask_ ()
    {
    }

public:
    ThreadPool::TaskType    GetCurrentTask () const
    {
        auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
        // THIS CODE IS TOO SUBTLE - BUT BECAUSE OF HOW THIS IS CALLED - fNextTask_ will NEVER be in the middle of being updated during this code - so this test is OK
        // Caller is never in the middle of doing a WaitForNextTask - and because we have this lock - we aren't updateing fCurTask_ or fNextTask_ either
        Assert (fCurTask_ == nullptr or fNextTask_ == nullptr);   // one or both must be null
        return fCurTask_ == nullptr ? fNextTask_ : fCurTask_;
    }

public:
    nonvirtual void    Run ()
    {
        // For NOW - allow ThreadAbort to just kill this thread. In the future - we may want to implement some sort of restartability

        // Keep grabbing new tasks, and running them
        while (true) {
            {
                fThreadPool_.WaitForNextTask_ (&fNextTask_);            // This will block INDEFINITELY until ThreadAbort throws out or we have a new task to run
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                Assert (fNextTask_ != nullptr);
                Assert (fCurTask_ == nullptr);
                fCurTask_ = fNextTask_;
                fNextTask_ = nullptr;
                Assert (fCurTask_ != nullptr);
                Assert (fNextTask_ == nullptr);
            }
            try {
                fCurTask_ ();
                fCurTask_ = nullptr;
            }
            catch (const Thread::AbortException&) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                throw;  // cancel this thread
            }
            catch (...) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                // other exceptions WARNING WITH DEBUG MESSAGE - but otehrwise - EAT/IGNORE
            }
        }
    }

private:
    mutable recursive_mutex     fCurTaskUpdateCritSection_;
    ThreadPool&                 fThreadPool_;
    ThreadPool::TaskType        fCurTask_;
    ThreadPool::TaskType        fNextTask_;
};

/*
 ********************************************************************************
 ****************************** Execution::ThreadPool ***************************
 ********************************************************************************
 */
ThreadPool::ThreadPool (unsigned int nThreads)
    : fCriticalSection_ ()
    , fAborted_ (false)
    , fThreads_ ()
    , fPendingTasks_ ()
    , fTasksMaybeAdded_ (WaitableEvent::eAutoReset)
{
    SetPoolSize (nThreads);
}

ThreadPool::~ThreadPool ()
{
    try {
        this->AbortAndWaitForDone ();
    }
    catch (...) {
        DbgTrace ("Ignore exception in destroying thread pool ** probably bad thing...");
    }
}

unsigned int    ThreadPool::GetPoolSize () const
{
    return static_cast<unsigned int> (fThreads_.size ());
}

void    ThreadPool::SetPoolSize (unsigned int poolSize)
{
    Debug::TraceContextBumper ctx ("ThreadPool::SetPoolSize");
    Require (not fAborted_);
    auto    critSec { make_unique_lock (fCriticalSection_) };
    while (poolSize > fThreads_.size ()) {
        fThreads_.Add (mkThread_ ());
    }

    // Still quite weak implementation of REMOVAL
    while (poolSize < fThreads_.size ()) {
        // iterate over threads if any not busy, remove that one
        bool anyFoundToKill = false;
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (ct == nullptr) {
                // since we have fCriticalSection_ - we can safely remove this thread
                fThreads_.Remove (i);
                anyFoundToKill = true;
                break;
            }
        }
        if (not anyFoundToKill) {
            // @todo - fix this better/eventually
            DbgTrace ("Failed to lower the loop size - cuz all threads busy - giving up");
            return;
        }
    }
}

ThreadPool::TaskType    ThreadPool::AddTask (const TaskType& task)
{
    //Debug::TraceContextBumper ctx ("ThreadPool::AddTask");
    Require (not fAborted_);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.push_back (task);
    }

    // Notify any waiting threads to wakeup and claim the next task
    fTasksMaybeAdded_.Set ();
    // this would be a race - if aborting and adding tasks at the same time
    Require (not fAborted_);
    return task;
}

void    ThreadPool::AbortTask (const TaskType& task, Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTask");
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                fPendingTasks_.erase (i);
                return;
            }
        }
    }

    // If we got here - its NOT in the task Q, so maybe its already running.
    //
    //

    // TODO:
    //      We walk the list of existing threads and ask each one if its (indirected - running task) is the given one and abort that task.
    //      But that requires we can RESTART an ABORTED thread (or that we remove it from the list - maybe thats better). THat COULD be OK
    //      actually since it involves on API changes and makes sense. The only slight issue is a peformace one but probably for something
    //      quite rare.
    //
    //      Anyhow SB OK for now to just not allow aborting a task which has already started....
    Thread  thread2Kill;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (task == ct) {
                thread2Kill =   i->fThread;
                fThreads_.Update (i, mkThread_ ());
                break;
            }
        }
    }
    if (thread2Kill.GetStatus () != Thread::Status::eNull) {
        thread2Kill.AbortAndWaitForDone (timeout);
    }
}

void    ThreadPool::AbortTasks (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTasks");
    auto tps = GetPoolSize ();
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            // @todo fix wrong timeout value here
            ti.fThread.AbortAndWaitForDone (timeout);
        }
        fThreads_.RemoveAll ();
    }
    // hack - not a good design or impl!!! - waste to recreate if not needed!
    SetPoolSize (tps);
}

bool    ThreadPool::IsPresent (const TaskType& task) const
{
    Require (task != nullptr);
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                return true;
            }
        }
    }
    return IsRunning (task);
}

bool    ThreadPool::IsRunning (const TaskType& task) const
{
    Require (task != nullptr);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    rTask   { tr->GetCurrentTask () };
            if (task == rTask) {
                return true;
            }
        }
    }
    return false;
}

void    ThreadPool::WaitForTask (const TaskType& task, Time::DurationSecondsType timeout) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForTask");
    // Inefficient / VERY SLOPPY impl
    using   Time::DurationSecondsType;
    DurationSecondsType   timeoutAt   =   timeout + Time::GetTickCount ();
    while (true) {
        if (not IsPresent (task)) {
            return;
        }
        DurationSecondsType   remaining   =   timeoutAt - Time::GetTickCount ();
        Execution::ThrowTimeoutExceptionAfter (timeoutAt);
        Execution::Sleep (min<DurationSecondsType> (remaining, 1.0));
    }
}

Collection<ThreadPool::TaskType>    ThreadPool::GetTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        result.AddAll (fPendingTasks_.begin (), fPendingTasks_.end ());          // copy pending tasks
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

Collection<ThreadPool::TaskType>    ThreadPool::GetRunningTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

size_t  ThreadPool::GetTasksCount () const
{
    size_t  count   =   0;
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                count++;
            }
        }
    }
    return count;
}

size_t   ThreadPool::GetPendingTasksCount () const
{
    size_t  count   =   0;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
    }
    return count;
}

void    ThreadPool::WaitForDoneUntil (Time::DurationSecondsType timeoutAt) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForDoneUntil");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Require (fAborted_);
    {
        Collection<Thread>  threadsToShutdown;      // cannot keep critical section while waiting on subthreads since they may need to acquire the critsection for whatever they are doing...
#if     qDefaultTracingOn
        Collection<String>  threadsNotAlreadyDone;  // just for DbgTrace message purposes
#endif
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            for (auto ti : fThreads_) {
                threadsToShutdown.Add (ti.fThread);
#if     qDefaultTracingOn
                if (not ti.fThread.IsDone ()) {
                    threadsNotAlreadyDone.Add (Execution::FormatThreadID (ti.fThread.GetID ()));
                }
#endif
            }
        }
        DbgTrace (L"threadsToShutdown.size = %d", threadsToShutdown.size ());
#if     qDefaultTracingOn
        DbgTrace (L"threadsNotAlreadyDone = %s", Characters::ToString (threadsNotAlreadyDone).c_str ());
#endif
        for (auto t : threadsToShutdown) {
            t.WaitForDoneUntil (timeoutAt);
        }
    }
}

void    ThreadPool::Abort ()
{
    Debug::TraceContextBumper ctx ("ThreadPool::Abort");
    Thread::SuppressInterruptionInContext suppressCtx; // must cleanly shut down each of our subthreads - even if our thread is aborting...
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    fAborted_ = true;   // No race, because fAborted never 'unset'
    // no need to set fTasksMaybeAdded_, since aborting each thread should be sufficient
    {
        // Clear the task Q and then abort each thread
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
        for (TPInfo_ && ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
}

void    ThreadPool::AbortAndWaitForDone (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper traceCtx ("ThreadPool::AbortAndWaitForDone");
    Thread::SuppressInterruptionInContext suppressCtx; // must cleanly shut down each of our subthreads - even if our thread is aborting...
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Abort ();
    WaitForDone (timeout);
}

String  ThreadPool::ToString () const
{
    auto    critSec { make_unique_lock (fCriticalSection_) };
    StringBuilder   sb;
    sb += L"{";
    sb += Characters::Format (L"pending-task-count: %d", GetPendingTasksCount ()) + L", ";
    sb += Characters::Format (L"running-task-count: %d", GetRunningTasks ().size ()) + L", ";
    sb += Characters::Format (L"pool-thread-count: %d", fThreads_.size ()) + L", ";
    sb += L"}";
    return sb.str ();
}

// THIS is called NOT from 'this' - but from the context of an OWNED thread of the pool
void    ThreadPool::WaitForNextTask_ (TaskType* result)
{
    RequireNotNull (result);
    if (fAborted_) {
        Execution::Throw (Thread::AbortException ());
    }

    while (true) {
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            if (not fPendingTasks_.empty ()) {
                *result =   fPendingTasks_.front ();
                fPendingTasks_.pop_front ();
                DbgTrace ("ThreadPool::WaitForNextTask_ () is starting a new task");
                return;
            }
        }

        // Prevent spinwaiting... This event is SET when any new item arrives
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - about to wait for added tasks");
        fTasksMaybeAdded_.Wait ();
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - completed wait for added tasks");
    }
}

ThreadPool::TPInfo_      ThreadPool::mkThread_ ()
{
    shared_ptr<MyRunnable_> r   { make_shared<ThreadPool::MyRunnable_> (*this) };
    static  int sThreadNum_ =   1;  // race condition for updating this number, but who cares - its purely cosmetic...
    Thread                  t   {
        [r] () { r->Run (); }
        , Thread::eAutoStart
        , Characters::Format (L"Thread Pool Entry %d", sThreadNum_++)
    };
    return TPInfo_ { t, r };
}
<commit_msg>USE_NOISY_TRACE_IN_THIS_MODULE_ define support foir ThreadPool module<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2016.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#include    "../Characters/Format.h"
#include    "../Characters/CString/Utilities.h"
#include    "../Characters/StringBuilder.h"
#include    "../Characters/ToString.h"
#include    "../Memory/BlockAllocated.h"

#include    "Common.h"
#include    "Sleep.h"
#include    "TimeOutException.h"

#include    "ThreadPool.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Containers;
using   namespace   Stroika::Foundation::Execution;

using   Characters::String;
using   Characters::StringBuilder;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

class   ThreadPool::MyRunnable_  {
public:
    MyRunnable_ (ThreadPool& threadPool)
        : fCurTaskUpdateCritSection_ ()
        , fThreadPool_ (threadPool)
        , fCurTask_ ()
        , fNextTask_ ()
    {
    }

public:
    ThreadPool::TaskType    GetCurrentTask () const
    {
        auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
        // THIS CODE IS TOO SUBTLE - BUT BECAUSE OF HOW THIS IS CALLED - fNextTask_ will NEVER be in the middle of being updated during this code - so this test is OK
        // Caller is never in the middle of doing a WaitForNextTask - and because we have this lock - we aren't updateing fCurTask_ or fNextTask_ either
        Assert (fCurTask_ == nullptr or fNextTask_ == nullptr);   // one or both must be null
        return fCurTask_ == nullptr ? fNextTask_ : fCurTask_;
    }

public:
    nonvirtual void    Run ()
    {
        // For NOW - allow ThreadAbort to just kill this thread. In the future - we may want to implement some sort of restartability

        // Keep grabbing new tasks, and running them
        while (true) {
            {
                fThreadPool_.WaitForNextTask_ (&fNextTask_);            // This will block INDEFINITELY until ThreadAbort throws out or we have a new task to run
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                Assert (fNextTask_ != nullptr);
                Assert (fCurTask_ == nullptr);
                fCurTask_ = fNextTask_;
                fNextTask_ = nullptr;
                Assert (fCurTask_ != nullptr);
                Assert (fNextTask_ == nullptr);
            }
            try {
                fCurTask_ ();
                fCurTask_ = nullptr;
            }
            catch (const Thread::AbortException&) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                throw;  // cancel this thread
            }
            catch (...) {
                auto    critSec { make_unique_lock (fCurTaskUpdateCritSection_) };
                fCurTask_ = nullptr;
                // other exceptions WARNING WITH DEBUG MESSAGE - but otehrwise - EAT/IGNORE
            }
        }
    }

private:
    mutable recursive_mutex     fCurTaskUpdateCritSection_;
    ThreadPool&                 fThreadPool_;
    ThreadPool::TaskType        fCurTask_;
    ThreadPool::TaskType        fNextTask_;
};

/*
 ********************************************************************************
 ****************************** Execution::ThreadPool ***************************
 ********************************************************************************
 */
ThreadPool::ThreadPool (unsigned int nThreads)
    : fCriticalSection_ ()
    , fAborted_ (false)
    , fThreads_ ()
    , fPendingTasks_ ()
    , fTasksMaybeAdded_ (WaitableEvent::eAutoReset)
{
    SetPoolSize (nThreads);
}

ThreadPool::~ThreadPool ()
{
    try {
        this->AbortAndWaitForDone ();
    }
    catch (...) {
        DbgTrace ("Ignore exception in destroying thread pool ** probably bad thing...");
    }
}

unsigned int    ThreadPool::GetPoolSize () const
{
    return static_cast<unsigned int> (fThreads_.size ());
}

void    ThreadPool::SetPoolSize (unsigned int poolSize)
{
    Debug::TraceContextBumper ctx ("ThreadPool::SetPoolSize");
    Require (not fAborted_);
    auto    critSec { make_unique_lock (fCriticalSection_) };
    while (poolSize > fThreads_.size ()) {
        fThreads_.Add (mkThread_ ());
    }

    // Still quite weak implementation of REMOVAL
    while (poolSize < fThreads_.size ()) {
        // iterate over threads if any not busy, remove that one
        bool anyFoundToKill = false;
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (ct == nullptr) {
                // since we have fCriticalSection_ - we can safely remove this thread
                fThreads_.Remove (i);
                anyFoundToKill = true;
                break;
            }
        }
        if (not anyFoundToKill) {
            // @todo - fix this better/eventually
            DbgTrace ("Failed to lower the loop size - cuz all threads busy - giving up");
            return;
        }
    }
}

ThreadPool::TaskType    ThreadPool::AddTask (const TaskType& task)
{
    //Debug::TraceContextBumper ctx ("ThreadPool::AddTask");
    Require (not fAborted_);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.push_back (task);
    }

    // Notify any waiting threads to wakeup and claim the next task
    fTasksMaybeAdded_.Set ();
    // this would be a race - if aborting and adding tasks at the same time
    Require (not fAborted_);
    return task;
}

void    ThreadPool::AbortTask (const TaskType& task, Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTask");
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                fPendingTasks_.erase (i);
                return;
            }
        }
    }

    // If we got here - its NOT in the task Q, so maybe its already running.
    //
    //

    // TODO:
    //      We walk the list of existing threads and ask each one if its (indirected - running task) is the given one and abort that task.
    //      But that requires we can RESTART an ABORTED thread (or that we remove it from the list - maybe thats better). THat COULD be OK
    //      actually since it involves on API changes and makes sense. The only slight issue is a peformace one but probably for something
    //      quite rare.
    //
    //      Anyhow SB OK for now to just not allow aborting a task which has already started....
    Thread  thread2Kill;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (Iterator<TPInfo_> i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    ct      { tr->GetCurrentTask () };
            if (task == ct) {
                thread2Kill =   i->fThread;
                fThreads_.Update (i, mkThread_ ());
                break;
            }
        }
    }
    if (thread2Kill.GetStatus () != Thread::Status::eNull) {
        thread2Kill.AbortAndWaitForDone (timeout);
    }
}

void    ThreadPool::AbortTasks (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper ctx ("ThreadPool::AbortTasks");
    auto tps = GetPoolSize ();
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (TPInfo_ ti : fThreads_) {
            // @todo fix wrong timeout value here
            ti.fThread.AbortAndWaitForDone (timeout);
        }
        fThreads_.RemoveAll ();
    }
    // hack - not a good design or impl!!! - waste to recreate if not needed!
    SetPoolSize (tps);
}

bool    ThreadPool::IsPresent (const TaskType& task) const
{
    Require (task != nullptr);
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fPendingTasks_.begin (); i != fPendingTasks_.end (); ++i) {
            if (*i == task) {
                return true;
            }
        }
    }
    return IsRunning (task);
}

bool    ThreadPool::IsRunning (const TaskType& task) const
{
    Require (task != nullptr);
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    rTask   { tr->GetCurrentTask () };
            if (task == rTask) {
                return true;
            }
        }
    }
    return false;
}

void    ThreadPool::WaitForTask (const TaskType& task, Time::DurationSecondsType timeout) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForTask");
    // Inefficient / VERY SLOPPY impl
    using   Time::DurationSecondsType;
    DurationSecondsType   timeoutAt   =   timeout + Time::GetTickCount ();
    while (true) {
        if (not IsPresent (task)) {
            return;
        }
        DurationSecondsType   remaining   =   timeoutAt - Time::GetTickCount ();
        Execution::ThrowTimeoutExceptionAfter (timeoutAt);
        Execution::Sleep (min<DurationSecondsType> (remaining, 1.0));
    }
}

Collection<ThreadPool::TaskType>    ThreadPool::GetTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        result.AddAll (fPendingTasks_.begin (), fPendingTasks_.end ());          // copy pending tasks
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

Collection<ThreadPool::TaskType>    ThreadPool::GetRunningTasks () const
{
    Collection<ThreadPool::TaskType>    result;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                result.Add (task);
            }
        }
    }
    return result;
}

size_t  ThreadPool::GetTasksCount () const
{
    size_t  count   =   0;
    {
        // First see if its in the Q
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
        for (auto i = fThreads_.begin (); i != fThreads_.end (); ++i) {
            shared_ptr<MyRunnable_>     tr      { i->fRunnable };
            TaskType                    task    { tr->GetCurrentTask () };
            if (task != nullptr) {
                count++;
            }
        }
    }
    return count;
}

size_t   ThreadPool::GetPendingTasksCount () const
{
    size_t  count   =   0;
    {
        auto    critSec { make_unique_lock (fCriticalSection_) };
        count += fPendingTasks_.size ();
    }
    return count;
}

void    ThreadPool::WaitForDoneUntil (Time::DurationSecondsType timeoutAt) const
{
    Debug::TraceContextBumper ctx ("ThreadPool::WaitForDoneUntil");
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Require (fAborted_);
    {
        Collection<Thread>  threadsToShutdown;      // cannot keep critical section while waiting on subthreads since they may need to acquire the critsection for whatever they are doing...
#if     qDefaultTracingOn
        Collection<String>  threadsNotAlreadyDone;  // just for DbgTrace message purposes
#endif
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            for (auto ti : fThreads_) {
                threadsToShutdown.Add (ti.fThread);
#if     qDefaultTracingOn
                if (not ti.fThread.IsDone ()) {
                    threadsNotAlreadyDone.Add (Execution::FormatThreadID (ti.fThread.GetID ()));
                }
#endif
            }
        }
        DbgTrace (L"threadsToShutdown.size = %d", threadsToShutdown.size ());
#if     qDefaultTracingOn
        DbgTrace (L"threadsNotAlreadyDone = %s", Characters::ToString (threadsNotAlreadyDone).c_str ());
#endif
        for (auto t : threadsToShutdown) {
            t.WaitForDoneUntil (timeoutAt);
        }
    }
}

void    ThreadPool::Abort ()
{
    Debug::TraceContextBumper ctx ("ThreadPool::Abort");
    Thread::SuppressInterruptionInContext suppressCtx; // must cleanly shut down each of our subthreads - even if our thread is aborting...
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    fAborted_ = true;   // No race, because fAborted never 'unset'
    // no need to set fTasksMaybeAdded_, since aborting each thread should be sufficient
    {
        // Clear the task Q and then abort each thread
        auto    critSec { make_unique_lock (fCriticalSection_) };
        fPendingTasks_.clear ();
        for (TPInfo_ && ti : fThreads_) {
            ti.fThread.Abort ();
        }
    }
}

void    ThreadPool::AbortAndWaitForDone (Time::DurationSecondsType timeout)
{
    Debug::TraceContextBumper traceCtx ("ThreadPool::AbortAndWaitForDone");
    Thread::SuppressInterruptionInContext suppressCtx; // must cleanly shut down each of our subthreads - even if our thread is aborting...
    DbgTrace (L"this-status: %s", ToString ().c_str ());
    Abort ();
    WaitForDone (timeout);
}

String  ThreadPool::ToString () const
{
    auto    critSec { make_unique_lock (fCriticalSection_) };
    StringBuilder   sb;
    sb += L"{";
    sb += Characters::Format (L"pending-task-count: %d", GetPendingTasksCount ()) + L", ";
    sb += Characters::Format (L"running-task-count: %d", GetRunningTasks ().size ()) + L", ";
    sb += Characters::Format (L"pool-thread-count: %d", fThreads_.size ()) + L", ";
    sb += L"}";
    return sb.str ();
}

// THIS is called NOT from 'this' - but from the context of an OWNED thread of the pool
void    ThreadPool::WaitForNextTask_ (TaskType* result)
{
    RequireNotNull (result);
    if (fAborted_) {
        Execution::Throw (Thread::AbortException ());
    }

    while (true) {
        {
            auto    critSec { make_unique_lock (fCriticalSection_) };
            if (not fPendingTasks_.empty ()) {
                *result =   fPendingTasks_.front ();
                fPendingTasks_.pop_front ();
                DbgTrace ("ThreadPool::WaitForNextTask_ () is starting a new task");
                return;
            }
        }

        // Prevent spinwaiting... This event is SET when any new item arrives
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - about to wait for added tasks");
        fTasksMaybeAdded_.Wait ();
        //DbgTrace ("ThreadPool::WaitForNextTask_ () - completed wait for added tasks");
    }
}

ThreadPool::TPInfo_      ThreadPool::mkThread_ ()
{
    shared_ptr<MyRunnable_> r   { make_shared<ThreadPool::MyRunnable_> (*this) };
    static  int sThreadNum_ =   1;  // race condition for updating this number, but who cares - its purely cosmetic...
    Thread                  t   {
        [r] () { r->Run (); }
        , Thread::eAutoStart
        , Characters::Format (L"Thread Pool Entry %d", sThreadNum_++)
    };
    return TPInfo_ { t, r };
}
<|endoftext|>


<commit_before><commit_msg>fix: revert formating<commit_after><|endoftext|>


<commit_before>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ozone/wayland_display.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <wayland-egl.h>

#include "ozone/wayland_input_device.h"
#include "ozone/wayland_screen.h"
#include "ozone/wayland_window.h"
#include "ozone/wayland_input_method_event_filter.h"
#include "base/message_loop.h"

#ifdef USE_CAIRO_GLESV2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GL/gl.h>
#endif

#include <EGL/egl.h>
#include <EGL/eglext.h>

namespace ui {

WaylandDisplay* g_display = NULL;

WaylandDisplay* WaylandDisplay::GetDisplay()
{
  if (!g_display)
    g_display = WaylandDisplay::Connect(NULL);
  return g_display;
}

void WaylandDisplay::DestroyDisplay()
{
  if(g_display)
    delete g_display;
  g_display = NULL;
}

// static
WaylandDisplay* WaylandDisplay::Connect(char* name)
{
  static const struct wl_registry_listener registry_listener = {
    WaylandDisplay::DisplayHandleGlobal
  };

  WaylandDisplay* display = new WaylandDisplay(name);
  if (!display->display_) {
    delete display;
    return NULL;
  }

  wl_display_set_user_data(display->display_, display);

  display->registry_ =  wl_display_get_registry(display->display_);
  wl_registry_add_listener(display->registry_, &registry_listener, display);

  wl_display_dispatch(display->display_);

  display->CreateCursors();

  return display;
}

void WaylandDisplay::AddWindow(WaylandWindow* window)
{
  if(window)
    window_list_.push_back(window);
}

void WaylandDisplay::AddTask(WaylandTask* task)
{
  if(task)
    task_list_.push_back(task);
}

void WaylandDisplay::ProcessTasks()
{
  WaylandTask *task = NULL;

  while(!task_list_.empty())
  {
    task = task_list_.front();
    task->Run();
    task_list_.pop_front();
    delete task;
  }
}

void WaylandDisplay::RemoveWindow(WaylandWindow* window)
{
  if(!window)
    return;

  WaylandTask *task = NULL;

  for (std::list<WaylandTask*>::iterator i = task_list_.begin();
      i != task_list_.end(); ++i) {
    if((*i)->GetWindow() == window)
    {
      delete *i;
      i = task_list_.erase(i);
    }
  }

  for (std::list<WaylandWindow*>::iterator i = window_list_.begin();
      i != window_list_.end(); ++i) {
    if((*i) == window)
    {
      i = window_list_.erase(i);
      break;
    }
  }

  if(window_list_.size() < 1)
  {
    base::MessageLoop::current()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
  }
}

bool WaylandDisplay::IsWindow(WaylandWindow* window)
{
  for (std::list<WaylandWindow*>::iterator i = window_list_.begin();
      i != window_list_.end(); ++i) {
    if((*i) == window)
    {
      return true;
    }
  }

  return false;
}

InputMethod* WaylandDisplay::GetInputMethod() const
{
  return input_method_filter_ ? input_method_filter_->GetInputMethod(): NULL;
}

// static
WaylandDisplay* WaylandDisplay::GetDisplay(wl_display* display)
{
  return static_cast<WaylandDisplay*>(wl_display_get_user_data(display));
}

WaylandDisplay::WaylandDisplay(char* name) : display_(NULL),
    cursor_theme_(NULL),
    cursors_(NULL),
    compositor_(NULL),
    shell_(NULL),
    shm_(NULL)
{
  display_ = wl_display_connect(name);
  input_method_filter_ = new WaylandInputMethodEventFilter;
}

WaylandDisplay::~WaylandDisplay()
{
  if (window_list_.size() > 0)
    fprintf(stderr, "toytoolkit warning: windows exist.\n");

  if (task_list_.size() > 0)
    fprintf(stderr, "toytoolkit warning: deferred tasks exist.\n");

  for (std::list<WaylandInputDevice*>::iterator i = input_list_.begin();
      i != input_list_.end(); ++i) {
    delete *i;
  }

  for (std::list<WaylandScreen*>::iterator i = screen_list_.begin();
      i != screen_list_.end(); ++i) {
    delete *i;
  }

  DestroyCursors();

  if (compositor_)
    wl_compositor_destroy(compositor_);
  if (shell_)
    wl_shell_destroy(shell_);
  if (shm_)
    wl_shm_destroy(shm_);
  if (display_)
    wl_display_disconnect(display_);

  delete input_method_filter_;
}

wl_surface* WaylandDisplay::CreateSurface()
{
  return wl_compositor_create_surface(compositor_);
}

std::list<WaylandScreen*> WaylandDisplay::GetScreenList() const {
  return screen_list_;
}

// static
void WaylandDisplay::DisplayHandleGlobal(void *data,
    struct wl_registry *registry,
    uint32_t name,
    const char *interface,
    uint32_t version)
{

  WaylandDisplay* disp = static_cast<WaylandDisplay*>(data);

  if (strcmp(interface, "wl_compositor") == 0) {
    disp->compositor_ = static_cast<wl_compositor*>(
        wl_registry_bind(registry, name, &wl_compositor_interface, 1));
  } else if (strcmp(interface, "wl_output") == 0) {
    WaylandScreen* screen = new WaylandScreen(disp, name);
    disp->screen_list_.push_back(screen);
  } else if (strcmp(interface, "wl_seat") == 0) {
    WaylandInputDevice *input_device = new WaylandInputDevice(disp, name);
    disp->input_list_.push_back(input_device);
  } else if (strcmp(interface, "wl_shell") == 0) {
    disp->shell_ = static_cast<wl_shell*>(
        wl_registry_bind(registry, name, &wl_shell_interface, 1));
  } else if (strcmp(interface, "wl_shm") == 0) {
    disp->shm_ = static_cast<wl_shm*>(
        wl_registry_bind(registry, name, &wl_shm_interface, 1));
  }
}

static const char *cursor_names[] = {
  "bottom_left_corner",
  "bottom_right_corner",
  "bottom_side",
  "grabbing",
  "left_ptr",
  "left_side",
  "right_side",
  "top_left_corner",
  "top_right_corner",
  "top_side",
  "xterm",
  "hand1",
};

void WaylandDisplay::CreateCursors()
{
  unsigned int i, array_size = sizeof(cursor_names) / sizeof(cursor_names[0]);
  cursor_theme_ = wl_cursor_theme_load(NULL, 32, shm_);
  cursors_ = new wl_cursor*[array_size];
  memset(cursors_, 0, sizeof(wl_cursor*) * array_size);

  for(i = 0; i < array_size; i++)
    cursors_[i] = wl_cursor_theme_get_cursor(cursor_theme_, cursor_names[i]);
}

void WaylandDisplay::DestroyCursors()
{
  if(cursor_theme_)
  {
    wl_cursor_theme_destroy(cursor_theme_);
    cursor_theme_ = NULL;
  }

  if(cursors_)
  {
    delete[] cursors_;
    cursors_ = NULL;
  }
}

void WaylandDisplay::SetPointerImage(WaylandInputDevice *device, uint32_t time, int index)
{
  struct wl_buffer *buffer;
  struct wl_cursor *cursor;
  struct wl_cursor_image *image;

  if (index == device->GetCurrentPointerImage())
    return;

  cursor = cursors_[index];
  if (!cursor)
    return;

  image = cursor->images[0];
  buffer = wl_cursor_image_get_buffer(image);
  if (!buffer)
    return;

  device->SetCurrentPointerImage(index);
  // TODO:
  //  wl_pointer_set_cursor(device->GetPointer(), time, buffer,
  //                        image->hotspot_x, image->hotspot_y);
}

cairo_surface_t* WaylandDisplay::CreateSurfaceFromFile(const char *filename, gfx::Rect *rect)
{
  return NULL;
}

void WaylandDisplay::AddWindowCallback(const wl_callback_listener *listener, WaylandWindow *window)
{
  wl_callback *cb;
  cb = wl_display_sync(display_);
  wl_callback_add_listener(cb, listener, window);
}

cairo_surface_t* WaylandDisplay::CreateSurface(wl_surface *surface, gfx::Rect *rect, uint32_t flags)
{
  return NULL;
}

}  // namespace ui
<commit_msg>Track Chromium base/message_loop headers directory change<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ozone/wayland_display.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <wayland-egl.h>

#include "ozone/wayland_input_device.h"
#include "ozone/wayland_screen.h"
#include "ozone/wayland_window.h"
#include "ozone/wayland_input_method_event_filter.h"
#include "base/message_loop/message_loop.h"

#ifdef USE_CAIRO_GLESV2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GL/gl.h>
#endif

#include <EGL/egl.h>
#include <EGL/eglext.h>

namespace ui {

WaylandDisplay* g_display = NULL;

WaylandDisplay* WaylandDisplay::GetDisplay()
{
  if (!g_display)
    g_display = WaylandDisplay::Connect(NULL);
  return g_display;
}

void WaylandDisplay::DestroyDisplay()
{
  if(g_display)
    delete g_display;
  g_display = NULL;
}

// static
WaylandDisplay* WaylandDisplay::Connect(char* name)
{
  static const struct wl_registry_listener registry_listener = {
    WaylandDisplay::DisplayHandleGlobal
  };

  WaylandDisplay* display = new WaylandDisplay(name);
  if (!display->display_) {
    delete display;
    return NULL;
  }

  wl_display_set_user_data(display->display_, display);

  display->registry_ =  wl_display_get_registry(display->display_);
  wl_registry_add_listener(display->registry_, &registry_listener, display);

  wl_display_dispatch(display->display_);

  display->CreateCursors();

  return display;
}

void WaylandDisplay::AddWindow(WaylandWindow* window)
{
  if(window)
    window_list_.push_back(window);
}

void WaylandDisplay::AddTask(WaylandTask* task)
{
  if(task)
    task_list_.push_back(task);
}

void WaylandDisplay::ProcessTasks()
{
  WaylandTask *task = NULL;

  while(!task_list_.empty())
  {
    task = task_list_.front();
    task->Run();
    task_list_.pop_front();
    delete task;
  }
}

void WaylandDisplay::RemoveWindow(WaylandWindow* window)
{
  if(!window)
    return;

  WaylandTask *task = NULL;

  for (std::list<WaylandTask*>::iterator i = task_list_.begin();
      i != task_list_.end(); ++i) {
    if((*i)->GetWindow() == window)
    {
      delete *i;
      i = task_list_.erase(i);
    }
  }

  for (std::list<WaylandWindow*>::iterator i = window_list_.begin();
      i != window_list_.end(); ++i) {
    if((*i) == window)
    {
      i = window_list_.erase(i);
      break;
    }
  }

  if(window_list_.size() < 1)
  {
    base::MessageLoop::current()->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
  }
}

bool WaylandDisplay::IsWindow(WaylandWindow* window)
{
  for (std::list<WaylandWindow*>::iterator i = window_list_.begin();
      i != window_list_.end(); ++i) {
    if((*i) == window)
    {
      return true;
    }
  }

  return false;
}

InputMethod* WaylandDisplay::GetInputMethod() const
{
  return input_method_filter_ ? input_method_filter_->GetInputMethod(): NULL;
}

// static
WaylandDisplay* WaylandDisplay::GetDisplay(wl_display* display)
{
  return static_cast<WaylandDisplay*>(wl_display_get_user_data(display));
}

WaylandDisplay::WaylandDisplay(char* name) : display_(NULL),
    cursor_theme_(NULL),
    cursors_(NULL),
    compositor_(NULL),
    shell_(NULL),
    shm_(NULL)
{
  display_ = wl_display_connect(name);
  input_method_filter_ = new WaylandInputMethodEventFilter;
}

WaylandDisplay::~WaylandDisplay()
{
  if (window_list_.size() > 0)
    fprintf(stderr, "toytoolkit warning: windows exist.\n");

  if (task_list_.size() > 0)
    fprintf(stderr, "toytoolkit warning: deferred tasks exist.\n");

  for (std::list<WaylandInputDevice*>::iterator i = input_list_.begin();
      i != input_list_.end(); ++i) {
    delete *i;
  }

  for (std::list<WaylandScreen*>::iterator i = screen_list_.begin();
      i != screen_list_.end(); ++i) {
    delete *i;
  }

  DestroyCursors();

  if (compositor_)
    wl_compositor_destroy(compositor_);
  if (shell_)
    wl_shell_destroy(shell_);
  if (shm_)
    wl_shm_destroy(shm_);
  if (display_)
    wl_display_disconnect(display_);

  delete input_method_filter_;
}

wl_surface* WaylandDisplay::CreateSurface()
{
  return wl_compositor_create_surface(compositor_);
}

std::list<WaylandScreen*> WaylandDisplay::GetScreenList() const {
  return screen_list_;
}

// static
void WaylandDisplay::DisplayHandleGlobal(void *data,
    struct wl_registry *registry,
    uint32_t name,
    const char *interface,
    uint32_t version)
{

  WaylandDisplay* disp = static_cast<WaylandDisplay*>(data);

  if (strcmp(interface, "wl_compositor") == 0) {
    disp->compositor_ = static_cast<wl_compositor*>(
        wl_registry_bind(registry, name, &wl_compositor_interface, 1));
  } else if (strcmp(interface, "wl_output") == 0) {
    WaylandScreen* screen = new WaylandScreen(disp, name);
    disp->screen_list_.push_back(screen);
  } else if (strcmp(interface, "wl_seat") == 0) {
    WaylandInputDevice *input_device = new WaylandInputDevice(disp, name);
    disp->input_list_.push_back(input_device);
  } else if (strcmp(interface, "wl_shell") == 0) {
    disp->shell_ = static_cast<wl_shell*>(
        wl_registry_bind(registry, name, &wl_shell_interface, 1));
  } else if (strcmp(interface, "wl_shm") == 0) {
    disp->shm_ = static_cast<wl_shm*>(
        wl_registry_bind(registry, name, &wl_shm_interface, 1));
  }
}

static const char *cursor_names[] = {
  "bottom_left_corner",
  "bottom_right_corner",
  "bottom_side",
  "grabbing",
  "left_ptr",
  "left_side",
  "right_side",
  "top_left_corner",
  "top_right_corner",
  "top_side",
  "xterm",
  "hand1",
};

void WaylandDisplay::CreateCursors()
{
  unsigned int i, array_size = sizeof(cursor_names) / sizeof(cursor_names[0]);
  cursor_theme_ = wl_cursor_theme_load(NULL, 32, shm_);
  cursors_ = new wl_cursor*[array_size];
  memset(cursors_, 0, sizeof(wl_cursor*) * array_size);

  for(i = 0; i < array_size; i++)
    cursors_[i] = wl_cursor_theme_get_cursor(cursor_theme_, cursor_names[i]);
}

void WaylandDisplay::DestroyCursors()
{
  if(cursor_theme_)
  {
    wl_cursor_theme_destroy(cursor_theme_);
    cursor_theme_ = NULL;
  }

  if(cursors_)
  {
    delete[] cursors_;
    cursors_ = NULL;
  }
}

void WaylandDisplay::SetPointerImage(WaylandInputDevice *device, uint32_t time, int index)
{
  struct wl_buffer *buffer;
  struct wl_cursor *cursor;
  struct wl_cursor_image *image;

  if (index == device->GetCurrentPointerImage())
    return;

  cursor = cursors_[index];
  if (!cursor)
    return;

  image = cursor->images[0];
  buffer = wl_cursor_image_get_buffer(image);
  if (!buffer)
    return;

  device->SetCurrentPointerImage(index);
  // TODO:
  //  wl_pointer_set_cursor(device->GetPointer(), time, buffer,
  //                        image->hotspot_x, image->hotspot_y);
}

cairo_surface_t* WaylandDisplay::CreateSurfaceFromFile(const char *filename, gfx::Rect *rect)
{
  return NULL;
}

void WaylandDisplay::AddWindowCallback(const wl_callback_listener *listener, WaylandWindow *window)
{
  wl_callback *cb;
  cb = wl_display_sync(display_);
  wl_callback_add_listener(cb, listener, window);
}

cairo_surface_t* WaylandDisplay::CreateSurface(wl_surface *surface, gfx::Rect *rect, uint32_t flags)
{
  return NULL;
}

}  // namespace ui
<|endoftext|>


<commit_before><commit_msg>Prevent div by zero.<commit_after><|endoftext|>