<commit_before>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
/**
 * @file        src/service/sockets/SocketManager.cpp
 * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
 * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
 * @version     1.0
 * @brief       This file implements socket layer manager for cynara
 */

#include <errno.h>
#include <fcntl.h>
#include <memory>
#include <signal.h>
#include <sys/select.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#include <systemd/sd-daemon.h>

#include <log/log.h>
#include <common.h>
#include <config/PathConfig.h>
#include <exceptions/DescriptorNotExistsException.h>
#include <exceptions/InitException.h>
#include <exceptions/UnexpectedErrorException.h>

#include <logic/Logic.h>
#include <main/Cynara.h>
#include <protocol/ProtocolAdmin.h>
#include <protocol/ProtocolAgent.h>
#include <protocol/ProtocolClient.h>
#include <protocol/ProtocolSignal.h>
#include <request/pointers.h>
#include <request/RequestContext.h>
#include <stdexcept>

#include "SocketManager.h"

namespace Cynara {

SocketManager::SocketManager() : m_working(false), m_maxDesc(-1) {
    FD_ZERO(&m_readSet);
    FD_ZERO(&m_writeSet);
}

SocketManager::~SocketManager() {
}

void SocketManager::run(void) {
    init();
    mainLoop();
}

void SocketManager::init(void) {
    LOGI("SocketManger init start");
    const mode_t clientSocketUMask(0);
    const mode_t adminSocketUMask(0077);
    const mode_t agentSocketUMask(0);

    createDomainSocket(std::make_shared<ProtocolClient>(), PathConfig::SocketPath::client,
                       clientSocketUMask, true);
    createDomainSocket(std::make_shared<ProtocolAdmin>(), PathConfig::SocketPath::admin,
                       adminSocketUMask, false);
    createDomainSocket(std::make_shared<ProtocolAgent>(), PathConfig::SocketPath::agent,
                       agentSocketUMask, false);
    createSignalSocket(std::make_shared<ProtocolSignal>());
    LOGI("SocketManger init done");
}

void SocketManager::mainLoop(void) {
    LOGI("SocketManger mainLoop start");
    m_working  = true;
    while (m_working) {
        fd_set readSet = m_readSet;
        fd_set writeSet = m_writeSet;

        int ret = select(m_maxDesc + 1, &readSet, &writeSet, nullptr, nullptr);

        if (ret < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                int err = errno;
                throw UnexpectedErrorException(err, strerror(err));
            }
        } else if (ret > 0) {
            for (int i = 0; i < m_maxDesc + 1 && ret; ++i) {
                if (FD_ISSET(i, &readSet)) {
                    readyForRead(i);
                    --ret;
                }
                if (FD_ISSET(i, &writeSet)) {
                    readyForWrite(i);
                    --ret;
                }
                if (m_fds[i].isUsed() && m_fds[i].hasDataToWrite())
                    addWriteSocket(i);
            }
        }
    }
    LOGI("SocketManger mainLoop done");
}

void SocketManager::mainLoopStop(void) {
    m_working = false;
}

void SocketManager::readyForRead(int fd) {
    LOGD("SocketManger readyForRead on fd [%d] start", fd);
    auto &desc = m_fds[fd];
    if (desc.isListen()) {
        readyForAccept(fd);
        return;
    }

    RawBuffer readBuffer(DEFAULT_BUFFER_SIZE);
    ssize_t size = read(fd, readBuffer.data(), DEFAULT_BUFFER_SIZE);

    if (size > 0) {
        LOGD("read [%zd] bytes", size);
        readBuffer.resize(size);
        if (handleRead(fd, readBuffer)) {
            LOGD("SocketManger readyForRead on fd [%d] successfully done", fd);
            return;
        }
        LOGI("interpreting buffer read from [%d] failed", fd);
    } else if (size < 0) {
        int err = errno;
        switch (err) {
            case EAGAIN:
#if EWOULDBLOCK != EAGAIN
            case EWOULDBLOCK:
#endif
            case EINTR:
                return;
            default:
                LOGW("While reading from [%d] socket, error [%d]:<%s>",
                     fd, err, strerror(err));
        }
    } else {
        LOGN("Socket [%d] closed on other end", fd);
    }
    closeSocket(fd);
    LOGD("SocketManger readyForRead on fd [%d] done", fd);
}

void SocketManager::readyForWrite(int fd) {
    LOGD("SocketManger readyForWrite on fd [%d] start", fd);
    auto &desc = m_fds[fd];
    auto &buffer = desc.prepareWriteBuffer();
    size_t size = buffer.size();
    ssize_t result = write(fd, buffer.data(), size);
    if (result == -1) {
        int err = errno;
        switch (err) {
        case EAGAIN:
        case EINTR:
            // select will trigger write once again, nothing to do
            break;
        case EPIPE:
        default:
            LOGD("Error during write to fd [%d]:<%s> ", fd, strerror(err));
            closeSocket(fd);
            break;
        }
        return; // We do not want to propagate error to next layer
    }

    LOGD("written [%zd] bytes", result);
    buffer.erase(buffer.begin(), buffer.begin() + result);

    if (buffer.empty())
        removeWriteSocket(fd);
    LOGD("SocketManger readyForWrite on fd [%d] done", fd);
}

void SocketManager::readyForAccept(int fd) {
    LOGD("SocketManger readyForAccept on fd [%d] start", fd);
    struct sockaddr_un clientAddr;
    unsigned int clientLen = sizeof(clientAddr);
    int clientFd = accept4(fd, (struct sockaddr*) &clientAddr, &clientLen, SOCK_NONBLOCK);
    if (clientFd == -1) {
        int err = errno;
        LOGW("Error in accept on socket [%d]: <%s>", fd, strerror(err));
        return;
    }
    LOGD("Accept on sock [%d]. New client socket opened [%d]", fd, clientFd);

    auto &desc = createDescriptor(clientFd, m_fds[fd].isClient());
    desc.setListen(false);
    desc.setProtocol(m_fds[fd].protocol()->clone());
    addReadSocket(clientFd);
    LOGD("SocketManger readyForAccept on fd [%d] done", fd);
}

void SocketManager::closeSocket(int fd) {
    LOGD("SocketManger closeSocket fd [%d] start", fd);
    Descriptor &desc = m_fds[fd];
    requestTaker()->contextClosed(std::make_shared<RequestContext>(nullptr,
                                                                   desc.writeQueue()));
    removeReadSocket(fd);
    removeWriteSocket(fd);
    desc.clear();
    close(fd);
    LOGD("SocketManger closeSocket fd [%d] done", fd);
}

bool SocketManager::handleRead(int fd, const RawBuffer &readbuffer) {
    LOGD("SocketManger handleRead on fd [%d] start", fd);
    auto &desc = m_fds[fd];
    desc.pushReadBuffer(readbuffer);

    try {
        while(true) {
            //try extract request from binary data received on socket
            auto req = desc.extractRequest();
            if (!req)   // not enough data to build request yet
                break;
            LOGD("request extracted");

            //build context
            auto context = std::make_shared<RequestContext>(desc.responseTaker(),
                                                            desc.writeQueue());
            //pass request to request taker
            req->execute(req, requestTaker(), context);
        }
    } catch (const Exception &ex) {
        LOGE("Error handling request <%s>. Closing socket", ex.what());
        return false;
    }
    LOGD("SocketManger handleRead on fd [%d] done", fd);
    return true;
}

void SocketManager::createDomainSocket(ProtocolPtr protocol, const std::string &path, mode_t mask,
                                       bool client) {
    int fd = getSocketFromSystemD(path);
    if (fd == -1)
        fd = createDomainSocketHelp(path, mask);

    auto &desc = createDescriptor(fd, client);
    desc.setListen(true);
    desc.setProtocol(protocol);
    addReadSocket(fd);

    LOGD("Domain socket: [%d] added.", fd);
}

int SocketManager::createDomainSocketHelp(const std::string &path, mode_t mask) {
    int fd;

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        int err = errno;
        LOGE("Error during UNIX socket creation: <%s>",  strerror(err));
        throw InitException();
    }

    int flags;
    if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
        flags = 0;
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        int err = errno;
        close(fd);
        LOGE("Error setting \"O_NONBLOCK\" on descriptor [%d] with fcntl: <%s>",
             fd, strerror(err));
        throw InitException();
    }

    sockaddr_un serverAddress;
    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sun_family = AF_UNIX;
    if (path.length() > sizeof(serverAddress.sun_path)) {
        LOGE("Path for unix domain socket <%s> is [%zu] bytes long, while it should be maximum "
             "[%zu] bytes long", path.c_str(), path.length(), sizeof(serverAddress));
        throw InitException();
    }
    strcpy(serverAddress.sun_path, path.c_str());
    unlink(serverAddress.sun_path);

    mode_t originalUmask;
    originalUmask = umask(mask);

    if (bind(fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
        int err = errno;
        close(fd);
        LOGE("Error in bind socket descriptor [%d] to path <%s>: <%s>",
             fd, path.c_str(), strerror(err));
        throw InitException();
    }

    umask(originalUmask);

    if (listen(fd, 5) == -1) {
        int err = errno;
        close(fd);
        LOGE("Error setting listen on file descriptor [%d], path <%s>: <%s>",
             fd, path.c_str(), strerror(err));
        throw InitException();
    }

    return fd;
}

int SocketManager::getSocketFromSystemD(const std::string &path) {
    int n = sd_listen_fds(0);
    LOGI("sd_listen_fds returns: [%d]", n);
    if (n < 0) {
        LOGE("Error in sd_listend_fds");
        throw InitException();
    }

    for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
        if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0) > 0) {
            LOGI("Useable socket <%s> was passed by SystemD under descriptor [%d]",
                    path.c_str(), fd);
            return fd;
        }
    }
    LOGI("No useable sockets were passed by systemd.");
    return -1;
}

void SocketManager::createSignalSocket(ProtocolPtr protocol) {
    sigset_t mask;

    // Maybe someone will find useful some kind of registering signals with callbacks
    // but for now I'm making it as simple as possible.
    sigemptyset(&mask);
    sigaddset(&mask, SIGTERM); // systemd terminates service sending this signal

    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        LOGE("sigprocmask failed: <%s>", strerror(errno));
        return;
    }

    int fd = signalfd(-1, &mask, SFD_NONBLOCK);
    if (fd < 0) {
        LOGE("Creating signal file descriptor failed: <%s>", strerror(errno));
        return;
    }

    auto &desc = createDescriptor(fd, false);
    desc.setListen(false);
    desc.setProtocol(protocol);
    addReadSocket(fd);

    LOGD("Signal socket: [%d] added.", fd);
}

Descriptor &SocketManager::createDescriptor(int fd, bool client) {
    if (fd > m_maxDesc) {
        m_maxDesc = fd;
        if (fd >= static_cast<int>(m_fds.size()))
            m_fds.resize(fd + 20);
    }
    auto &desc = m_fds[fd];
    desc.setUsed(true);
    desc.setClient(client);
    return desc;
}

void SocketManager::addReadSocket(int fd) {
    FD_SET(fd, &m_readSet);
}

void SocketManager::removeReadSocket(int fd) {
    FD_CLR(fd, &m_readSet);
}

void SocketManager::addWriteSocket(int fd) {
    FD_SET(fd, &m_writeSet);
}

void SocketManager::removeWriteSocket(int fd) {
    FD_CLR(fd, &m_writeSet);
}

RequestTakerPtr SocketManager::requestTaker(void) {
    return std::static_pointer_cast<RequestTaker>(m_logic);
}

void SocketManager::disconnectAllClients(void) {
    for(int i = 0; i <= m_maxDesc; ++i) {
        auto &desc = m_fds[i];
        if(desc.isUsed() && desc.isClient() && !desc.isListen())
            closeSocket(i);
    }
}

} // namespace Cynara
<commit_msg>Set all needed socket descriptors to write state<commit_after>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
/**
 * @file        src/service/sockets/SocketManager.cpp
 * @author      Lukasz Wojciechowski <l.wojciechow@partner.samsung.com>
 * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
 * @version     1.0
 * @brief       This file implements socket layer manager for cynara
 */

#include <errno.h>
#include <fcntl.h>
#include <memory>
#include <signal.h>
#include <sys/select.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#include <systemd/sd-daemon.h>

#include <log/log.h>
#include <common.h>
#include <config/PathConfig.h>
#include <exceptions/DescriptorNotExistsException.h>
#include <exceptions/InitException.h>
#include <exceptions/UnexpectedErrorException.h>

#include <logic/Logic.h>
#include <main/Cynara.h>
#include <protocol/ProtocolAdmin.h>
#include <protocol/ProtocolAgent.h>
#include <protocol/ProtocolClient.h>
#include <protocol/ProtocolSignal.h>
#include <request/pointers.h>
#include <request/RequestContext.h>
#include <stdexcept>

#include "SocketManager.h"

namespace Cynara {

SocketManager::SocketManager() : m_working(false), m_maxDesc(-1) {
    FD_ZERO(&m_readSet);
    FD_ZERO(&m_writeSet);
}

SocketManager::~SocketManager() {
}

void SocketManager::run(void) {
    init();
    mainLoop();
}

void SocketManager::init(void) {
    LOGI("SocketManger init start");
    const mode_t clientSocketUMask(0);
    const mode_t adminSocketUMask(0077);
    const mode_t agentSocketUMask(0);

    createDomainSocket(std::make_shared<ProtocolClient>(), PathConfig::SocketPath::client,
                       clientSocketUMask, true);
    createDomainSocket(std::make_shared<ProtocolAdmin>(), PathConfig::SocketPath::admin,
                       adminSocketUMask, false);
    createDomainSocket(std::make_shared<ProtocolAgent>(), PathConfig::SocketPath::agent,
                       agentSocketUMask, false);
    createSignalSocket(std::make_shared<ProtocolSignal>());
    LOGI("SocketManger init done");
}

void SocketManager::mainLoop(void) {
    LOGI("SocketManger mainLoop start");
    m_working  = true;
    while (m_working) {
        fd_set readSet = m_readSet;
        fd_set writeSet = m_writeSet;

        int ret = select(m_maxDesc + 1, &readSet, &writeSet, nullptr, nullptr);

        if (ret < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                int err = errno;
                throw UnexpectedErrorException(err, strerror(err));
            }
        } else if (ret > 0) {
            for (int i = 0; i < m_maxDesc + 1 && ret; ++i) {
                if (FD_ISSET(i, &readSet)) {
                    readyForRead(i);
                    --ret;
                }
                if (FD_ISSET(i, &writeSet)) {
                    readyForWrite(i);
                    --ret;
                }
            }

            for (int i = 0; i < m_maxDesc + 1; ++i) {
                if (m_fds[i].isUsed() && m_fds[i].hasDataToWrite())
                    addWriteSocket(i);
            }
        }
    }
    LOGI("SocketManger mainLoop done");
}

void SocketManager::mainLoopStop(void) {
    m_working = false;
}

void SocketManager::readyForRead(int fd) {
    LOGD("SocketManger readyForRead on fd [%d] start", fd);
    auto &desc = m_fds[fd];
    if (desc.isListen()) {
        readyForAccept(fd);
        return;
    }

    RawBuffer readBuffer(DEFAULT_BUFFER_SIZE);
    ssize_t size = read(fd, readBuffer.data(), DEFAULT_BUFFER_SIZE);

    if (size > 0) {
        LOGD("read [%zd] bytes", size);
        readBuffer.resize(size);
        if (handleRead(fd, readBuffer)) {
            LOGD("SocketManger readyForRead on fd [%d] successfully done", fd);
            return;
        }
        LOGI("interpreting buffer read from [%d] failed", fd);
    } else if (size < 0) {
        int err = errno;
        switch (err) {
            case EAGAIN:
#if EWOULDBLOCK != EAGAIN
            case EWOULDBLOCK:
#endif
            case EINTR:
                return;
            default:
                LOGW("While reading from [%d] socket, error [%d]:<%s>",
                     fd, err, strerror(err));
        }
    } else {
        LOGN("Socket [%d] closed on other end", fd);
    }
    closeSocket(fd);
    LOGD("SocketManger readyForRead on fd [%d] done", fd);
}

void SocketManager::readyForWrite(int fd) {
    LOGD("SocketManger readyForWrite on fd [%d] start", fd);
    auto &desc = m_fds[fd];
    auto &buffer = desc.prepareWriteBuffer();
    size_t size = buffer.size();
    ssize_t result = write(fd, buffer.data(), size);
    if (result == -1) {
        int err = errno;
        switch (err) {
        case EAGAIN:
        case EINTR:
            // select will trigger write once again, nothing to do
            break;
        case EPIPE:
        default:
            LOGD("Error during write to fd [%d]:<%s> ", fd, strerror(err));
            closeSocket(fd);
            break;
        }
        return; // We do not want to propagate error to next layer
    }

    LOGD("written [%zd] bytes", result);
    buffer.erase(buffer.begin(), buffer.begin() + result);

    if (buffer.empty())
        removeWriteSocket(fd);
    LOGD("SocketManger readyForWrite on fd [%d] done", fd);
}

void SocketManager::readyForAccept(int fd) {
    LOGD("SocketManger readyForAccept on fd [%d] start", fd);
    struct sockaddr_un clientAddr;
    unsigned int clientLen = sizeof(clientAddr);
    int clientFd = accept4(fd, (struct sockaddr*) &clientAddr, &clientLen, SOCK_NONBLOCK);
    if (clientFd == -1) {
        int err = errno;
        LOGW("Error in accept on socket [%d]: <%s>", fd, strerror(err));
        return;
    }
    LOGD("Accept on sock [%d]. New client socket opened [%d]", fd, clientFd);

    auto &desc = createDescriptor(clientFd, m_fds[fd].isClient());
    desc.setListen(false);
    desc.setProtocol(m_fds[fd].protocol()->clone());
    addReadSocket(clientFd);
    LOGD("SocketManger readyForAccept on fd [%d] done", fd);
}

void SocketManager::closeSocket(int fd) {
    LOGD("SocketManger closeSocket fd [%d] start", fd);
    Descriptor &desc = m_fds[fd];
    requestTaker()->contextClosed(std::make_shared<RequestContext>(nullptr,
                                                                   desc.writeQueue()));
    removeReadSocket(fd);
    removeWriteSocket(fd);
    desc.clear();
    close(fd);
    LOGD("SocketManger closeSocket fd [%d] done", fd);
}

bool SocketManager::handleRead(int fd, const RawBuffer &readbuffer) {
    LOGD("SocketManger handleRead on fd [%d] start", fd);
    auto &desc = m_fds[fd];
    desc.pushReadBuffer(readbuffer);

    try {
        while(true) {
            //try extract request from binary data received on socket
            auto req = desc.extractRequest();
            if (!req)   // not enough data to build request yet
                break;
            LOGD("request extracted");

            //build context
            auto context = std::make_shared<RequestContext>(desc.responseTaker(),
                                                            desc.writeQueue());
            //pass request to request taker
            req->execute(req, requestTaker(), context);
        }
    } catch (const Exception &ex) {
        LOGE("Error handling request <%s>. Closing socket", ex.what());
        return false;
    }
    LOGD("SocketManger handleRead on fd [%d] done", fd);
    return true;
}

void SocketManager::createDomainSocket(ProtocolPtr protocol, const std::string &path, mode_t mask,
                                       bool client) {
    int fd = getSocketFromSystemD(path);
    if (fd == -1)
        fd = createDomainSocketHelp(path, mask);

    auto &desc = createDescriptor(fd, client);
    desc.setListen(true);
    desc.setProtocol(protocol);
    addReadSocket(fd);

    LOGD("Domain socket: [%d] added.", fd);
}

int SocketManager::createDomainSocketHelp(const std::string &path, mode_t mask) {
    int fd;

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        int err = errno;
        LOGE("Error during UNIX socket creation: <%s>",  strerror(err));
        throw InitException();
    }

    int flags;
    if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
        flags = 0;
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        int err = errno;
        close(fd);
        LOGE("Error setting \"O_NONBLOCK\" on descriptor [%d] with fcntl: <%s>",
             fd, strerror(err));
        throw InitException();
    }

    sockaddr_un serverAddress;
    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sun_family = AF_UNIX;
    if (path.length() > sizeof(serverAddress.sun_path)) {
        LOGE("Path for unix domain socket <%s> is [%zu] bytes long, while it should be maximum "
             "[%zu] bytes long", path.c_str(), path.length(), sizeof(serverAddress));
        throw InitException();
    }
    strcpy(serverAddress.sun_path, path.c_str());
    unlink(serverAddress.sun_path);

    mode_t originalUmask;
    originalUmask = umask(mask);

    if (bind(fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
        int err = errno;
        close(fd);
        LOGE("Error in bind socket descriptor [%d] to path <%s>: <%s>",
             fd, path.c_str(), strerror(err));
        throw InitException();
    }

    umask(originalUmask);

    if (listen(fd, 5) == -1) {
        int err = errno;
        close(fd);
        LOGE("Error setting listen on file descriptor [%d], path <%s>: <%s>",
             fd, path.c_str(), strerror(err));
        throw InitException();
    }

    return fd;
}

int SocketManager::getSocketFromSystemD(const std::string &path) {
    int n = sd_listen_fds(0);
    LOGI("sd_listen_fds returns: [%d]", n);
    if (n < 0) {
        LOGE("Error in sd_listend_fds");
        throw InitException();
    }

    for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; ++fd) {
        if (sd_is_socket_unix(fd, SOCK_STREAM, 1, path.c_str(), 0) > 0) {
            LOGI("Useable socket <%s> was passed by SystemD under descriptor [%d]",
                    path.c_str(), fd);
            return fd;
        }
    }
    LOGI("No useable sockets were passed by systemd.");
    return -1;
}

void SocketManager::createSignalSocket(ProtocolPtr protocol) {
    sigset_t mask;

    // Maybe someone will find useful some kind of registering signals with callbacks
    // but for now I'm making it as simple as possible.
    sigemptyset(&mask);
    sigaddset(&mask, SIGTERM); // systemd terminates service sending this signal

    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        LOGE("sigprocmask failed: <%s>", strerror(errno));
        return;
    }

    int fd = signalfd(-1, &mask, SFD_NONBLOCK);
    if (fd < 0) {
        LOGE("Creating signal file descriptor failed: <%s>", strerror(errno));
        return;
    }

    auto &desc = createDescriptor(fd, false);
    desc.setListen(false);
    desc.setProtocol(protocol);
    addReadSocket(fd);

    LOGD("Signal socket: [%d] added.", fd);
}

Descriptor &SocketManager::createDescriptor(int fd, bool client) {
    if (fd > m_maxDesc) {
        m_maxDesc = fd;
        if (fd >= static_cast<int>(m_fds.size()))
            m_fds.resize(fd + 20);
    }
    auto &desc = m_fds[fd];
    desc.setUsed(true);
    desc.setClient(client);
    return desc;
}

void SocketManager::addReadSocket(int fd) {
    FD_SET(fd, &m_readSet);
}

void SocketManager::removeReadSocket(int fd) {
    FD_CLR(fd, &m_readSet);
}

void SocketManager::addWriteSocket(int fd) {
    FD_SET(fd, &m_writeSet);
}

void SocketManager::removeWriteSocket(int fd) {
    FD_CLR(fd, &m_writeSet);
}

RequestTakerPtr SocketManager::requestTaker(void) {
    return std::static_pointer_cast<RequestTaker>(m_logic);
}

void SocketManager::disconnectAllClients(void) {
    for(int i = 0; i <= m_maxDesc; ++i) {
        auto &desc = m_fds[i];
        if(desc.isUsed() && desc.isClient() && !desc.isListen())
            closeSocket(i);
    }
}

} // namespace Cynara
<|endoftext|>


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

  Library: CppMicroServices

  Copyright (c) German Cancer Research Center,
    Division of Medical and Biological Informatics

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

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

#include <usConfig.h>
#ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT
#include US_BASECLASS_HEADER
#endif

#include "usServiceRegistration.h"
#include "usServiceRegistrationPrivate.h"
#include "usServiceListenerEntry_p.h"
#include "usServiceRegistry_p.h"
#include "usServiceFactory.h"

#include "usModulePrivate.h"
#include "usCoreModuleContext_p.h"

#include <stdexcept>

US_BEGIN_NAMESPACE

typedef ServiceRegistrationPrivate::MutexLocker MutexLocker;

ServiceRegistration::ServiceRegistration()
  : d(0)
{

}

ServiceRegistration::ServiceRegistration(const ServiceRegistration& reg)
  : d(reg.d)
{
  if (d) d->ref.Ref();
}

ServiceRegistration::ServiceRegistration(ServiceRegistrationPrivate* registrationPrivate)
  : d(registrationPrivate)
{
  if (d) d->ref.Ref();
}

ServiceRegistration::ServiceRegistration(ModulePrivate* module, US_BASECLASS_NAME* service,
                                         const ServiceProperties& props)
  : d(new ServiceRegistrationPrivate(module, service, props))
{

}

ServiceRegistration::operator bool() const
{
  return d != 0;
}

ServiceRegistration& ServiceRegistration::operator=(int null)
{
  if (null == 0)
  {
    if (d && !d->ref.Deref())
    {
      delete d;
    }
    d = 0;
  }
  return *this;
}

ServiceRegistration::~ServiceRegistration()
{
  if (d && !d->ref.Deref())
    delete d;
}

ServiceReference ServiceRegistration::GetReference() const
{
  if (!d) throw std::logic_error("ServiceRegistration object invalid");
  if (!d->available) throw std::logic_error("Service is unregistered");

  return d->reference;
}

void ServiceRegistration::SetProperties(const ServiceProperties& props)
{
  if (!d) throw std::logic_error("ServiceRegistration object invalid");

  MutexLocker lock(d->eventLock);

  ServiceListeners::ServiceListenerEntries before;
  // TBD, optimize the locking of services
  {
    //MutexLocker lock2(d->module->coreCtx->globalFwLock);
    MutexLocker lock3(d->propsLock);

    if (d->available)
    {
      // NYI! Optimize the MODIFIED_ENDMATCH code
      int old_rank = 0;
      Any any = d->properties[ServiceConstants::SERVICE_RANKING()];
      if (any.Type() == typeid(int)) old_rank = any_cast<int>(any);

      d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, before, false);
      const std::list<std::string>& classes = ref_any_cast<std::list<std::string> >(d->properties[ServiceConstants::OBJECTCLASS()]);
      long int sid = any_cast<long int>(d->properties[ServiceConstants::SERVICE_ID()]);
      d->properties = ServiceRegistry::CreateServiceProperties(props, classes, sid);

      int new_rank = 0;
      any = d->properties[ServiceConstants::SERVICE_RANKING()];
      if (any.Type() == typeid(int)) new_rank = any_cast<int>(any);

      if (old_rank != new_rank)
      {
        d->module->coreCtx->services.UpdateServiceRegistrationOrder(*this, classes);
      }
    }
    else
    {
      throw std::logic_error("Service is unregistered");
    }
  }
  ServiceListeners::ServiceListenerEntries matchingListeners;
  d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, matchingListeners);
  d->module->coreCtx->listeners.ServiceChanged(matchingListeners,
                                               ServiceEvent(ServiceEvent::MODIFIED, d->reference),
                                               before);

  d->module->coreCtx->listeners.ServiceChanged(before,
                                               ServiceEvent(ServiceEvent::MODIFIED_ENDMATCH, d->reference));
}

void ServiceRegistration::Unregister()
{
  if (!d) throw std::logic_error("ServiceRegistration object invalid");

  if (d->unregistering) return; // Silently ignore redundant unregistration.
  {
    MutexLocker lock(d->eventLock);
    if (d->unregistering) return;
    d->unregistering = true;

    if (d->available)
    {
      if (d->module)
      {
        d->module->coreCtx->services.RemoveServiceRegistration(*this);
      }
    }
    else
    {
      throw std::logic_error("Service is unregistered");
    }
  }

  if (d->module)
  {
    ServiceListeners::ServiceListenerEntries listeners;
    d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, listeners);
     d->module->coreCtx->listeners.ServiceChanged(
         listeners,
         ServiceEvent(ServiceEvent::UNREGISTERING, d->reference));
  }

  {
    MutexLocker lock(d->eventLock);
    {
      MutexLocker lock2(d->propsLock);
      d->available = false;
      #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT
      if (d->module)
      {
        ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator end = d->serviceInstances.end();
        for (ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator i = d->serviceInstances.begin();
             i != end; ++i)
        {
          US_BASECLASS_NAME* obj = i->second;
          try
          {
            // NYI, don't call inside lock
            dynamic_cast<ServiceFactory*>(d->service)->UngetService(i->first,
                                                                    *this,
                                                                    obj);
          }
          catch (const std::exception& /*ue*/)
          {
            US_WARN << "ServiceFactory UngetService implementation threw an exception";
          }
        }
      }
      #endif
      d->module = 0;
      d->dependents.clear();
      d->service = 0;
      d->serviceInstances.clear();
      d->reference = 0;
      d->unregistering = false;
    }
  }
}

bool ServiceRegistration::operator<(const ServiceRegistration& o) const
{
  if (!d) return true;
  return d->reference <(o.d->reference);
}

bool ServiceRegistration::operator==(const ServiceRegistration& registration) const
{
  return d == registration.d;
}

ServiceRegistration& ServiceRegistration::operator=(const ServiceRegistration& registration)
{
  ServiceRegistrationPrivate* curr_d = d;
  d = registration.d;
  if (d) d->ref.Ref();

  if (curr_d && !curr_d->ref.Deref())
    delete curr_d;

  return *this;
}

US_END_NAMESPACE
<commit_msg>Copy the classes list, since we need it after the original is destroyed.<commit_after>/*=============================================================================

  Library: CppMicroServices

  Copyright (c) German Cancer Research Center,
    Division of Medical and Biological Informatics

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

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

#include <usConfig.h>
#ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT
#include US_BASECLASS_HEADER
#endif

#include "usServiceRegistration.h"
#include "usServiceRegistrationPrivate.h"
#include "usServiceListenerEntry_p.h"
#include "usServiceRegistry_p.h"
#include "usServiceFactory.h"

#include "usModulePrivate.h"
#include "usCoreModuleContext_p.h"

#include <stdexcept>

US_BEGIN_NAMESPACE

typedef ServiceRegistrationPrivate::MutexLocker MutexLocker;

ServiceRegistration::ServiceRegistration()
  : d(0)
{

}

ServiceRegistration::ServiceRegistration(const ServiceRegistration& reg)
  : d(reg.d)
{
  if (d) d->ref.Ref();
}

ServiceRegistration::ServiceRegistration(ServiceRegistrationPrivate* registrationPrivate)
  : d(registrationPrivate)
{
  if (d) d->ref.Ref();
}

ServiceRegistration::ServiceRegistration(ModulePrivate* module, US_BASECLASS_NAME* service,
                                         const ServiceProperties& props)
  : d(new ServiceRegistrationPrivate(module, service, props))
{

}

ServiceRegistration::operator bool() const
{
  return d != 0;
}

ServiceRegistration& ServiceRegistration::operator=(int null)
{
  if (null == 0)
  {
    if (d && !d->ref.Deref())
    {
      delete d;
    }
    d = 0;
  }
  return *this;
}

ServiceRegistration::~ServiceRegistration()
{
  if (d && !d->ref.Deref())
    delete d;
}

ServiceReference ServiceRegistration::GetReference() const
{
  if (!d) throw std::logic_error("ServiceRegistration object invalid");
  if (!d->available) throw std::logic_error("Service is unregistered");

  return d->reference;
}

void ServiceRegistration::SetProperties(const ServiceProperties& props)
{
  if (!d) throw std::logic_error("ServiceRegistration object invalid");

  MutexLocker lock(d->eventLock);

  ServiceListeners::ServiceListenerEntries before;
  // TBD, optimize the locking of services
  {
    //MutexLocker lock2(d->module->coreCtx->globalFwLock);
    MutexLocker lock3(d->propsLock);

    if (d->available)
    {
      // NYI! Optimize the MODIFIED_ENDMATCH code
      int old_rank = 0;
      Any any = d->properties[ServiceConstants::SERVICE_RANKING()];
      if (any.Type() == typeid(int)) old_rank = any_cast<int>(any);

      d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, before, false);
      const std::list<std::string> classes = ref_any_cast<std::list<std::string> >(d->properties[ServiceConstants::OBJECTCLASS()]);
      long int sid = any_cast<long int>(d->properties[ServiceConstants::SERVICE_ID()]);
      d->properties = ServiceRegistry::CreateServiceProperties(props, classes, sid);

      int new_rank = 0;
      any = d->properties[ServiceConstants::SERVICE_RANKING()];
      if (any.Type() == typeid(int)) new_rank = any_cast<int>(any);

      if (old_rank != new_rank)
      {
        d->module->coreCtx->services.UpdateServiceRegistrationOrder(*this, classes);
      }
    }
    else
    {
      throw std::logic_error("Service is unregistered");
    }
  }
  ServiceListeners::ServiceListenerEntries matchingListeners;
  d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, matchingListeners);
  d->module->coreCtx->listeners.ServiceChanged(matchingListeners,
                                               ServiceEvent(ServiceEvent::MODIFIED, d->reference),
                                               before);

  d->module->coreCtx->listeners.ServiceChanged(before,
                                               ServiceEvent(ServiceEvent::MODIFIED_ENDMATCH, d->reference));
}

void ServiceRegistration::Unregister()
{
  if (!d) throw std::logic_error("ServiceRegistration object invalid");

  if (d->unregistering) return; // Silently ignore redundant unregistration.
  {
    MutexLocker lock(d->eventLock);
    if (d->unregistering) return;
    d->unregistering = true;

    if (d->available)
    {
      if (d->module)
      {
        d->module->coreCtx->services.RemoveServiceRegistration(*this);
      }
    }
    else
    {
      throw std::logic_error("Service is unregistered");
    }
  }

  if (d->module)
  {
    ServiceListeners::ServiceListenerEntries listeners;
    d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, listeners);
     d->module->coreCtx->listeners.ServiceChanged(
         listeners,
         ServiceEvent(ServiceEvent::UNREGISTERING, d->reference));
  }

  {
    MutexLocker lock(d->eventLock);
    {
      MutexLocker lock2(d->propsLock);
      d->available = false;
      #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT
      if (d->module)
      {
        ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator end = d->serviceInstances.end();
        for (ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator i = d->serviceInstances.begin();
             i != end; ++i)
        {
          US_BASECLASS_NAME* obj = i->second;
          try
          {
            // NYI, don't call inside lock
            dynamic_cast<ServiceFactory*>(d->service)->UngetService(i->first,
                                                                    *this,
                                                                    obj);
          }
          catch (const std::exception& /*ue*/)
          {
            US_WARN << "ServiceFactory UngetService implementation threw an exception";
          }
        }
      }
      #endif
      d->module = 0;
      d->dependents.clear();
      d->service = 0;
      d->serviceInstances.clear();
      d->reference = 0;
      d->unregistering = false;
    }
  }
}

bool ServiceRegistration::operator<(const ServiceRegistration& o) const
{
  if (!d) return true;
  return d->reference <(o.d->reference);
}

bool ServiceRegistration::operator==(const ServiceRegistration& registration) const
{
  return d == registration.d;
}

ServiceRegistration& ServiceRegistration::operator=(const ServiceRegistration& registration)
{
  ServiceRegistrationPrivate* curr_d = d;
  d = registration.d;
  if (d) d->ref.Ref();

  if (curr_d && !curr_d->ref.Deref())
    delete curr_d;

  return *this;
}

US_END_NAMESPACE
<|endoftext|>


<commit_before>/*
 * PuckManager.cpp
 *
 *  Created on: 09.06.2017
 *      Author: aca592
 */

#include "PuckManager.h"

#include "logger.h"
#include "logScope.h"

PuckManager::PuckManager(int chid)
	: puckList()
	, nextPuckID(0)
	, chid(chid)
{}

PuckManager::~PuckManager() {
	reset();
}

void PuckManager::reset() {
	std::list<PuckContext*>::iterator it = puckList.begin();
	while(it != puckList.end()) {
		LOG_DEBUG <<"[Puck" + std::to_string((*it)->getPuckID()) + "] was deleted \n";
		delete *it;					// delete the puck from memory
		it = puckList.erase(it);	// delete the puck from list
	}
}

PuckManager::ManagerReturn PuckManager::newPuck(PuckSignal::PuckType type) {
	LOG_SCOPE;
	ManagerReturn ret;
	ret.errorFlag = false;
	ret.actorFlag = true;
	ret.speedSignal = PuckSignal::PuckSpeed::SLOW;
	ret.actorSignal = ActorSignal::ACCEPTED_PUCK;
	puckList.push_back(new PuckContext(chid, type, nextPuckID++));
	return ret;
}

void PuckManager::addPuck(PuckContext *puck) {
	LOG_SCOPE;
	puck->setPuckID(nextPuckID++);
	puckList.push_back(puck);
	LOG_DEBUG << "[PuckManager] Size of puckList" << puckList.size() << endl;
}

PuckSignal::PuckSpeed PuckManager::getCurrentSpeed() {
	PuckSignal::PuckSpeed speed = PuckSignal::PuckSpeed::SLIDE_STOP;
	std::list<PuckContext*>::iterator it = puckList.begin();
	while (it != puckList.end()) {
		if ((*it)->getCurrentSpeed() > speed) {
			// Check for speed prio
			speed = (*it)->getCurrentSpeed();
		}
		it++;
	}

	if(puckList.empty()) {
		speed = PuckSignal::PuckSpeed::STOP;
	}
	return speed;
}

void PuckManager::handlePuckTimer(const PuckSignal::Signal& signal, ManagerReturn& prioReturnVal) {
	LOG_DEBUG << "[PuckManager] Handle Puck Timer \n";
	std::list<PuckContext*>::iterator it = puckList.begin();
	while (it != puckList.end()) {
		// check for puckID
		uint16_t currentPuckID = (*it)->getPuckID();
		if (currentPuckID == signal.timerSignal.TimerInfo.puckID) {
			// pass the timer signal
			PuckSignal::Return returnVal = (*it)->process(signal);
			// check return value
			if (returnVal.puckReturn == PuckSignal::PuckReturn::SLIDE_FULL) {
				LOG_DEBUG << "[PuckManager] Puck returned Slide full \n";
				setErrorOnBothSlidesAreFull(prioReturnVal);
				sort.process(returnVal.puckReturn);
				prioReturnVal.actorFlag = true;
				prioReturnVal.actorSignal = ActorSignal::SEND_SLIDE_FULL;
			} else if (returnVal.puckReturn != PuckSignal::PuckReturn::ACCEPT
					&& returnVal.puckReturn != PuckSignal::PuckReturn::ERROR) {
				// puck should be triggered on accept or error -> unexpected otherwise
				prioReturnVal.errorFlag = true;
				prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
			} else if (returnVal.puckReturn == PuckSignal::PuckReturn::ERROR) {
				prioReturnVal.errorFlag = true;
				prioReturnVal.errorSignal = ErrorSignal::PUCK_LOST; //Late Timer expiered
			}
		}
		it++;
	}
}

void PuckManager::handlePuckSignal(const PuckSignal::Signal &signal, int32_t &acceptCounter, int32_t &warningCounter, ManagerReturn &prioReturnVal) {
	LOG_DEBUG << "[PuckManager] Handle Puck Signal \n";
	// check the signal for every puck in the list
	if(puckList.empty()) {
		prioReturnVal.speedSignal = PuckSignal::PuckSpeed::STOP;
		prioReturnVal.errorFlag = true;
		prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
	}

	std::list<PuckContext*>::iterator it = puckList.begin();
	while (it != puckList.end()) {
		PuckSignal::Return returnVal = (*it)->process(signal);

		switch (returnVal.puckReturn) {
		case PuckSignal::PuckReturn::ACCEPT:
			acceptCounter++;
			break;
		case PuckSignal::PuckReturn::DELETE:
			acceptCounter++;
			LOG_DEBUG << "[Puck" + std::to_string((*it)->getPuckID()) + "] was deleted \n";
			delete *it;					// delete the puck from memory
			it = puckList.erase(it);	// delete the puck from list
			break;
		case PuckSignal::PuckReturn::SEND:
			acceptCounter++;
			prioReturnVal.puckType = new PuckSignal::PuckType((*it)->getType());
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::SEND_PUCK;
			break;
		case PuckSignal::PuckReturn::RECEIVED:
			acceptCounter++;
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::RECEIVED_PUCK;
			break;
		case PuckSignal::PuckReturn::EVALUATE:
			acceptCounter++;
			if (!sort.process((*it)->getType())) {
				prioReturnVal.actorFlag = true;
				prioReturnVal.actorSignal = ActorSignal::OPEN_SWITCH;
			}
			break;
		case PuckSignal::PuckReturn::START_HEIGHT:
			acceptCounter++;
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::START_MEASUREMENT;
			break;
		case PuckSignal::PuckReturn::STOP_HEIGHT:
			acceptCounter++;
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::STOP_MEASUREMENT;
			break;

		case PuckSignal::PuckReturn::SLIDE_EMPTY:
			acceptCounter++;

			sort.process(returnVal.puckReturn);
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::SEND_SLIDE_EMPTY;
			LOG_DEBUG << "[Puck" + std::to_string((*it)->getPuckID()) + "] was deleted \n";
			delete *it;					// delete the puck from memory
			it = puckList.erase(it);	// delete the puck from list
			break;
			//
		case PuckSignal::PuckReturn::WARNING:
			warningCounter++;
			break;
		case PuckSignal::PuckReturn::DENY:
			break;
		case PuckSignal::PuckReturn::ERROR:
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::PUCK_LOST;
			break;
		default:
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
			LOG_DEBUG << "[PuckManager] Returning with Default UNEXPECTED_SIGNAL";
			//return prioReturnVal;
		}

		if (returnVal.puckReturn != PuckSignal::PuckReturn::DELETE || returnVal.puckReturn != PuckSignal::PuckReturn::SLIDE_EMPTY) { //if a puck was deleted, you should not increment the iteratpr
			++it;
		}
	}

	prioReturnVal.speedSignal = getCurrentSpeed();

	/* No one Accepted the signal, so it is likely to be unexpected*/
	if(acceptCounter == 0) {
		prioReturnVal.errorFlag = true;
		prioReturnVal.errorSignal = ErrorSignal::NOT_ACCEPTED;
	}
}

bool PuckManager::passToPuckSort(const PuckSignal::Signal& signal, ManagerReturn& prioReturnVal) {
	LOG_DEBUG << "[PuckManager] Pass to Pucksort \n";
	if ( signal.signalType == PuckSignal::SignalType::SERIAL_SIGNAL
		 && (signal.serialSignal == Serial_n::ser_proto_msg::SLIDE_FULL_SER
		 || signal.serialSignal == Serial_n::ser_proto_msg::SLIDE_EMTPY_SER)){

		setErrorOnBothSlidesAreFull(prioReturnVal);
		sort.process(signal.serialSignal);
		LOG_DEBUG << "[PuckManager] Returning with with Slide management only \n";
		return true;
	} else {
		return false;
	}
}

bool PuckManager::checkErrorMetal(const PuckSignal::Signal& signal) {
	// metal detect hot fix
	return (signal.signalType == PuckSignal::SignalType::INTERRUPT_SIGNAL
			&& signal.interruptSignal
			== interrupts::interruptSignals::METAL_DETECT);
}

void PuckManager::setErrorOnBothSlidesAreFull(ManagerReturn &prioReturnVal) {
	bool conditionIsMet = sort.areBothSlidesFull();
	LOG_DEBUG << "[PuckManager] areBothSlidesFull: " << int(conditionIsMet) << endl;
	if (conditionIsMet) {
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::BOTH_SLIDES_FULL;
	}
}

bool PuckManager::checkSerialError(const PuckSignal::Signal& signal) {
	/* Accept serial RESUME and STOP signals */
	return (signal.signalType == PuckSignal::SignalType::SERIAL_SIGNAL
			&& (signal.serialSignal == Serial_n::ser_proto_msg::STOP_SER
			|| signal.serialSignal == Serial_n::ser_proto_msg::RESUME_SER));
}

PuckManager::ManagerReturn PuckManager::process(PuckSignal::Signal signal) {
	LOG_SCOPE;

	ManagerReturn prioReturnVal; // the prioritized return value
	prioReturnVal.speedSignal = PuckSignal::PuckSpeed::FAST;
	prioReturnVal.actorFlag = false;
	prioReturnVal.errorFlag = false;
	prioReturnVal.puckType = nullptr;

	int32_t acceptCounter = 0; // count all accepted signals
	int32_t warningCounter = 0; // count all warnings

	if(passToPuckSort(signal, prioReturnVal)){ //Only pucksort is intrested in signal
			prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed
			LOG_DEBUG << "[PuckManager] Returning with pass to puck sort only \n";
			return prioReturnVal;
	}

	prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed

#if !machine // machine0
	// check for first interrupt signal - puck must be created
	if(	signal.signalType == PuckSignal::SignalType::INTERRUPT_SIGNAL &&
		signal.interruptSignal == interrupts::interruptSignals::INLET_IN) {

		addPuck(new PuckContext(chid));
		prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed

		return prioReturnVal;
	}

	// signal can be passed for speed prio -> every puck should return deny
#endif

	// Pass the timer signal to the given puckID
	if(signal.signalType == PuckSignal::SignalType::TIMER_SIGNAL) {
		handlePuckTimer(signal, prioReturnVal);
		prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed
		LOG_DEBUG << "[PuckManager] Returning with with timer management only \n";
		return prioReturnVal;
	} else {
		// check the signal for every puck in the list
		handlePuckSignal(signal, acceptCounter, warningCounter, prioReturnVal);
	}

	/* Signal was unexpected for the pucks, might be expected somewhere else */
	if(prioReturnVal.errorFlag && prioReturnVal.errorSignal == ErrorSignal::NOT_ACCEPTED){
		if(checkErrorMetal(signal)){
			LOG_DEBUG << "[PuckManager] Error was from Metall \n";
			prioReturnVal.errorFlag = false;
		} else if(checkSerialError(signal)){
			LOG_DEBUG << "[PuckManager] Error was from Serial \n";
			prioReturnVal.errorFlag = false;
		} else {
			LOG_DEBUG << "[PuckManager] Transform NOT_ACCEPTED to UNEXPECTED_SIGNAL \n";
			prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
		}
	}

	/*if(!prioReturnVal.errorFlag) {
		if(acceptCounter > 1 || acceptCounter < 0) {
			LOG_DEBUG << "[PuckManager] Returning with MULTIPLE_ACCEPT";
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::MULTIPLE_ACCEPT;
			return prioReturnVal;
		}
	}*/

	if(puckList.empty()) {
		prioReturnVal.speedSignal = PuckSignal::PuckSpeed::STOP;
	}

	prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed

	LOG_DEBUG << "Puck Manager returns: Speed " << prioReturnVal.speedSignal << " Actor Flag " << prioReturnVal.actorFlag
			 << " Actor Signal " << prioReturnVal.actorSignal << " Error Flag " << prioReturnVal.errorFlag
			 << " Error Signal " << prioReturnVal.errorSignal << " \n";

	// everything OK
	return prioReturnVal;
}
<commit_msg>snap<commit_after>/*
 * PuckManager.cpp
 *
 *  Created on: 09.06.2017
 *      Author: aca592
 */

#include "PuckManager.h"

#include "logger.h"
#include "logScope.h"

PuckManager::PuckManager(int chid)
	: puckList()
	, nextPuckID(0)
	, chid(chid)
{}

PuckManager::~PuckManager() {
	reset();
}

void PuckManager::reset() {
	std::list<PuckContext*>::iterator it = puckList.begin();
	while(it != puckList.end()) {
		LOG_DEBUG <<"[Puck" + std::to_string((*it)->getPuckID()) + "] was deleted \n";
		delete *it;					// delete the puck from memory
		it = puckList.erase(it);	// delete the puck from list
	}
}

PuckManager::ManagerReturn PuckManager::newPuck(PuckSignal::PuckType type) {
	LOG_SCOPE;
	ManagerReturn ret;
	ret.errorFlag = false;
	ret.actorFlag = true;
	ret.speedSignal = PuckSignal::PuckSpeed::SLOW;
	ret.actorSignal = ActorSignal::ACCEPTED_PUCK;
	puckList.push_back(new PuckContext(chid, type, nextPuckID++));
	return ret;
}

void PuckManager::addPuck(PuckContext *puck) {
	LOG_SCOPE;
	puck->setPuckID(nextPuckID++);
	puckList.push_back(puck);
	LOG_DEBUG << "[PuckManager] Size of puckList" << puckList.size() << endl;
}

PuckSignal::PuckSpeed PuckManager::getCurrentSpeed() {
	PuckSignal::PuckSpeed speed = PuckSignal::PuckSpeed::SLIDE_STOP;
	std::list<PuckContext*>::iterator it = puckList.begin();
	while (it != puckList.end()) {
		if ((*it)->getCurrentSpeed() > speed) {
			// Check for speed prio
			speed = (*it)->getCurrentSpeed();
		}
		it++;
	}

	if(puckList.empty()) {
		speed = PuckSignal::PuckSpeed::STOP;
	}
	return speed;
}

void PuckManager::handlePuckTimer(const PuckSignal::Signal& signal, ManagerReturn& prioReturnVal) {
	LOG_DEBUG << "[PuckManager] Handle Puck Timer \n";
	std::list<PuckContext*>::iterator it = puckList.begin();
	while (it != puckList.end()) {
		// check for puckID
		uint16_t currentPuckID = (*it)->getPuckID();
		if (currentPuckID == signal.timerSignal.TimerInfo.puckID) {
			// pass the timer signal
			PuckSignal::Return returnVal = (*it)->process(signal);
			// check return value
			if (returnVal.puckReturn == PuckSignal::PuckReturn::SLIDE_FULL) {
				LOG_DEBUG << "[PuckManager] Puck returned Slide full \n";
				setErrorOnBothSlidesAreFull(prioReturnVal);
				sort.process(returnVal.puckReturn);
				prioReturnVal.actorFlag = true;
				prioReturnVal.actorSignal = ActorSignal::SEND_SLIDE_FULL;
			} else if (returnVal.puckReturn != PuckSignal::PuckReturn::ACCEPT
					&& returnVal.puckReturn != PuckSignal::PuckReturn::ERROR) {
				// puck should be triggered on accept or error -> unexpected otherwise
				prioReturnVal.errorFlag = true;
				prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
			} else if (returnVal.puckReturn == PuckSignal::PuckReturn::ERROR) {
				prioReturnVal.errorFlag = true;
				prioReturnVal.errorSignal = ErrorSignal::PUCK_LOST; //Late Timer expiered
			}
		}
		it++;
	}
}

void PuckManager::handlePuckSignal(const PuckSignal::Signal &signal, int32_t &acceptCounter, int32_t &warningCounter, ManagerReturn &prioReturnVal) {
	LOG_DEBUG << "[PuckManager] Handle Puck Signal \n";
	// check the signal for every puck in the list
	if(puckList.empty()) {
		prioReturnVal.speedSignal = PuckSignal::PuckSpeed::STOP;
		prioReturnVal.errorFlag = true;
		prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
	}

	std::list<PuckContext*>::iterator it = puckList.begin();
	while (it != puckList.end()) {
		PuckSignal::Return returnVal = (*it)->process(signal);

		switch (returnVal.puckReturn) {
		case PuckSignal::PuckReturn::ACCEPT:
			acceptCounter++;
			break;
		case PuckSignal::PuckReturn::DELETE:
			acceptCounter++;
			LOG_DEBUG << "[Puck" + std::to_string((*it)->getPuckID()) + "] was deleted \n";
			delete *it;					// delete the puck from memory
			it = puckList.erase(it);	// delete the puck from list
			break;
		case PuckSignal::PuckReturn::SEND:
			acceptCounter++;
			prioReturnVal.puckType = new PuckSignal::PuckType((*it)->getType());
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::SEND_PUCK;
			break;
		case PuckSignal::PuckReturn::RECEIVED:
			acceptCounter++;
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::RECEIVED_PUCK;
			break;
		case PuckSignal::PuckReturn::EVALUATE:
			acceptCounter++;
			if (!sort.process((*it)->getType())) {
				prioReturnVal.actorFlag = true;
				prioReturnVal.actorSignal = ActorSignal::OPEN_SWITCH;
			}
			break;
		case PuckSignal::PuckReturn::START_HEIGHT:
			acceptCounter++;
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::START_MEASUREMENT;
			break;
		case PuckSignal::PuckReturn::STOP_HEIGHT:
			acceptCounter++;
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::STOP_MEASUREMENT;
			break;

		case PuckSignal::PuckReturn::SLIDE_EMPTY:
			acceptCounter++;

			sort.process(returnVal.puckReturn);
			prioReturnVal.actorFlag = true;
			prioReturnVal.actorSignal = ActorSignal::SEND_SLIDE_EMPTY;
			LOG_DEBUG << "[Puck" + std::to_string((*it)->getPuckID()) + "] was deleted \n";
			delete *it;					// delete the puck from memory
			it = puckList.erase(it);	// delete the puck from list
			break;
			//
		case PuckSignal::PuckReturn::WARNING:
			warningCounter++;
			break;
		case PuckSignal::PuckReturn::DENY:
			break;
		case PuckSignal::PuckReturn::ERROR:
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::PUCK_LOST;
			break;
		default:
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
			LOG_DEBUG << "[PuckManager] Returning with Default UNEXPECTED_SIGNAL";
			//return prioReturnVal;
		}

		if (returnVal.puckReturn != PuckSignal::PuckReturn::DELETE || returnVal.puckReturn != PuckSignal::PuckReturn::SLIDE_EMPTY) { //if a puck was deleted, you should not increment the iteratpr
			++it;
		}
	}

	prioReturnVal.speedSignal = getCurrentSpeed();

	/* No one Accepted the signal, so it is likely to be unexpected*/
	if(acceptCounter == 0) {
		prioReturnVal.errorFlag = true;
		prioReturnVal.errorSignal = ErrorSignal::NOT_ACCEPTED;
	}
}

bool PuckManager::passToPuckSort(const PuckSignal::Signal& signal, ManagerReturn& prioReturnVal) {
	LOG_DEBUG << "[PuckManager] Pass to Pucksort \n";
	if ( signal.signalType == PuckSignal::SignalType::SERIAL_SIGNAL
		 && (signal.serialSignal == Serial_n::ser_proto_msg::SLIDE_FULL_SER
		 || signal.serialSignal == Serial_n::ser_proto_msg::SLIDE_EMTPY_SER)){

		setErrorOnBothSlidesAreFull(prioReturnVal);
		sort.process(signal.serialSignal);
		LOG_DEBUG << "[PuckManager] Returning with with Slide management only \n";
		return true;
	} else {
		return false;
	}
}

bool PuckManager::checkErrorMetal(const PuckSignal::Signal& signal) {
	// metal detect hot fix
	return (signal.signalType == PuckSignal::SignalType::INTERRUPT_SIGNAL
			&& signal.interruptSignal
			== interrupts::interruptSignals::METAL_DETECT);
}

void PuckManager::setErrorOnBothSlidesAreFull(ManagerReturn &prioReturnVal) {
	bool conditionIsMet = sort.areBothSlidesFull();
	LOG_DEBUG << "[PuckManager] areBothSlidesFull: " << int(conditionIsMet) << endl;
	if (conditionIsMet) {
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::BOTH_SLIDES_FULL;
	}
}

bool PuckManager::checkSerialError(const PuckSignal::Signal& signal) {
	/* Accept serial RESUME and STOP signals */
	return (signal.signalType == PuckSignal::SignalType::SERIAL_SIGNAL
			&& (signal.serialSignal == Serial_n::ser_proto_msg::STOP_SER
			|| signal.serialSignal == Serial_n::ser_proto_msg::RESUME_SER));
}

PuckManager::ManagerReturn PuckManager::process(PuckSignal::Signal signal) {
	LOG_SCOPE;

	ManagerReturn prioReturnVal; // the prioritized return value
	prioReturnVal.speedSignal = PuckSignal::PuckSpeed::FAST;
	prioReturnVal.actorFlag = false;
	prioReturnVal.errorFlag = false;
	prioReturnVal.puckType = nullptr;

	int32_t acceptCounter = 0; // count all accepted signals
	int32_t warningCounter = 0; // count all warnings

	if(passToPuckSort(signal, prioReturnVal)){ //Only pucksort is intrested in signal
			prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed
			LOG_DEBUG << "[PuckManager] Returning with pass to puck sort only \n";
			return prioReturnVal;
	}

	prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed

#if !machine // machine0
	// check for first interrupt signal - puck must be created
	if(	signal.signalType == PuckSignal::SignalType::INTERRUPT_SIGNAL &&
		signal.interruptSignal == interrupts::interruptSignals::INLET_IN) {

		addPuck(new PuckContext(chid));
		prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed

		return prioReturnVal;
	}

	// signal can be passed for speed prio -> every puck should return deny
#endif

	// Pass the timer signal to the given puckID
	if(signal.signalType == PuckSignal::SignalType::TIMER_SIGNAL) {
		handlePuckTimer(signal, prioReturnVal);
		prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed
		LOG_DEBUG << "[PuckManager] Returning with with timer management only \n";
		return prioReturnVal;
	} else {
		// check the signal for every puck in the list
		handlePuckSignal(signal, acceptCounter, warningCounter, prioReturnVal);
	}

	/* Signal was unexpected for the pucks, might be expected somewhere else */
	if(prioReturnVal.errorFlag && prioReturnVal.errorSignal == ErrorSignal::NOT_ACCEPTED){
		if(checkErrorMetal(signal)){
			LOG_DEBUG << "[PuckManager] Error was from Metall \n";
			prioReturnVal.errorFlag = false;
		} else if(checkSerialError(signal)){
			LOG_DEBUG << "[PuckManager] Error was from Serial \n";
			prioReturnVal.errorFlag = false;
		} else {
			LOG_DEBUG << "[PuckManager] Transform NOT_ACCEPTED to UNEXPECTED_SIGNAL \n";
			prioReturnVal.errorSignal = ErrorSignal::UNEXPECTED_SIGNAL;
		}
	}

	/*if(!prioReturnVal.errorFlag) {
		if(acceptCounter > 1 || acceptCounter < 0) {
			LOG_DEBUG << "[PuckManager] Returning with MULTIPLE_ACCEPT";
			prioReturnVal.errorFlag = true;
			prioReturnVal.errorSignal = ErrorSignal::MULTIPLE_ACCEPT;
			return prioReturnVal;
		}
	}*/

	setErrorOnBothSlidesAreFull(prioReturnVal);

	if(puckList.empty()) {
		prioReturnVal.speedSignal = PuckSignal::PuckSpeed::STOP;
	}

	prioReturnVal.speedSignal = getCurrentSpeed(); //Always set speed

	LOG_DEBUG << "Puck Manager returns: Speed " << prioReturnVal.speedSignal << " Actor Flag " << prioReturnVal.actorFlag
			 << " Actor Signal " << prioReturnVal.actorSignal << " Error Flag " << prioReturnVal.errorFlag
			 << " Error Signal " << prioReturnVal.errorSignal << " \n";

	// everything OK
	return prioReturnVal;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 - 2018 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "types.hpp"

#ifdef DEBUG
#define OxError(x) x ? reinterpret_cast<uint64_t>(__FILE__) | _errorTags(__LINE__, x) : 0
#else
#define OxError(x) x
#endif

namespace ox {

using Error = uint64_t;

constexpr Error errCode(Error err) {
	return (err >> 58) & onMask<Error>(5);
}

struct ErrorInfo {
	const char *file = nullptr;
	int line = -1;
	Error errCode = 0;

	ErrorInfo() = default;

	ErrorInfo(Error err) {
		this->file = reinterpret_cast<const char*>(err & onMask<Error>(48));
		this->line = static_cast<int>((err >> 48) & onMask<Error>(11));
		this->errCode = ox::errCode(err);
	}
};

static constexpr Error _errorTags(Error line, Error errCode) {
	line &= onMask<Error>(11);
	line <<= 48;
	errCode &= onMask<Error>(5);
	errCode <<= 59;
	return errCode | line;
}

template<typename T>
struct ValErr {
	T value;
	Error error;

	inline constexpr ValErr() = default;

	inline constexpr ValErr(T value, Error error = 0): value(value), error(error) {
	}

	inline constexpr operator const T&() const {
		return value;
	}

	inline constexpr operator T&() {
		return value;
	}

	inline constexpr bool ok() const {
		return error == 0;
	}

};

}

<commit_msg>[ox/std] Add oxReturnError<commit_after>/*
 * Copyright 2015 - 2018 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "types.hpp"

#ifdef DEBUG
#define OxError(x) x ? reinterpret_cast<uint64_t>(__FILE__) | _errorTags(__LINE__, x) : 0
#else
#define OxError(x) x
#endif

#define oxReturnError(x) if (const auto err = x) return OxError(err)

namespace ox {

using Error = uint64_t;

constexpr Error errCode(Error err) {
	return (err >> 58) & onMask<Error>(5);
}

struct ErrorInfo {
	const char *file = nullptr;
	int line = -1;
	Error errCode = 0;

	ErrorInfo() = default;

	ErrorInfo(Error err) {
		this->file = reinterpret_cast<const char*>(err & onMask<Error>(48));
		this->line = static_cast<int>((err >> 48) & onMask<Error>(11));
		this->errCode = ox::errCode(err);
	}
};

static constexpr Error _errorTags(Error line, Error errCode) {
	line &= onMask<Error>(11);
	line <<= 48;
	errCode &= onMask<Error>(5);
	errCode <<= 59;
	return errCode | line;
}

template<typename T>
struct ValErr {
	T value;
	Error error;

	inline constexpr ValErr() = default;

	inline constexpr ValErr(T value, Error error = 0): value(value), error(error) {
	}

	inline constexpr operator const T&() const {
		return value;
	}

	inline constexpr operator T&() {
		return value;
	}

	inline constexpr bool ok() const {
		return error == 0;
	}

};

}

<|endoftext|>


<commit_before><commit_msg>Move the change workign directory before loading external loads, allowing the files to be found<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkClipOp.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "src/core/SkClipOpPriv.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"

#include <string.h>

namespace skiagm {

constexpr SkColor gPathColor = SK_ColorBLACK;
constexpr SkColor gClipAColor = SK_ColorBLUE;
constexpr SkColor gClipBColor = SK_ColorRED;

class ComplexClipGM : public GM {
public:
    ComplexClipGM(bool aaclip, bool saveLayer, bool invertDraw)
    : fDoAAClip(aaclip)
    , fDoSaveLayer(saveLayer)
    , fInvertDraw(invertDraw) {
        this->setBGColor(0xFFDEDFDE);
    }

protected:
    SkString onShortName() override {
        SkString str;
        str.printf("complexclip_%s%s%s",
                   fDoAAClip ? "aa" : "bw",
                   fDoSaveLayer ? "_layer" : "",
                   fInvertDraw ? "_invert" : "");
        return str;
    }

    SkISize onISize() override { return SkISize::Make(970, 780); }

    void onDraw(SkCanvas* canvas) override {
        SkPath path;
        path.moveTo(0,   50)
            .quadTo(0,   0,   50,  0)
            .lineTo(175, 0)
            .quadTo(200, 0,   200, 25)
            .lineTo(200, 150)
            .quadTo(200, 200, 150, 200)
            .lineTo(0,   200)
            .close()
            .moveTo(50,  50)
            .lineTo(150, 50)
            .lineTo(150, 125)
            .quadTo(150, 150, 125, 150)
            .lineTo(50,  150)
            .close();
        if (fInvertDraw) {
            path.setFillType(SkPathFillType::kInverseEvenOdd);
        } else {
            path.setFillType(SkPathFillType::kEvenOdd);
        }
        SkPaint pathPaint;
        pathPaint.setAntiAlias(true);
        pathPaint.setColor(gPathColor);

        SkPath clipA;
        clipA.addPoly({{10,  20}, {165, 22}, {70,  105}, {165, 177}, {-5,  180}}, false).close();

        SkPath clipB;
        clipB.addPoly({{40,  10}, {190, 15}, {195, 190}, {40,  185}, {155, 100}}, false).close();

        SkFont font(ToolUtils::create_portable_typeface(), 20);

        constexpr struct {
            SkClipOp fOp;
            const char*      fName;
        } gOps[] = { //extra spaces in names for measureText
            {kIntersect_SkClipOp,         "Isect "},
            {kDifference_SkClipOp,        "Diff " },
            {kUnion_SkClipOp,             "Union "},
            {kXOR_SkClipOp,               "Xor "  },
            {kReverseDifference_SkClipOp, "RDiff "}
        };

        canvas->translate(20, 20);
        canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4);

        if (fDoSaveLayer) {
            // We want the layer to appear symmetric relative to actual
            // device boundaries so we need to "undo" the effect of the
            // scale and translate
            SkRect bounds = SkRect::MakeLTRB(
              4.0f/3.0f * -20,
              4.0f/3.0f * -20,
              4.0f/3.0f * (this->getISize().fWidth - 20),
              4.0f/3.0f * (this->getISize().fHeight - 20));

            bounds.inset(100, 100);
            SkPaint boundPaint;
            boundPaint.setColor(SK_ColorRED);
            boundPaint.setStyle(SkPaint::kStroke_Style);
            canvas->drawRect(bounds, boundPaint);
            canvas->clipRect(bounds);
            canvas->saveLayer(&bounds, nullptr);
        }

        for (int invBits = 0; invBits < 4; ++invBits) {
            canvas->save();
            for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
                this->drawHairlines(canvas, path, clipA, clipB);

                bool doInvA = SkToBool(invBits & 1);
                bool doInvB = SkToBool(invBits & 2);
                canvas->save();
                    // set clip
                    clipA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
                                      SkPathFillType::kEvenOdd);
                    clipB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
                                      SkPathFillType::kEvenOdd);
                    canvas->clipPath(clipA, fDoAAClip);
                    canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip);

                    // In the inverse case we need to prevent the draw from covering the whole
                    // canvas.
                    if (fInvertDraw) {
                        SkRect rectClip = clipA.getBounds();
                        rectClip.join(path.getBounds());
                        rectClip.join(path.getBounds());
                        rectClip.outset(5, 5);
                        canvas->clipRect(rectClip);
                    }

                    // draw path clipped
                    canvas->drawPath(path, pathPaint);
                canvas->restore();

                SkPaint paint;
                SkScalar txtX = 45;
                paint.setColor(gClipAColor);
                const char* aTxt = doInvA ? "InvA " : "A ";
                canvas->drawSimpleText(aTxt, strlen(aTxt), SkTextEncoding::kUTF8, txtX, 220, font, paint);
                txtX += font.measureText(aTxt, strlen(aTxt), SkTextEncoding::kUTF8);
                paint.setColor(SK_ColorBLACK);
                canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8, txtX, 220,
                                       font, paint);
                txtX += font.measureText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8);
                paint.setColor(gClipBColor);
                const char* bTxt = doInvB ? "InvB " : "B ";
                canvas->drawSimpleText(bTxt, strlen(bTxt), SkTextEncoding::kUTF8, txtX, 220, font, paint);

                canvas->translate(250,0);
            }
            canvas->restore();
            canvas->translate(0, 250);
        }

        if (fDoSaveLayer) {
            canvas->restore();
        }
    }
private:
    void drawHairlines(SkCanvas* canvas, const SkPath& path,
                       const SkPath& clipA, const SkPath& clipB) {
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kStroke_Style);
        const SkAlpha fade = 0x33;

        // draw path in hairline
        paint.setColor(gPathColor); paint.setAlpha(fade);
        canvas->drawPath(path, paint);

        // draw clips in hair line
        paint.setColor(gClipAColor); paint.setAlpha(fade);
        canvas->drawPath(clipA, paint);
        paint.setColor(gClipBColor); paint.setAlpha(fade);
        canvas->drawPath(clipB, paint);
    }

    bool fDoAAClip;
    bool fDoSaveLayer;
    bool fInvertDraw;

    typedef GM INHERITED;
};

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

DEF_GM(return new ComplexClipGM(false, false, false);)
DEF_GM(return new ComplexClipGM(false, false, true);)
DEF_GM(return new ComplexClipGM(false, true, false);)
DEF_GM(return new ComplexClipGM(false, true, true);)
DEF_GM(return new ComplexClipGM(true, false, false);)
DEF_GM(return new ComplexClipGM(true, false, true);)
DEF_GM(return new ComplexClipGM(true, true, false);)
DEF_GM(return new ComplexClipGM(true, true, true);)
}

DEF_SIMPLE_GM(clip_shader, canvas, 840, 650) {
    auto img = GetResourceAsImage("images/yellow_rose.png");
    auto sh = img->makeShader();

    SkRect r = SkRect::MakeIWH(img->width(), img->height());
    SkPaint p;

    canvas->translate(10, 10);
    canvas->drawImage(img, 0, 0, nullptr);

    canvas->save();
    canvas->translate(img->width() + 10, 0);
    canvas->clipShader(sh, SkClipOp::kIntersect);
    p.setColor(SK_ColorRED);
    canvas->drawRect(r, p);
    canvas->restore();

    canvas->save();
    canvas->translate(0, img->height() + 10);
    canvas->clipShader(sh, SkClipOp::kDifference);
    p.setColor(SK_ColorGREEN);
    canvas->drawRect(r, p);
    canvas->restore();

    canvas->save();
    canvas->translate(img->width() + 10, img->height() + 10);
    canvas->clipShader(sh, SkClipOp::kIntersect);
    canvas->save();
    SkMatrix lm = SkMatrix::MakeScale(1.0f / 5);
    canvas->clipShader(img->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &lm));
    canvas->drawImage(img, 0, 0, nullptr);

    canvas->restore();
    canvas->restore();
}
<commit_msg>add gm for clipShader in a layer<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkClipOp.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "src/core/SkClipOpPriv.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"

#include <string.h>

namespace skiagm {

constexpr SkColor gPathColor = SK_ColorBLACK;
constexpr SkColor gClipAColor = SK_ColorBLUE;
constexpr SkColor gClipBColor = SK_ColorRED;

class ComplexClipGM : public GM {
public:
    ComplexClipGM(bool aaclip, bool saveLayer, bool invertDraw)
    : fDoAAClip(aaclip)
    , fDoSaveLayer(saveLayer)
    , fInvertDraw(invertDraw) {
        this->setBGColor(0xFFDEDFDE);
    }

protected:
    SkString onShortName() override {
        SkString str;
        str.printf("complexclip_%s%s%s",
                   fDoAAClip ? "aa" : "bw",
                   fDoSaveLayer ? "_layer" : "",
                   fInvertDraw ? "_invert" : "");
        return str;
    }

    SkISize onISize() override { return SkISize::Make(970, 780); }

    void onDraw(SkCanvas* canvas) override {
        SkPath path;
        path.moveTo(0,   50)
            .quadTo(0,   0,   50,  0)
            .lineTo(175, 0)
            .quadTo(200, 0,   200, 25)
            .lineTo(200, 150)
            .quadTo(200, 200, 150, 200)
            .lineTo(0,   200)
            .close()
            .moveTo(50,  50)
            .lineTo(150, 50)
            .lineTo(150, 125)
            .quadTo(150, 150, 125, 150)
            .lineTo(50,  150)
            .close();
        if (fInvertDraw) {
            path.setFillType(SkPathFillType::kInverseEvenOdd);
        } else {
            path.setFillType(SkPathFillType::kEvenOdd);
        }
        SkPaint pathPaint;
        pathPaint.setAntiAlias(true);
        pathPaint.setColor(gPathColor);

        SkPath clipA;
        clipA.addPoly({{10,  20}, {165, 22}, {70,  105}, {165, 177}, {-5,  180}}, false).close();

        SkPath clipB;
        clipB.addPoly({{40,  10}, {190, 15}, {195, 190}, {40,  185}, {155, 100}}, false).close();

        SkFont font(ToolUtils::create_portable_typeface(), 20);

        constexpr struct {
            SkClipOp fOp;
            const char*      fName;
        } gOps[] = { //extra spaces in names for measureText
            {kIntersect_SkClipOp,         "Isect "},
            {kDifference_SkClipOp,        "Diff " },
            {kUnion_SkClipOp,             "Union "},
            {kXOR_SkClipOp,               "Xor "  },
            {kReverseDifference_SkClipOp, "RDiff "}
        };

        canvas->translate(20, 20);
        canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4);

        if (fDoSaveLayer) {
            // We want the layer to appear symmetric relative to actual
            // device boundaries so we need to "undo" the effect of the
            // scale and translate
            SkRect bounds = SkRect::MakeLTRB(
              4.0f/3.0f * -20,
              4.0f/3.0f * -20,
              4.0f/3.0f * (this->getISize().fWidth - 20),
              4.0f/3.0f * (this->getISize().fHeight - 20));

            bounds.inset(100, 100);
            SkPaint boundPaint;
            boundPaint.setColor(SK_ColorRED);
            boundPaint.setStyle(SkPaint::kStroke_Style);
            canvas->drawRect(bounds, boundPaint);
            canvas->clipRect(bounds);
            canvas->saveLayer(&bounds, nullptr);
        }

        for (int invBits = 0; invBits < 4; ++invBits) {
            canvas->save();
            for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
                this->drawHairlines(canvas, path, clipA, clipB);

                bool doInvA = SkToBool(invBits & 1);
                bool doInvB = SkToBool(invBits & 2);
                canvas->save();
                    // set clip
                    clipA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
                                      SkPathFillType::kEvenOdd);
                    clipB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
                                      SkPathFillType::kEvenOdd);
                    canvas->clipPath(clipA, fDoAAClip);
                    canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip);

                    // In the inverse case we need to prevent the draw from covering the whole
                    // canvas.
                    if (fInvertDraw) {
                        SkRect rectClip = clipA.getBounds();
                        rectClip.join(path.getBounds());
                        rectClip.join(path.getBounds());
                        rectClip.outset(5, 5);
                        canvas->clipRect(rectClip);
                    }

                    // draw path clipped
                    canvas->drawPath(path, pathPaint);
                canvas->restore();

                SkPaint paint;
                SkScalar txtX = 45;
                paint.setColor(gClipAColor);
                const char* aTxt = doInvA ? "InvA " : "A ";
                canvas->drawSimpleText(aTxt, strlen(aTxt), SkTextEncoding::kUTF8, txtX, 220, font, paint);
                txtX += font.measureText(aTxt, strlen(aTxt), SkTextEncoding::kUTF8);
                paint.setColor(SK_ColorBLACK);
                canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8, txtX, 220,
                                       font, paint);
                txtX += font.measureText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8);
                paint.setColor(gClipBColor);
                const char* bTxt = doInvB ? "InvB " : "B ";
                canvas->drawSimpleText(bTxt, strlen(bTxt), SkTextEncoding::kUTF8, txtX, 220, font, paint);

                canvas->translate(250,0);
            }
            canvas->restore();
            canvas->translate(0, 250);
        }

        if (fDoSaveLayer) {
            canvas->restore();
        }
    }
private:
    void drawHairlines(SkCanvas* canvas, const SkPath& path,
                       const SkPath& clipA, const SkPath& clipB) {
        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kStroke_Style);
        const SkAlpha fade = 0x33;

        // draw path in hairline
        paint.setColor(gPathColor); paint.setAlpha(fade);
        canvas->drawPath(path, paint);

        // draw clips in hair line
        paint.setColor(gClipAColor); paint.setAlpha(fade);
        canvas->drawPath(clipA, paint);
        paint.setColor(gClipBColor); paint.setAlpha(fade);
        canvas->drawPath(clipB, paint);
    }

    bool fDoAAClip;
    bool fDoSaveLayer;
    bool fInvertDraw;

    typedef GM INHERITED;
};

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

DEF_GM(return new ComplexClipGM(false, false, false);)
DEF_GM(return new ComplexClipGM(false, false, true);)
DEF_GM(return new ComplexClipGM(false, true, false);)
DEF_GM(return new ComplexClipGM(false, true, true);)
DEF_GM(return new ComplexClipGM(true, false, false);)
DEF_GM(return new ComplexClipGM(true, false, true);)
DEF_GM(return new ComplexClipGM(true, true, false);)
DEF_GM(return new ComplexClipGM(true, true, true);)
}

DEF_SIMPLE_GM(clip_shader, canvas, 840, 650) {
    auto img = GetResourceAsImage("images/yellow_rose.png");
    auto sh = img->makeShader();

    SkRect r = SkRect::MakeIWH(img->width(), img->height());
    SkPaint p;

    canvas->translate(10, 10);
    canvas->drawImage(img, 0, 0, nullptr);

    canvas->save();
    canvas->translate(img->width() + 10, 0);
    canvas->clipShader(sh, SkClipOp::kIntersect);
    p.setColor(SK_ColorRED);
    canvas->drawRect(r, p);
    canvas->restore();

    canvas->save();
    canvas->translate(0, img->height() + 10);
    canvas->clipShader(sh, SkClipOp::kDifference);
    p.setColor(SK_ColorGREEN);
    canvas->drawRect(r, p);
    canvas->restore();

    canvas->save();
    canvas->translate(img->width() + 10, img->height() + 10);
    canvas->clipShader(sh, SkClipOp::kIntersect);
    canvas->save();
    SkMatrix lm = SkMatrix::MakeScale(1.0f / 5);
    canvas->clipShader(img->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, &lm));
    canvas->drawImage(img, 0, 0, nullptr);

    canvas->restore();
    canvas->restore();
}

DEF_SIMPLE_GM(clip_shader_layer, canvas, 430, 320) {
    auto img = GetResourceAsImage("images/yellow_rose.png");
    auto sh = img->makeShader();

    SkRect r = SkRect::MakeIWH(img->width(), img->height());

    canvas->translate(10, 10);
    // now add the cool clip
    canvas->clipRect(r);
    canvas->clipShader(sh);
    // now draw a layer with the same image, and watch it get restored w/ the clip
    canvas->saveLayer(&r, nullptr);
    canvas->drawColor(0xFFFF0000);
    canvas->restore();
}
<|endoftext|>