<commit_before>/* traqnsport.cc
   Mathieu Stefani, 02 July 2017

   TCP transport handling

*/

#include <sys/sendfile.h>
#include <sys/timerfd.h>

#include <pistache/transport.h>
#include <pistache/peer.h>
#include <pistache/tcp.h>
#include <pistache/os.h>

namespace Pistache {

using namespace Polling;

namespace Tcp {

Transport::Transport(const std::shared_ptr<Tcp::Handler>& handler) {
    init(handler);
}

void
Transport::init(const std::shared_ptr<Tcp::Handler>& handler) {
    handler_ = handler;
    handler_->associateTransport(this);
}

std::shared_ptr<Aio::Handler>
Transport::clone() const {
    return std::make_shared<Transport>(handler_->clone());
}

void
Transport::registerPoller(Polling::Epoll& poller) {
    writesQueue.bind(poller);
    timersQueue.bind(poller);
    peersQueue.bind(poller);
    notifier.bind(poller);
}

void
Transport::handleNewPeer(const std::shared_ptr<Tcp::Peer>& peer) {
    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    if (!isInRightThread) {
        PeerEntry entry(peer);
        peersQueue.push(std::move(entry));
    } else {
        handlePeer(peer);
    }
    int fd = peer->fd();
    {
        Guard guard(toWriteLock);
        toWrite.emplace(fd, std::deque<WriteEntry>{});
    }
}

void
Transport::onReady(const Aio::FdSet& fds) {
    for (const auto& entry: fds) {
        if (entry.getTag() == writesQueue.tag()) {
            handleWriteQueue();
        }
        else if (entry.getTag() == timersQueue.tag()) {
            handleTimerQueue();
        }
        else if (entry.getTag() == peersQueue.tag()) {
            handlePeerQueue();
        }
        else if (entry.getTag() == notifier.tag()) {
            handleNotify();
        }

        else if (entry.isReadable()) {
            auto tag = entry.getTag();
            if (isPeerFd(tag)) {
                auto& peer = getPeer(tag);
                handleIncoming(peer);
            } else if (isTimerFd(tag)) {
                auto it = timers.find(tag.value());
                auto& entry = it->second;
                handleTimer(std::move(entry));
                timers.erase(it->first);
            }
            else {
                throw std::runtime_error("Unknown fd");
            }

        }
        else if (entry.isWritable()) {
            auto tag = entry.getTag();
            auto fd = tag.value();

            {
                Guard guard(toWriteLock);
                auto it = toWrite.find(fd);
                if (it == std::end(toWrite)) {
                    throw std::runtime_error("Assertion Error: could not find write data");
                }
            }

            reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);

            // Try to drain the queue
            asyncWriteImpl(fd);
        }
    }
}

void
Transport::disarmTimer(Fd fd) {
    auto it = timers.find(fd);
    if (it == std::end(timers))
        throw std::runtime_error("Timer has not been armed");

    auto &entry = it->second;
    entry.disable();
}

void
Transport::handleIncoming(const std::shared_ptr<Peer>& peer) {
    char buffer[Const::MaxBuffer] = {0};

    ssize_t totalBytes = 0;
    int fd = peer->fd();

    for (;;) {

        ssize_t bytes;

#ifdef PISTACHE_USE_SSL
        if (peer->ssl() != NULL) {
            bytes = SSL_read((SSL *)peer->ssl(), buffer + totalBytes,
                Const::MaxBuffer - totalBytes);
        } else {
#endif /* PISTACHE_USE_SSL */
            bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
#ifdef PISTACHE_USE_SSL
        }
#endif /* PISTACHE_USE_SSL */

        if (bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                if (totalBytes > 0) {
                    handler_->onInput(buffer, totalBytes, peer);
                }
            } else {
                if (errno == ECONNRESET) {
                    handlePeerDisconnection(peer);
                }
                else {
                    throw std::runtime_error(strerror(errno));
                }
            }
            break;
        }
        else if (bytes == 0) {
            handlePeerDisconnection(peer);
            break;
        }

        else {
            handler_->onInput(buffer, bytes, peer);
        }
    }
}

void
Transport::handlePeerDisconnection(const std::shared_ptr<Peer>& peer) {
    handler_->onDisconnection(peer);

    int fd = peer->fd();
    auto it = peers.find(fd);
    if (it == std::end(peers))
        throw std::runtime_error("Could not find peer to erase");

#ifdef PISTACHE_USE_SSL
    if (peer->ssl() != NULL) {
        SSL_free((SSL *)peer->ssl());
        peer->associateSSL(NULL);
    }
#endif /* PISTACHE_USE_SSL */

    peers.erase(it->first);

    {
        // Clean up buffers
        Guard guard(toWriteLock);
        auto & wq = toWrite[fd];
        while (wq.size() > 0) {
            wq.pop_front();
        }
        toWrite.erase(fd);
    }

    close(fd);
}

void
Transport::asyncWriteImpl(Fd fd)
{
    bool stop = false;
    while (!stop) {
        Guard guard(toWriteLock);

        auto it = toWrite.find(fd);

        // cleanup will have been handled by handlePeerDisconnection
        if (it == std::end(toWrite)) { return; }
        auto & wq = it->second;
        if (wq.size() == 0) {
            break;
        }

        auto & entry = wq.front();
        int flags    = entry.flags;
        BufferHolder &buffer = entry.buffer;
        Async::Deferred<ssize_t> deferred = std::move(entry.deferred);

        auto cleanUp = [&]() {
            wq.pop_front();
            if (wq.size() == 0) {
                toWrite.erase(fd);
                reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);
                stop = true;
            }
        };

        size_t totalWritten = buffer.offset();
        for (;;) {
            ssize_t bytesWritten = 0;
            auto len = buffer.size() - totalWritten;

            if (buffer.isRaw()) {
                auto raw = buffer.raw();
                auto ptr = raw.data().c_str() + totalWritten;

#ifdef PISTACHE_USE_SSL
                auto it = peers.find(fd);

                if (it == std::end(peers))
                    throw std::runtime_error("No peer found for fd: " + std::to_string(fd));

                if (it->second->ssl() != NULL) {
                    bytesWritten = SSL_write((SSL *)it->second->ssl(), ptr, len);
                } else {
#endif /* PISTACHE_USE_SSL */
                    bytesWritten = ::send(fd, ptr, len, flags);
#ifdef PISTACHE_USE_SSL
                }
#endif /* PISTACHE_USE_SSL */
            } else {
                auto file = buffer.fd();
                off_t offset = totalWritten;
                bytesWritten = ::sendfile(fd, file, &offset, len);
            }
            if (bytesWritten < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {

                    auto bufferHolder = buffer.detach(totalWritten);

                    // pop_front kills buffer - so we cannot continue loop or use buffer after this point
                    wq.pop_front();
                    wq.push_front(WriteEntry(std::move(deferred), bufferHolder, flags));
                    reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
                }
                // EBADF can happen when the HTTP parser, in the case of
                // an error, closes fd before the entire request is processed.
                // https://github.com/oktal/pistache/issues/501
                else if (errno == EBADF) {
                    wq.pop_front();
                    toWrite.erase(fd);
                    stop = true;
                }
                else {
                    cleanUp();
                    deferred.reject(Pistache::Error::system("Could not write data"));
                }
                break;
            }
            else {
                totalWritten += bytesWritten;
                if (totalWritten >= buffer.size()) {
                    if (buffer.isFile()) {
                        // done with the file buffer, nothing else knows whether to
                        // close it with the way the code is written.
                        ::close(buffer.fd());
                    }

                    cleanUp();

                    // Cast to match the type of defered template
                    // to avoid a BadType exception
                    deferred.resolve(static_cast<ssize_t>(totalWritten));
                    break;
                }
            }
        }
    }
}

void
Transport::armTimerMs(
        Fd fd, std::chrono::milliseconds value,
        Async::Deferred<uint64_t> deferred) {

    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    TimerEntry entry(fd, value, std::move(deferred));

    if (!isInRightThread) {
        timersQueue.push(std::move(entry));
    } else {
        armTimerMsImpl(std::move(entry));
    }
}

void
Transport::armTimerMsImpl(TimerEntry entry) {

    auto it = timers.find(entry.fd);
    if (it != std::end(timers)) {
        entry.deferred.reject(std::runtime_error("Timer is already armed"));
        return;
    }

    itimerspec spec;
    spec.it_interval.tv_sec = 0;
    spec.it_interval.tv_nsec = 0;

    if (entry.value.count() < 1000) {
        spec.it_value.tv_sec = 0;
        spec.it_value.tv_nsec
            = std::chrono::duration_cast<std::chrono::nanoseconds>(entry.value).count();
    } else {
        spec.it_value.tv_sec
            = std::chrono::duration_cast<std::chrono::seconds>(entry.value).count();
        spec.it_value.tv_nsec = 0;
    }

    int res = timerfd_settime(entry.fd, 0, &spec, 0);
    if (res == -1) {
        entry.deferred.reject(Pistache::Error::system("Could not set timer time"));
        return;
    }

    reactor()->registerFdOneShot(key(), entry.fd, NotifyOn::Read, Polling::Mode::Edge);
    timers.insert(std::make_pair(entry.fd, std::move(entry)));
}

void
Transport::handleWriteQueue() {
    // Let's drain the queue
    for (;;) {
        auto write = writesQueue.popSafe();
        if (!write) break;

        auto fd = write->peerFd;
        if (!isPeerFd(fd)) continue;

        {
            Guard guard(toWriteLock);
            // don't enqueue anything to write if the peer has already
            // disconnected. Security and memory leaks occur if we do.
            if (peers.find(fd) == peers.end()) continue;
            toWrite[fd].push_back(std::move(*write));
        }

        reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
    }
}

void
Transport::handleTimerQueue() {
    for (;;) {
        auto timer = timersQueue.popSafe();
        if (!timer) break;

        armTimerMsImpl(std::move(*timer));
    }
}

void
Transport::handlePeerQueue() {
    for (;;) {
        auto data = peersQueue.popSafe();
        if (!data) break;

        handlePeer(data->peer);
    }
}

void
Transport::handlePeer(const std::shared_ptr<Peer>& peer) {
    int fd = peer->fd();
    peers.insert(std::make_pair(fd, peer));

    peer->associateTransport(this);

    handler_->onConnection(peer);
    reactor()->registerFd(key(), fd, NotifyOn::Read | NotifyOn::Shutdown, Polling::Mode::Edge);
}

void
Transport::handleNotify() {
    while (this->notifier.tryRead()) ;

    rusage now;

    auto res = getrusage(RUSAGE_THREAD, &now);
    if (res == -1)
        loadRequest_.reject(std::runtime_error("Could not compute usage"));

    loadRequest_.resolve(now);
    loadRequest_.clear();
}

void
Transport::handleTimer(TimerEntry entry) {
    if (entry.isActive()) {
        uint64_t numWakeups;
        int res = ::read(entry.fd, &numWakeups, sizeof numWakeups);
        if (res == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;
            else
                entry.deferred.reject(Pistache::Error::system("Could not read timerfd"));
        } else {
            if (res != sizeof(numWakeups)) {
                entry.deferred.reject(Pistache::Error("Read invalid number of bytes for timer fd: "
                            + std::to_string(entry.fd)));
            }
            else {
                entry.deferred.resolve(numWakeups);
            }
        }
    }
}

bool
Transport::isPeerFd(Fd fd) const {
    return peers.find(fd) != std::end(peers);
}

bool
Transport::isTimerFd(Fd fd) const {
    return timers.find(fd) != std::end(timers);
}

bool
Transport::isPeerFd(Polling::Tag tag) const {
    return isPeerFd(tag.value());
}
bool
Transport::isTimerFd(Polling::Tag tag) const {
    return isTimerFd(tag.value());
}

std::shared_ptr<Peer>&
Transport::getPeer(Fd fd)
{
    auto it = peers.find(fd);
    if (it == std::end(peers))
    {
        throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
    }
    return it->second;
}

std::shared_ptr<Peer>&
Transport::getPeer(Polling::Tag tag)
{
    return getPeer(tag.value());
}

} // namespace Tcp
} // namespace Pistache
<commit_msg>Backing out unintented change<commit_after>/* traqnsport.cc
   Mathieu Stefani, 02 July 2017

   TCP transport handling

*/

#include <sys/sendfile.h>
#include <sys/timerfd.h>

#include <pistache/transport.h>
#include <pistache/peer.h>
#include <pistache/tcp.h>
#include <pistache/os.h>

namespace Pistache {

using namespace Polling;

namespace Tcp {

Transport::Transport(const std::shared_ptr<Tcp::Handler>& handler) {
    init(handler);
}

void
Transport::init(const std::shared_ptr<Tcp::Handler>& handler) {
    handler_ = handler;
    handler_->associateTransport(this);
}

std::shared_ptr<Aio::Handler>
Transport::clone() const {
    return std::make_shared<Transport>(handler_->clone());
}

void
Transport::registerPoller(Polling::Epoll& poller) {
    writesQueue.bind(poller);
    timersQueue.bind(poller);
    peersQueue.bind(poller);
    notifier.bind(poller);
}

void
Transport::handleNewPeer(const std::shared_ptr<Tcp::Peer>& peer) {
    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    if (!isInRightThread) {
        PeerEntry entry(peer);
        peersQueue.push(std::move(entry));
    } else {
        handlePeer(peer);
    }
    int fd = peer->fd();
    {
        Guard guard(toWriteLock);
        toWrite.emplace(fd, std::deque<WriteEntry>{});
    }
}

void
Transport::onReady(const Aio::FdSet& fds) {
    for (const auto& entry: fds) {
        if (entry.getTag() == writesQueue.tag()) {
            handleWriteQueue();
        }
        else if (entry.getTag() == timersQueue.tag()) {
            handleTimerQueue();
        }
        else if (entry.getTag() == peersQueue.tag()) {
            handlePeerQueue();
        }
        else if (entry.getTag() == notifier.tag()) {
            handleNotify();
        }

        else if (entry.isReadable()) {
            auto tag = entry.getTag();
            if (isPeerFd(tag)) {
                auto& peer = getPeer(tag);
                handleIncoming(peer);
            } else if (isTimerFd(tag)) {
                auto it = timers.find(tag.value());
                auto& entry = it->second;
                handleTimer(std::move(entry));
                timers.erase(it->first);
            }
            else {
                throw std::runtime_error("Unknown fd");
            }

        }
        else if (entry.isWritable()) {
            auto tag = entry.getTag();
            auto fd = tag.value();

            {
                Guard guard(toWriteLock);
                auto it = toWrite.find(fd);
                if (it == std::end(toWrite)) {
                    throw std::runtime_error("Assertion Error: could not find write data");
                }
            }

            reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);

            // Try to drain the queue
            asyncWriteImpl(fd);
        }
    }
}

void
Transport::disarmTimer(Fd fd) {
    auto it = timers.find(fd);
    if (it == std::end(timers))
        throw std::runtime_error("Timer has not been armed");

    auto &entry = it->second;
    entry.disable();
}

void
Transport::handleIncoming(const std::shared_ptr<Peer>& peer) {
    char buffer[Const::MaxBuffer] = {0};

    ssize_t totalBytes = 0;
    int fd = peer->fd();

    for (;;) {

        ssize_t bytes;

#ifdef PISTACHE_USE_SSL
        if (peer->ssl() != NULL) {
            bytes = SSL_read((SSL *)peer->ssl(), buffer + totalBytes,
                Const::MaxBuffer - totalBytes);
        } else {
#endif /* PISTACHE_USE_SSL */
            bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
#ifdef PISTACHE_USE_SSL
        }
#endif /* PISTACHE_USE_SSL */

        if (bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                if (totalBytes > 0) {
                    handler_->onInput(buffer, totalBytes, peer);
                }
            } else {
                if (errno == ECONNRESET) {
                    handlePeerDisconnection(peer);
                }
                else {
                    throw std::runtime_error(strerror(errno));
                }
            }
            break;
        }
        else if (bytes == 0) {
            handlePeerDisconnection(peer);
            break;
        }

        else {
            handler_->onInput(buffer, bytes, peer);
        }
    }
}

void
Transport::handlePeerDisconnection(const std::shared_ptr<Peer>& peer) {
    handler_->onDisconnection(peer);

    int fd = peer->fd();
    auto it = peers.find(fd);
    if (it == std::end(peers))
        throw std::runtime_error("Could not find peer to erase");

#ifdef PISTACHE_USE_SSL
    if (peer->ssl() != NULL) {
        SSL_free((SSL *)peer->ssl());
        peer->associateSSL(NULL);
    }
#endif /* PISTACHE_USE_SSL */

    peers.erase(it->first);

    {
        // Clean up buffers
        Guard guard(toWriteLock);
        auto & wq = toWrite[fd];
        while (wq.size() > 0) {
            wq.pop_front();
        }
        toWrite.erase(fd);
    }

    close(fd);
}

void
Transport::asyncWriteImpl(Fd fd)
{
    bool stop = false;
    while (!stop) {
        Guard guard(toWriteLock);

        auto it = toWrite.find(fd);

        // cleanup will have been handled by handlePeerDisconnection
        if (it == std::end(toWrite)) { return; }
        auto & wq = it->second;
        if (wq.size() == 0) {
            break;
        }

        auto & entry = wq.front();
        int flags    = entry.flags;
        BufferHolder &buffer = entry.buffer;
        Async::Deferred<ssize_t> deferred = std::move(entry.deferred);

        auto cleanUp = [&]() {
            wq.pop_front();
            if (wq.size() == 0) {
                toWrite.erase(fd);
                reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);
                stop = true;
            }
        };

        size_t totalWritten = buffer.offset();
        for (;;) {
            ssize_t bytesWritten = 0;
            auto len = buffer.size() - totalWritten;

            if (buffer.isRaw()) {
                auto raw = buffer.raw();
                auto ptr = raw.data().c_str() + totalWritten;

#ifdef PISTACHE_USE_SSL
                auto it = peers.find(fd);

                if (it == std::end(peers))
                    throw std::runtime_error("No peer found for fd: " + std::to_string(fd));

                if (it->second->ssl() != NULL) {
                    bytesWritten = SSL_write((SSL *)it->second->ssl(), ptr, len);
                } else {
#endif /* PISTACHE_USE_SSL */
                    bytesWritten = ::send(fd, ptr, len, flags);
#ifdef PISTACHE_USE_SSL
                }
#endif /* PISTACHE_USE_SSL */
            } else {
                auto file = buffer.fd();
                off_t offset = totalWritten;
                bytesWritten = ::sendfile(fd, file, &offset, len);
            }
            if (bytesWritten < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {

                    auto bufferHolder = buffer.detach(totalWritten);

                    // pop_front kills buffer - so we cannot continue loop or use buffer after this point
                    wq.pop_front();
                    wq.push_front(WriteEntry(std::move(deferred), bufferHolder, flags));
                    reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
                }
                // EBADF can happen when the HTTP parser, in the case of
                // an error, closes fd before the entire request is processed.
                // https://github.com/oktal/pistache/issues/501
                else if (errno == EBADF) {
                    wq.pop_front();
                    toWrite.erase(fd);
                    stop = true;
                }
                else {
                    cleanUp();
                    deferred.reject(Pistache::Error::system("Could not write data"));
                }
                break;
            }
            else {
                totalWritten += bytesWritten;
                if (totalWritten >= buffer.size()) {
                    if (buffer.isFile()) {
                        // done with the file buffer, nothing else knows whether to
                        // close it with the way the code is written.
                        ::close(buffer.fd());
                    }

                    cleanUp();

                    // Cast to match the type of defered template
                    // to avoid a BadType exception
                    deferred.resolve(static_cast<ssize_t>(totalWritten));
                    break;
                }
            }
        }
    }
}

void
Transport::armTimerMs(
        Fd fd, std::chrono::milliseconds value,
        Async::Deferred<uint64_t> deferred) {

    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    TimerEntry entry(fd, value, std::move(deferred));

    if (!isInRightThread) {
        timersQueue.push(std::move(entry));
    } else {
        armTimerMsImpl(std::move(entry));
    }
}

void
Transport::armTimerMsImpl(TimerEntry entry) {

    auto it = timers.find(entry.fd);
    if (it != std::end(timers)) {
        entry.deferred.reject(std::runtime_error("Timer is already armed"));
        return;
    }

    itimerspec spec;
    spec.it_interval.tv_sec = 0;
    spec.it_interval.tv_nsec = 0;

    if (entry.value.count() < 1000) {
        spec.it_value.tv_sec = 0;
        spec.it_value.tv_nsec
            = std::chrono::duration_cast<std::chrono::nanoseconds>(entry.value).count();
    } else {
        spec.it_value.tv_sec
            = std::chrono::duration_cast<std::chrono::seconds>(entry.value).count();
        spec.it_value.tv_nsec = 0;
    }

    int res = timerfd_settime(entry.fd, 0, &spec, 0);
    if (res == -1) {
        entry.deferred.reject(Pistache::Error::system("Could not set timer time"));
        return;
    }

    reactor()->registerFdOneShot(key(), entry.fd, NotifyOn::Read, Polling::Mode::Edge);
    timers.insert(std::make_pair(entry.fd, std::move(entry)));
}

void
Transport::handleWriteQueue() {
    // Let's drain the queue
    for (;;) {
        auto write = writesQueue.popSafe();
        if (!write) break;

        auto fd = write->peerFd;
        if (!isPeerFd(fd)) continue;

        {
            Guard guard(toWriteLock);
            toWrite[fd].push_back(std::move(*write));
        }

        reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
    }
}

void
Transport::handleTimerQueue() {
    for (;;) {
        auto timer = timersQueue.popSafe();
        if (!timer) break;

        armTimerMsImpl(std::move(*timer));
    }
}

void
Transport::handlePeerQueue() {
    for (;;) {
        auto data = peersQueue.popSafe();
        if (!data) break;

        handlePeer(data->peer);
    }
}

void
Transport::handlePeer(const std::shared_ptr<Peer>& peer) {
    int fd = peer->fd();
    peers.insert(std::make_pair(fd, peer));

    peer->associateTransport(this);

    handler_->onConnection(peer);
    reactor()->registerFd(key(), fd, NotifyOn::Read | NotifyOn::Shutdown, Polling::Mode::Edge);
}

void
Transport::handleNotify() {
    while (this->notifier.tryRead()) ;

    rusage now;

    auto res = getrusage(RUSAGE_THREAD, &now);
    if (res == -1)
        loadRequest_.reject(std::runtime_error("Could not compute usage"));

    loadRequest_.resolve(now);
    loadRequest_.clear();
}

void
Transport::handleTimer(TimerEntry entry) {
    if (entry.isActive()) {
        uint64_t numWakeups;
        int res = ::read(entry.fd, &numWakeups, sizeof numWakeups);
        if (res == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;
            else
                entry.deferred.reject(Pistache::Error::system("Could not read timerfd"));
        } else {
            if (res != sizeof(numWakeups)) {
                entry.deferred.reject(Pistache::Error("Read invalid number of bytes for timer fd: "
                            + std::to_string(entry.fd)));
            }
            else {
                entry.deferred.resolve(numWakeups);
            }
        }
    }
}

bool
Transport::isPeerFd(Fd fd) const {
    return peers.find(fd) != std::end(peers);
}

bool
Transport::isTimerFd(Fd fd) const {
    return timers.find(fd) != std::end(timers);
}

bool
Transport::isPeerFd(Polling::Tag tag) const {
    return isPeerFd(tag.value());
}
bool
Transport::isTimerFd(Polling::Tag tag) const {
    return isTimerFd(tag.value());
}

std::shared_ptr<Peer>&
Transport::getPeer(Fd fd)
{
    auto it = peers.find(fd);
    if (it == std::end(peers))
    {
        throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
    }
    return it->second;
}

std::shared_ptr<Peer>&
Transport::getPeer(Polling::Tag tag)
{
    return getPeer(tag.value());
}

} // namespace Tcp
} // namespace Pistache
<|endoftext|>


<commit_before>#include <mlopen.h>
#include "test.hpp"
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <cmath>
#include <mlopen/tensor_extra.hpp>
#include <mlopen/manage_ptr.hpp>
#include <mlopen/returns.hpp>
#include <limits>

mlopenHandle_t global_handle;
struct handle_fixture
{
    mlopenHandle_t handle;
    cl_command_queue q;
    cl_context ctx;

    typedef MLOPEN_MANAGE_PTR(cl_mem, clReleaseMemObject) data_ptr;

    handle_fixture()
    {
        // mlopenCreate(&handle);
        handle = global_handle;
        mlopenGetStream(handle, &q);
        auto status = clGetCommandQueueInfo(q, CL_QUEUE_CONTEXT, sizeof(cl_context), &ctx, nullptr);
        if (status != CL_SUCCESS) throw status;
    }

    template<class T>
    data_ptr create(int sz)
    {
        cl_int status = CL_SUCCESS;
        auto result = data_ptr{clCreateBuffer(ctx, CL_MEM_READ_ONLY, sizeof(T)*sz, nullptr, &status)};
        if (status != CL_SUCCESS) throw status;
        return std::move(result);
    }

    template<class Container, class DataPtr>
    data_ptr write(const Container& c, DataPtr&& data, int sz)
    {
        assert(sz == c.size()*sizeof(typename Container::value_type));
        cl_int status = CL_SUCCESS;
        status = clEnqueueWriteBuffer(q, data.get(), CL_TRUE, 0, sz, c.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return std::move(data);
    }

    template<class Container>
    data_ptr write(const Container& c)
    {
        typedef typename Container::value_type type;
        return this->write(c, this->create<type>(c.size()), c.size()*sizeof(type));
    }

    template<class T>
    std::vector<T> read(const data_ptr& data, int sz)
    {
        std::vector<T> result(sz);
        auto status = clEnqueueReadBuffer(q, data.get(), CL_TRUE, 0, sizeof(T)*sz, result.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return result;
    }

    ~handle_fixture()
    {
        // mlopenDestroy(handle);
    }
};

template<class F>
struct protect_fn
{
    F f;
    protect_fn(F x) : f(std::move(x))
    {}

    template<class... Ts>
    auto operator()(Ts&&... xs) const MLOPEN_RETURNS
    (f(std::forward<Ts>(xs)...));
};

template<class F>
protect_fn<F> protect(F f)
{
    return {std::move(f)};
}

// Multidimensional for loop
struct ford_impl
{
    template<class F, class T>
    void operator()(F f, T x) const
    {
        for(T i=0;i<x;i++) f(i);
    }

    template<class F, class T, class... Ts>
    void operator()(F f, T x, Ts... xs) const
    {
#if (defined(__GNUC__) && !defined (__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9)
        // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55914
        // This reverses the order of evaluation
        (*this)([&](Ts... is)
        {
            (*this)(std::bind(protect(f), std::placeholders::_1, is...), x);
        }, xs...);
#else
        (*this)([&](T i)
        {
            (*this)([&](Ts... is)
            {
                f(i, is...);
            }, xs...);
        }, x);
#endif

    }
};

template<class... Ts>
auto ford(Ts... xs) MLOPEN_RETURNS
(
    std::bind(ford_impl{}, std::placeholders::_1, xs...)
);

template<class T>
struct tensor
{
    MLOPEN_MANAGE_PTR(mlopenTensorDescriptor_t, mlopenDestroyTensorDescriptor) desc;
    std::vector<T> data;

    tensor(int n, int c, int h, int w)
    : desc(nullptr), data(n*c*h*w)
    {
        mlopenTensorDescriptor_t local;
        mlopenCreateTensorDescriptor(&local);
        desc.reset(local);
        mlopenSet4dTensorDescriptor(
                local,
                mlopenFloat, // TODO: Pick correct type
                n,
                c,
                h,
                w);
    }

    mlopenTensorDescriptor_t get() const
    {
        return desc.get();
    }

    template<class G>
    tensor& generate(G g) &
    {
        this->generate_impl(g);
        return *this;
    }

    template<class G>
    tensor&& generate(G g) &&
    {
        this->generate_impl(g);
        return std::move(*this);
    }

    template<class G>
    void generate_impl(G g)
    {
        auto iterator = data.begin();
        this->for_each([&](int i, int j, int k, int m)
        {
            assert(iterator < data.end());
            *iterator = g(i, j, k, m);
            ++iterator;
        });
    }

    template<class F>
    void for_each(F f) const
    {
        int n, c, h, w;
        mlopenGet4dTensorDescriptorLengths(desc.get(), &n, &c, &h, &w);
        ford(n, c, h, w)(std::move(f));
    }

    std::tuple<int, int, int, int> get_lengths() const
    {
        int n_in, c_in, h_in, w_in;
        int nStride_in, cStride_in, hStride_in, wStride_in;
        mlopenDataType_t dt;
        mlopenGet4dTensorDescriptor(
                desc.get(),
                &dt,
                &n_in,
                &c_in,
                &h_in,
                &w_in,
                &nStride_in,
                &cStride_in,
                &hStride_in,
                &wStride_in);

        return std::make_tuple(n_in, c_in, h_in, w_in);
    }

    int index(int n, int c, int h, int w) const
    {
        return mlopenGetTensorIndex(desc.get(), {n, c, h, w});
    }

    T& operator()(int n, int c, int h, int w)
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }

    const T& operator()(int n, int c, int h, int w) const
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }
};

struct tensor_generate
{
    template<class Tensor, class G>
    Tensor&& operator()(Tensor&& t, G g) const
    {
        return std::forward<Tensor>(t.generate(g));
    }
};

template<mlopenConvolutionMode_t Mode>
struct conv_filter
{
    MLOPEN_MANAGE_PTR(mlopenConvolutionDescriptor_t, mlopenDestroyConvolutionDescriptor) filter;

    conv_filter(int padh = 0, int padw = 0, int u = 1, int v = 1, int upx = 1, int upy = 1)
    {
        mlopenConvolutionDescriptor_t local;
        mlopenCreateConvolutionDescriptor(&local);
        filter.reset(local);
        mlopenInitConvolutionDescriptor(local,
                Mode,
                padh,
                padw,
                u,
                v,
                upx,
                upy);
    }

    mlopenConvolutionDescriptor_t get() const
    {
        return this->filter.get();
    }

    template<class T>
    tensor<T> get_output(const tensor<T>& input, const tensor<T>& weights) const
    {
        int x, y, z, a;
        mlopenGetConvolutionForwardOutputDim(this->filter.get(), input.get(), weights.get(), &x, &y, &z, &a);
        return {x, y, z, a};
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_cpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);

    int in_n, in_c, in_h, in_w;
    std::tie(in_n, in_c, in_h, in_w) = input.get_lengths();

    int wei_h, wei_w;
    std::tie(std::ignore, std::ignore, wei_h, wei_w) = weights.get_lengths();

    int pad_w, pad_h, u, v, upx, upy;
    mlopenConvolutionMode_t mode = mlopenConvolution;
    mlopenGetConvolutionDescriptor(filter.get(), &mode, &pad_h, &pad_w, &u, &v, &upx, &upy);

    out.for_each([&](int o, int w, int i, int j)
    {
        int in_off_h = i * v;
        int in_off_w = j * u;

        T acc = 0;
        for(int k = 0; k < in_c; k++) { // in_channels (RGB)
            for(int x = 0; x < wei_h; x++) {
                int in_x = in_off_h - pad_h + x;
                if(in_x >= 0 && in_x < in_h) {
                    for(int y = 0; y < wei_w; y++) {
                        int in_y = in_off_w - pad_w + y;
                        if(in_y >= 0 && in_y < in_w) {
                            acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                        }
                    }
                }
            }
        }
        out(o, w, i, j) = acc+bias;
    });
    return out.data;
}

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_gpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);
    handle_fixture handle;

    auto in_dev = handle.write(input.data);
    auto wei_dev = handle.write(weights.data);
    auto out_dev = handle.create<T>(out.data.size());

    int ret_algo_count;
    mlopenConvAlgoPerf_t perf;

    int alpha = 1, beta = 1;

    mlopenFindConvolutionForwardAlgorithm(handle.handle,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        out.get(),
        out_dev.get(),
        1,
        &ret_algo_count,
        &perf,
        mlopenConvolutionFastest,
        NULL,
        10,
		0); // MD: Not performing exhaustiveSearch by default for now

    mlopenConvolutionForward(handle.handle,
        &alpha,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        mlopenConvolutionFwdAlgoDirect,
        &beta,
        out.get(),
        out_dev.get(),
        NULL,
        0);

    out.data = handle.read<T>(out_dev, out.data.size());
    return out.data;
}

struct float_equal_fn
{
    template<class T>
    bool operator()(T x, T y) const
    {
        return std::fabs(x - y) < std::numeric_limits<T>::epsilon() * std::max(x, y);
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> verify_forward_conv(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out_cpu = forward_conv_cpu(input, weights, filter, bias);
    auto out_gpu = forward_conv_gpu(input, weights, filter, bias);
    CHECK(std::distance(out_cpu.begin(), out_cpu.end()) == std::distance(out_gpu.begin(), out_gpu.end()));
    CHECK(std::equal(out_cpu.begin(), out_cpu.end(), out_gpu.begin(), float_equal_fn{}));
    // CHECK(out_cpu == out_gpu);
}
struct verify_forward_conv_gen
{
    template<class T, mlopenConvolutionMode_t Mode, class G1, class G2>
    std::vector<T> operator()(tensor<T>& input, G1 g1, tensor<T>& weights, G2 g2, const conv_filter<Mode>& filter, int bias = 0) const
    {
        input.generate(g1);
        weights.generate(g2);
        verify_forward_conv(input, weights, filter, bias);
    }
};

template<class F, class... Ts>
void each_args(F f, Ts&&... xs)
{
    std::initializer_list<int>{(f(std::forward<Ts>(xs)), 0)...};
}

struct cross_args_apply
{
    template<class F, class T, class... Ts>
    void operator()(F f, T&& x, Ts&&... xs) const
    {
        each_args(std::bind(f, std::forward<T>(x), std::placeholders::_1), std::forward<Ts>(xs)...);
    }
};

template<class F, class... Ts>
void cross_args(F f, Ts&&... xs)
{
    each_args(
        std::bind(cross_args_apply{}, protect(std::move(f)), std::placeholders::_1, std::forward<Ts>(xs)...),
    std::forward<Ts>(xs)...);
}

template<class T, class G>
void verify_one(G g)
{
    auto input = tensor<float>{16, 32, 8, 8}.generate(g);
    auto weights = tensor<float>{64, 32, 5, 5}.generate(g);
    conv_filter<mlopenConvolution> filter{1, 1};
    verify_forward_conv(input, weights, filter);
}

template<class T>
struct verify_both
{
    template<class G1, class G2>
    void operator()(G1 g1, G2 g2) const
    {
        ford(8,8,8,8,8,8)([&](int padh, int padw, int u, int v, int upx, int upy)
        {
            conv_filter<mlopenConvolution> filter{padh, padw, u+1, v+1, upx+1, upy+1};
            ford(8,8,8,8)([&](int x1, int y1, int x2, int y2)
            {
                if (x1 >= x2 && y1 >= y2)
                {
                    auto input = tensor<float>{5, 16, x1+1, y1+1}.generate(g1);
                    auto weights = tensor<float>{8, 16, x2+1, y2+1}.generate(g2);
                    conv_filter<mlopenConvolution> filter{};
                    verify_forward_conv(input, weights, filter);
                }
            });
        });
    }
};

template<class T, class... Gs>
void verify_all(Gs... gs)
{
    cross_args(verify_both<T>{}, gs...);
}

int main() {
    mlopenCreate(&global_handle);
    auto g0 = [](int, int, int, int) { return 0; };
    auto g1 = [](int, int, int, int) { return 1; };
    auto g_id = [](int n, int c, int h, int w) { return h == w ? 1 : 0; };
    auto g = [](int n, int c, int h, int w) { return n+c+h+w; };

#if MLOPEN_TEST_ALL
    printf("verify_all\n");
    verify_all<float>(g0,g1, g_id, g);
#else
    printf("verify_one\n");
    verify_one<float>(g);
#endif

    mlopenDestroy(global_handle);
}
<commit_msg>Fix invalid free<commit_after>#include <mlopen.h>
#include "test.hpp"
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <cmath>
#include <mlopen/tensor_extra.hpp>
#include <mlopen/manage_ptr.hpp>
#include <mlopen/returns.hpp>
#include <limits>

mlopenHandle_t global_handle;
struct handle_fixture
{
    mlopenHandle_t handle;
    cl_command_queue q;
    cl_context ctx;

    typedef MLOPEN_MANAGE_PTR(cl_mem, clReleaseMemObject) data_ptr;

    handle_fixture()
    {
        // mlopenCreate(&handle);
        handle = global_handle;
        mlopenGetStream(handle, &q);
        auto status = clGetCommandQueueInfo(q, CL_QUEUE_CONTEXT, sizeof(cl_context), &ctx, nullptr);
        if (status != CL_SUCCESS) throw status;
    }

    template<class T>
    data_ptr create(int sz)
    {
        cl_int status = CL_SUCCESS;
        auto result = data_ptr{clCreateBuffer(ctx, CL_MEM_READ_ONLY, sizeof(T)*sz, nullptr, &status)};
        if (status != CL_SUCCESS) throw status;
        return std::move(result);
    }

    template<class Container, class DataPtr>
    data_ptr write(const Container& c, DataPtr&& data, int sz)
    {
        assert(sz == c.size()*sizeof(typename Container::value_type));
        cl_int status = CL_SUCCESS;
        status = clEnqueueWriteBuffer(q, data.get(), CL_TRUE, 0, sz, c.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return std::move(data);
    }

    template<class Container>
    data_ptr write(const Container& c)
    {
        typedef typename Container::value_type type;
        return this->write(c, this->create<type>(c.size()), c.size()*sizeof(type));
    }

    template<class T>
    std::vector<T> read(const data_ptr& data, int sz)
    {
        std::vector<T> result(sz);
        auto status = clEnqueueReadBuffer(q, data.get(), CL_TRUE, 0, sizeof(T)*sz, result.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return result;
    }

    ~handle_fixture()
    {
        // mlopenDestroy(handle);
    }
};

template<class F>
struct protect_fn
{
    F f;
    protect_fn(F x) : f(std::move(x))
    {}

    template<class... Ts>
    auto operator()(Ts&&... xs) const MLOPEN_RETURNS
    (f(std::forward<Ts>(xs)...));
};

template<class F>
protect_fn<F> protect(F f)
{
    return {std::move(f)};
}

// Multidimensional for loop
struct ford_impl
{
    template<class F, class T>
    void operator()(F f, T x) const
    {
        for(T i=0;i<x;i++) f(i);
    }

    template<class F, class T, class... Ts>
    void operator()(F f, T x, Ts... xs) const
    {
#if (defined(__GNUC__) && !defined (__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9)
        // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55914
        // This reverses the order of evaluation
        (*this)([&](Ts... is)
        {
            (*this)(std::bind(protect(f), std::placeholders::_1, is...), x);
        }, xs...);
#else
        (*this)([&](T i)
        {
            (*this)([&](Ts... is)
            {
                f(i, is...);
            }, xs...);
        }, x);
#endif

    }
};

template<class... Ts>
auto ford(Ts... xs) MLOPEN_RETURNS
(
    std::bind(ford_impl{}, std::placeholders::_1, xs...)
);

template<class T>
struct tensor
{
    MLOPEN_MANAGE_PTR(mlopenTensorDescriptor_t, mlopenDestroyTensorDescriptor) desc;
    std::vector<T> data;

    tensor(int n, int c, int h, int w)
    : desc(nullptr), data(n*c*h*w)
    {
        mlopenTensorDescriptor_t local;
        mlopenCreateTensorDescriptor(&local);
        desc.reset(local);
        mlopenSet4dTensorDescriptor(
                local,
                mlopenFloat, // TODO: Pick correct type
                n,
                c,
                h,
                w);
    }

    mlopenTensorDescriptor_t get() const
    {
        return desc.get();
    }

    template<class G>
    tensor& generate(G g) &
    {
        this->generate_impl(g);
        return *this;
    }

    template<class G>
    tensor&& generate(G g) &&
    {
        this->generate_impl(g);
        return std::move(*this);
    }

    template<class G>
    void generate_impl(G g)
    {
        auto iterator = data.begin();
        this->for_each([&](int i, int j, int k, int m)
        {
            assert(iterator < data.end());
            *iterator = g(i, j, k, m);
            ++iterator;
        });
    }

    template<class F>
    void for_each(F f) const
    {
        int n, c, h, w;
        mlopenGet4dTensorDescriptorLengths(desc.get(), &n, &c, &h, &w);
        ford(n, c, h, w)(std::move(f));
    }

    std::tuple<int, int, int, int> get_lengths() const
    {
        int n_in, c_in, h_in, w_in;
        int nStride_in, cStride_in, hStride_in, wStride_in;
        mlopenDataType_t dt;
        mlopenGet4dTensorDescriptor(
                desc.get(),
                &dt,
                &n_in,
                &c_in,
                &h_in,
                &w_in,
                &nStride_in,
                &cStride_in,
                &hStride_in,
                &wStride_in);

        return std::make_tuple(n_in, c_in, h_in, w_in);
    }

    int index(int n, int c, int h, int w) const
    {
        return mlopenGetTensorIndex(desc.get(), {n, c, h, w});
    }

    T& operator()(int n, int c, int h, int w)
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }

    const T& operator()(int n, int c, int h, int w) const
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }
};

struct tensor_generate
{
    template<class Tensor, class G>
    Tensor&& operator()(Tensor&& t, G g) const
    {
        return std::forward<Tensor>(t.generate(g));
    }
};

template<mlopenConvolutionMode_t Mode>
struct conv_filter
{
    MLOPEN_MANAGE_PTR(mlopenConvolutionDescriptor_t, mlopenDestroyConvolutionDescriptor) filter;

    conv_filter(int padh = 0, int padw = 0, int u = 1, int v = 1, int upx = 1, int upy = 1)
    {
        mlopenConvolutionDescriptor_t local;
        mlopenCreateConvolutionDescriptor(&local);
        filter.reset(local);
        mlopenInitConvolutionDescriptor(local,
                Mode,
                padh,
                padw,
                u,
                v,
                upx,
                upy);
    }

    mlopenConvolutionDescriptor_t get() const
    {
        return this->filter.get();
    }

    template<class T>
    tensor<T> get_output(const tensor<T>& input, const tensor<T>& weights) const
    {
        int x, y, z, a;
        mlopenGetConvolutionForwardOutputDim(this->filter.get(), input.get(), weights.get(), &x, &y, &z, &a);
        return {x, y, z, a};
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_cpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);

    int in_n, in_c, in_h, in_w;
    std::tie(in_n, in_c, in_h, in_w) = input.get_lengths();

    int wei_h, wei_w;
    std::tie(std::ignore, std::ignore, wei_h, wei_w) = weights.get_lengths();

    int pad_w, pad_h, u, v, upx, upy;
    mlopenConvolutionMode_t mode = mlopenConvolution;
    mlopenGetConvolutionDescriptor(filter.get(), &mode, &pad_h, &pad_w, &u, &v, &upx, &upy);

    out.for_each([&](int o, int w, int i, int j)
    {
        int in_off_h = i * v;
        int in_off_w = j * u;

        T acc = 0;
        for(int k = 0; k < in_c; k++) { // in_channels (RGB)
            for(int x = 0; x < wei_h; x++) {
                int in_x = in_off_h - pad_h + x;
                if(in_x >= 0 && in_x < in_h) {
                    for(int y = 0; y < wei_w; y++) {
                        int in_y = in_off_w - pad_w + y;
                        if(in_y >= 0 && in_y < in_w) {
                            acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                        }
                    }
                }
            }
        }
        out(o, w, i, j) = acc+bias;
    });
    return out.data;
}

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_gpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);
    handle_fixture handle;

    auto in_dev = handle.write(input.data);
    auto wei_dev = handle.write(weights.data);
    auto out_dev = handle.create<T>(out.data.size());

    int ret_algo_count;
    mlopenConvAlgoPerf_t perf;

    int alpha = 1, beta = 1;

    mlopenFindConvolutionForwardAlgorithm(handle.handle,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        out.get(),
        out_dev.get(),
        1,
        &ret_algo_count,
        &perf,
        mlopenConvolutionFastest,
        NULL,
        10,
		0); // MD: Not performing exhaustiveSearch by default for now

    mlopenConvolutionForward(handle.handle,
        &alpha,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        mlopenConvolutionFwdAlgoDirect,
        &beta,
        out.get(),
        out_dev.get(),
        NULL,
        0);

    out.data = handle.read<T>(out_dev, out.data.size());
    return out.data;
}

struct float_equal_fn
{
    template<class T>
    bool operator()(T x, T y) const
    {
        return std::fabs(x - y) < std::numeric_limits<T>::epsilon() * std::max(x, y);
    }
};

template<class T, mlopenConvolutionMode_t Mode>
void verify_forward_conv(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out_cpu = forward_conv_cpu(input, weights, filter, bias);
    auto out_gpu = forward_conv_gpu(input, weights, filter, bias);
    CHECK(std::distance(out_cpu.begin(), out_cpu.end()) == std::distance(out_gpu.begin(), out_gpu.end()));
    CHECK(std::equal(out_cpu.begin(), out_cpu.end(), out_gpu.begin(), float_equal_fn{}));
    // CHECK(out_cpu == out_gpu);
}
struct verify_forward_conv_gen
{
    template<class T, mlopenConvolutionMode_t Mode, class G1, class G2>
    std::vector<T> operator()(tensor<T>& input, G1 g1, tensor<T>& weights, G2 g2, const conv_filter<Mode>& filter, int bias = 0) const
    {
        input.generate(g1);
        weights.generate(g2);
        verify_forward_conv(input, weights, filter, bias);
    }
};

template<class F, class... Ts>
void each_args(F f, Ts&&... xs)
{
    std::initializer_list<int>{(f(std::forward<Ts>(xs)), 0)...};
}

struct cross_args_apply
{
    template<class F, class T, class... Ts>
    void operator()(F f, T&& x, Ts&&... xs) const
    {
        each_args(std::bind(f, std::forward<T>(x), std::placeholders::_1), std::forward<Ts>(xs)...);
    }
};

template<class F, class... Ts>
void cross_args(F f, Ts&&... xs)
{
    each_args(
        std::bind(cross_args_apply{}, protect(std::move(f)), std::placeholders::_1, std::forward<Ts>(xs)...),
    std::forward<Ts>(xs)...);
}

template<class T, class G>
void verify_one(G g)
{
    auto input = tensor<float>{16, 32, 8, 8}.generate(g);
    auto weights = tensor<float>{64, 32, 5, 5}.generate(g);
    conv_filter<mlopenConvolution> filter{1, 1};
    verify_forward_conv(input, weights, filter);
}

template<class T>
struct verify_both
{
    template<class G1, class G2>
    void operator()(G1 g1, G2 g2) const
    {
        ford(8,8,8,8,8,8)([&](int padh, int padw, int u, int v, int upx, int upy)
        {
            conv_filter<mlopenConvolution> filter{padh, padw, u+1, v+1, upx+1, upy+1};
            ford(8,8,8,8)([&](int x1, int y1, int x2, int y2)
            {
                if (x1 >= x2 && y1 >= y2)
                {
                    auto input = tensor<float>{5, 16, x1+1, y1+1}.generate(g1);
                    auto weights = tensor<float>{8, 16, x2+1, y2+1}.generate(g2);
                    conv_filter<mlopenConvolution> filter{};
                    verify_forward_conv(input, weights, filter);
                }
            });
        });
    }
};

template<class T, class... Gs>
void verify_all(Gs... gs)
{
    cross_args(verify_both<T>{}, gs...);
}

int main() {
    mlopenCreate(&global_handle);
    auto g0 = [](int, int, int, int) { return 0; };
    auto g1 = [](int, int, int, int) { return 1; };
    auto g_id = [](int n, int c, int h, int w) { return h == w ? 1 : 0; };
    auto g = [](int n, int c, int h, int w) { return n+c+h+w; };

#if MLOPEN_TEST_ALL
    printf("verify_all\n");
    verify_all<float>(g0,g1, g_id, g);
#else
    printf("verify_one\n");
    verify_one<float>(g);
#endif

    mlopenDestroy(global_handle);
}
<|endoftext|>


<commit_before>#include <mlopen.h>
#include "test.hpp"
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <cmath>
#include <mlopen/tensor_extra.hpp>
#include <mlopen/manage_ptr.hpp>
#include <mlopen/returns.hpp>
#include <limits>

mlopenHandle_t global_handle;
struct handle_fixture
{
    mlopenHandle_t handle;
    cl_command_queue q;
    cl_context ctx;

    typedef MLOPEN_MANAGE_PTR(cl_mem, clReleaseMemObject) data_ptr;

    handle_fixture()
    {
        // mlopenCreate(&handle);
        handle = global_handle;
        mlopenGetStream(handle, &q);
        auto status = clGetCommandQueueInfo(q, CL_QUEUE_CONTEXT, sizeof(cl_context), &ctx, nullptr);
        if (status != CL_SUCCESS) throw status;
    }

    template<class T>
    data_ptr create(int sz)
    {
        cl_int status = CL_SUCCESS;
        auto result = data_ptr{clCreateBuffer(ctx, CL_MEM_READ_ONLY, sizeof(T)*sz, nullptr, &status)};
        if (status != CL_SUCCESS) throw status;
        return std::move(result);
    }

    template<class Container, class DataPtr>
    data_ptr write(const Container& c, DataPtr&& data, int sz)
    {
        assert(sz == c.size()*sizeof(typename Container::value_type));
        cl_int status = CL_SUCCESS;
        status = clEnqueueWriteBuffer(q, data.get(), CL_TRUE, 0, sz, c.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return std::move(data);
    }

    template<class Container>
    data_ptr write(const Container& c)
    {
        typedef typename Container::value_type type;
        return this->write(c, this->create<type>(c.size()), c.size()*sizeof(type));
    }

    template<class T>
    std::vector<T> read(const data_ptr& data, int sz)
    {
        std::vector<T> result(sz);
        auto status = clEnqueueReadBuffer(q, data.get(), CL_TRUE, 0, sizeof(T)*sz, result.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return result;
    }

    ~handle_fixture()
    {
        // mlopenDestroy(handle);
    }
};

template<class F>
struct protect_fn
{
    F f;
    protect_fn(F x) : f(std::move(x))
    {}

    template<class... Ts>
    auto operator()(Ts&&... xs) const MLOPEN_RETURNS
    (f(std::forward<Ts>(xs)...));
};

template<class F>
protect_fn<F> protect(F f)
{
    return {std::move(f)};
}

// Multidimensional for loop
struct ford_impl
{
    template<class F, class T>
    void operator()(F f, T x) const
    {
        for(T i=0;i<x;i++) f(i);
    }

    template<class F, class T, class... Ts>
    void operator()(F f, T x, Ts... xs) const
    {
#if (defined(__GNUC__) && !defined (__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9)
        // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55914
        // This reverses the order of evaluation
        (*this)([&](Ts... is)
        {
            (*this)(std::bind(protect(f), std::placeholders::_1, is...), x);
        }, xs...);
#else
        (*this)([&](T i)
        {
            (*this)([&](Ts... is)
            {
                f(i, is...);
            }, xs...);
        }, x);
#endif

    }
};

template<class... Ts>
auto ford(Ts... xs) MLOPEN_RETURNS
(
    std::bind(ford_impl{}, std::placeholders::_1, xs...)
);

template<class T>
struct tensor
{
    MLOPEN_MANAGE_PTR(mlopenTensorDescriptor_t, mlopenDestroyTensorDescriptor) desc;
    std::vector<T> data;

    tensor(int n, int c, int h, int w)
    : desc(nullptr), data(n*c*h*w)
    {
        mlopenTensorDescriptor_t local;
        mlopenCreateTensorDescriptor(&local);
        desc.reset(local);
        mlopenSet4dTensorDescriptor(
                local,
                mlopenFloat, // TODO: Pick correct type
                n,
                c,
                h,
                w);
    }

    mlopenTensorDescriptor_t get() const
    {
        return desc.get();
    }

    template<class G>
    tensor& generate(G g) &
    {
        this->generate_impl(g);
        return *this;
    }

    template<class G>
    tensor&& generate(G g) &&
    {
        this->generate_impl(g);
        return std::move(*this);
    }

    template<class G>
    void generate_impl(G g)
    {
        auto iterator = data.begin();
        this->for_each([&](int i, int j, int k, int m)
        {
            assert(iterator < data.end());
            *iterator = g(i, j, k, m);
            ++iterator;
        });
    }

    template<class F>
    void for_each(F f) const
    {
        int n, c, h, w;
        mlopenGet4dTensorDescriptorLengths(desc.get(), &n, &c, &h, &w);
        ford(n, c, h, w)(std::move(f));
    }

    std::tuple<int, int, int, int> get_lengths() const
    {
        int n_in, c_in, h_in, w_in;
        int nStride_in, cStride_in, hStride_in, wStride_in;
        mlopenDataType_t dt;
        mlopenGet4dTensorDescriptor(
                desc.get(),
                &dt,
                &n_in,
                &c_in,
                &h_in,
                &w_in,
                &nStride_in,
                &cStride_in,
                &hStride_in,
                &wStride_in);

        return std::make_tuple(n_in, c_in, h_in, w_in);
    }

    int index(int n, int c, int h, int w) const
    {
        return mlopenGetTensorIndex(desc.get(), {n, c, h, w});
    }

    T& operator()(int n, int c, int h, int w)
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }

    const T& operator()(int n, int c, int h, int w) const
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }
};

struct tensor_generate
{
    template<class Tensor, class G>
    Tensor&& operator()(Tensor&& t, G g) const
    {
        return std::forward<Tensor>(t.generate(g));
    }
};

template<mlopenConvolutionMode_t Mode>
struct conv_filter
{
    MLOPEN_MANAGE_PTR(mlopenConvolutionDescriptor_t, mlopenDestroyConvolutionDescriptor) filter;

    conv_filter(int padh = 0, int padw = 0, int u = 1, int v = 1, int upx = 1, int upy = 1)
    {
        mlopenConvolutionDescriptor_t local;
        mlopenCreateConvolutionDescriptor(&local);
        filter.reset(local);
        mlopenInitConvolutionDescriptor(local,
                Mode,
                padh,
                padw,
                u,
                v,
                upx,
                upy);
    }

    mlopenConvolutionDescriptor_t get() const
    {
        return this->filter.get();
    }

    template<class T>
    tensor<T> get_output(const tensor<T>& input, const tensor<T>& weights) const
    {
        int x, y, z, a;
        mlopenGetConvolutionForwardOutputDim(this->filter.get(), input.get(), weights.get(), &x, &y, &z, &a);
        return {x, y, z, a};
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_cpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);

    int in_n, in_c, in_h, in_w;
    std::tie(in_n, in_c, in_h, in_w) = input.get_lengths();

    int wei_h, wei_w;
    std::tie(std::ignore, std::ignore, wei_h, wei_w) = weights.get_lengths();

    int pad_w, pad_h, u, v, upx, upy;
    mlopenConvolutionMode_t mode = mlopenConvolution;
    mlopenGetConvolutionDescriptor(filter.get(), &mode, &pad_h, &pad_w, &u, &v, &upx, &upy);

    out.for_each([&](int o, int w, int i, int j)
    {
        int in_off_h = i * v;
        int in_off_w = j * u;

        T acc = 0;
        for(int k = 0; k < in_c; k++) { // in_channels (RGB)
            for(int x = 0; x < wei_h; x++) {
                int in_x = in_off_h - pad_h + x;
                if(in_x >= 0 && in_x < in_h) {
                    for(int y = 0; y < wei_w; y++) {
                        int in_y = in_off_w - pad_w + y;
                        if(in_y >= 0 && in_y < in_w) {
                            acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                        }
                    }
                }
            }
        }
        out(o, w, i, j) = acc+bias;
    });
    return out.data;
}

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_gpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);
    handle_fixture handle;

    auto in_dev = handle.write(input.data);
    auto wei_dev = handle.write(weights.data);
    auto out_dev = handle.create<T>(out.data.size());

    int ret_algo_count;
    mlopenConvAlgoPerf_t perf;

    int alpha = 1, beta = 1;

    mlopenFindConvolutionForwardAlgorithm(handle.handle,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        out.get(),
        out_dev.get(),
        1,
        &ret_algo_count,
        &perf,
        mlopenConvolutionFastest,
        NULL,
        10,
		0); // MD: Not performing exhaustiveSearch by default for now

    mlopenConvolutionForward(handle.handle,
        &alpha,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        mlopenConvolutionFwdAlgoDirect,
        &beta,
        out.get(),
        out_dev.get(),
        NULL,
        0);

    out.data = handle.read<T>(out_dev, out.data.size());
    return out.data;
}

struct float_equal_fn
{
    template<class T>
    bool operator()(T x, T y) const
    {
        return std::fabs(x - y) < std::numeric_limits<T>::epsilon() * std::max(x, y);
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> verify_forward_conv(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out_cpu = forward_conv_cpu(input, weights, filter, bias);
    auto out_gpu = forward_conv_gpu(input, weights, filter, bias);
    CHECK(std::distance(out_cpu.begin(), out_cpu.end()) == std::distance(out_gpu.begin(), out_gpu.end()));
    CHECK(std::equal(out_cpu.begin(), out_cpu.end(), out_gpu.begin(), float_equal_fn{}));
    // CHECK(out_cpu == out_gpu);
}
struct verify_forward_conv_gen
{
    template<class T, mlopenConvolutionMode_t Mode, class G1, class G2>
    std::vector<T> operator()(tensor<T>& input, G1 g1, tensor<T>& weights, G2 g2, const conv_filter<Mode>& filter, int bias = 0) const
    {
        input.generate(g1);
        weights.generate(g2);
        verify_forward_conv(input, weights, filter, bias);
    }
};

template<class F, class... Ts>
void each_args(F f, Ts&&... xs)
{
    std::initializer_list<int>{(f(std::forward<Ts>(xs)), 0)...};
}

struct cross_args_apply
{
    template<class F, class T, class... Ts>
    void operator()(F f, T&& x, Ts&&... xs) const
    {
        each_args(std::bind(f, std::forward<T>(x), std::placeholders::_1), std::forward<Ts>(xs)...);
    }
};

template<class F, class... Ts>
void cross_args(F f, Ts&&... xs)
{
    each_args(
        std::bind(cross_args_apply{}, protect(std::move(f)), std::placeholders::_1, std::forward<Ts>(xs)...),
    std::forward<Ts>(xs)...);
}

template<class T, class G>
void verify_one(G g)
{
    auto input = tensor<float>{5, 16, 8, 8}.generate(g);
    auto weights = tensor<float>{8, 16, 5, 5}.generate(g);
    conv_filter<mlopenConvolution> filter{};
    verify_forward_conv(input, weights, filter);
}

template<class T>
struct verify_both
{
    template<class G1, class G2>
    void operator()(G1 g1, G2 g2) const
    {
        ford(8,8,8,8,8,8)([&](int padh, int padw, int u, int v, int upx, int upy)
        {
            conv_filter<mlopenConvolution> filter{padh, padw, u+1, v+1, upx+1, upy+1};
            ford(8,8,8,8)([&](int x1, int y1, int x2, int y2)
            {
                if (x1 >= x2 && y1 >= y2)
                {
                    auto input = tensor<float>{5, 16, x1+1, y1+1}.generate(g1);
                    auto weights = tensor<float>{8, 16, x2+1, y2+1}.generate(g2);
                    conv_filter<mlopenConvolution> filter{};
                    verify_forward_conv(input, weights, filter);
                }
            });
        });
    }
};

template<class T, class... Gs>
void verify_all(Gs... gs)
{
    cross_args(verify_both<T>{}, gs...);
}

int main() {
    mlopenCreate(&global_handle);
    auto g0 = [](int, int, int, int) { return 0; };
    auto g1 = [](int, int, int, int) { return 1; };
    auto g_id = [](int n, int c, int h, int w) { return h == w ? 1 : 0; };
    auto g = [](int n, int c, int h, int w) { return n+c+h+w; };

#if MLOPEN_TEST_ALL
    printf("verify_all\n");
    verify_all<float>(g0,g1, g_id, g);
#else
    printf("verify_one\n");
    verify_one<float>(g);
#endif

    mlopenDestroy(global_handle);
}
<commit_msg>changed network configs<commit_after>#include <mlopen.h>
#include "test.hpp"
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <cmath>
#include <mlopen/tensor_extra.hpp>
#include <mlopen/manage_ptr.hpp>
#include <mlopen/returns.hpp>
#include <limits>

mlopenHandle_t global_handle;
struct handle_fixture
{
    mlopenHandle_t handle;
    cl_command_queue q;
    cl_context ctx;

    typedef MLOPEN_MANAGE_PTR(cl_mem, clReleaseMemObject) data_ptr;

    handle_fixture()
    {
        // mlopenCreate(&handle);
        handle = global_handle;
        mlopenGetStream(handle, &q);
        auto status = clGetCommandQueueInfo(q, CL_QUEUE_CONTEXT, sizeof(cl_context), &ctx, nullptr);
        if (status != CL_SUCCESS) throw status;
    }

    template<class T>
    data_ptr create(int sz)
    {
        cl_int status = CL_SUCCESS;
        auto result = data_ptr{clCreateBuffer(ctx, CL_MEM_READ_ONLY, sizeof(T)*sz, nullptr, &status)};
        if (status != CL_SUCCESS) throw status;
        return std::move(result);
    }

    template<class Container, class DataPtr>
    data_ptr write(const Container& c, DataPtr&& data, int sz)
    {
        assert(sz == c.size()*sizeof(typename Container::value_type));
        cl_int status = CL_SUCCESS;
        status = clEnqueueWriteBuffer(q, data.get(), CL_TRUE, 0, sz, c.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return std::move(data);
    }

    template<class Container>
    data_ptr write(const Container& c)
    {
        typedef typename Container::value_type type;
        return this->write(c, this->create<type>(c.size()), c.size()*sizeof(type));
    }

    template<class T>
    std::vector<T> read(const data_ptr& data, int sz)
    {
        std::vector<T> result(sz);
        auto status = clEnqueueReadBuffer(q, data.get(), CL_TRUE, 0, sizeof(T)*sz, result.data(), 0, nullptr, nullptr);
        if (status != CL_SUCCESS) throw status;
        return result;
    }

    ~handle_fixture()
    {
        // mlopenDestroy(handle);
    }
};

template<class F>
struct protect_fn
{
    F f;
    protect_fn(F x) : f(std::move(x))
    {}

    template<class... Ts>
    auto operator()(Ts&&... xs) const MLOPEN_RETURNS
    (f(std::forward<Ts>(xs)...));
};

template<class F>
protect_fn<F> protect(F f)
{
    return {std::move(f)};
}

// Multidimensional for loop
struct ford_impl
{
    template<class F, class T>
    void operator()(F f, T x) const
    {
        for(T i=0;i<x;i++) f(i);
    }

    template<class F, class T, class... Ts>
    void operator()(F f, T x, Ts... xs) const
    {
#if (defined(__GNUC__) && !defined (__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ < 9)
        // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55914
        // This reverses the order of evaluation
        (*this)([&](Ts... is)
        {
            (*this)(std::bind(protect(f), std::placeholders::_1, is...), x);
        }, xs...);
#else
        (*this)([&](T i)
        {
            (*this)([&](Ts... is)
            {
                f(i, is...);
            }, xs...);
        }, x);
#endif

    }
};

template<class... Ts>
auto ford(Ts... xs) MLOPEN_RETURNS
(
    std::bind(ford_impl{}, std::placeholders::_1, xs...)
);

template<class T>
struct tensor
{
    MLOPEN_MANAGE_PTR(mlopenTensorDescriptor_t, mlopenDestroyTensorDescriptor) desc;
    std::vector<T> data;

    tensor(int n, int c, int h, int w)
    : desc(nullptr), data(n*c*h*w)
    {
        mlopenTensorDescriptor_t local;
        mlopenCreateTensorDescriptor(&local);
        desc.reset(local);
        mlopenSet4dTensorDescriptor(
                local,
                mlopenFloat, // TODO: Pick correct type
                n,
                c,
                h,
                w);
    }

    mlopenTensorDescriptor_t get() const
    {
        return desc.get();
    }

    template<class G>
    tensor& generate(G g) &
    {
        this->generate_impl(g);
        return *this;
    }

    template<class G>
    tensor&& generate(G g) &&
    {
        this->generate_impl(g);
        return std::move(*this);
    }

    template<class G>
    void generate_impl(G g)
    {
        auto iterator = data.begin();
        this->for_each([&](int i, int j, int k, int m)
        {
            assert(iterator < data.end());
            *iterator = g(i, j, k, m);
            ++iterator;
        });
    }

    template<class F>
    void for_each(F f) const
    {
        int n, c, h, w;
        mlopenGet4dTensorDescriptorLengths(desc.get(), &n, &c, &h, &w);
        ford(n, c, h, w)(std::move(f));
    }

    std::tuple<int, int, int, int> get_lengths() const
    {
        int n_in, c_in, h_in, w_in;
        int nStride_in, cStride_in, hStride_in, wStride_in;
        mlopenDataType_t dt;
        mlopenGet4dTensorDescriptor(
                desc.get(),
                &dt,
                &n_in,
                &c_in,
                &h_in,
                &w_in,
                &nStride_in,
                &cStride_in,
                &hStride_in,
                &wStride_in);

        return std::make_tuple(n_in, c_in, h_in, w_in);
    }

    int index(int n, int c, int h, int w) const
    {
        return mlopenGetTensorIndex(desc.get(), {n, c, h, w});
    }

    T& operator()(int n, int c, int h, int w)
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }

    const T& operator()(int n, int c, int h, int w) const
    {
        assert(this->index(n, c, h, w) < data.size());
        return this->data[this->index(n, c, h, w)];
    }
};

struct tensor_generate
{
    template<class Tensor, class G>
    Tensor&& operator()(Tensor&& t, G g) const
    {
        return std::forward<Tensor>(t.generate(g));
    }
};

template<mlopenConvolutionMode_t Mode>
struct conv_filter
{
    MLOPEN_MANAGE_PTR(mlopenConvolutionDescriptor_t, mlopenDestroyConvolutionDescriptor) filter;

    conv_filter(int padh = 0, int padw = 0, int u = 1, int v = 1, int upx = 1, int upy = 1)
    {
        mlopenConvolutionDescriptor_t local;
        mlopenCreateConvolutionDescriptor(&local);
        filter.reset(local);
        mlopenInitConvolutionDescriptor(local,
                Mode,
                padh,
                padw,
                u,
                v,
                upx,
                upy);
    }

    mlopenConvolutionDescriptor_t get() const
    {
        return this->filter.get();
    }

    template<class T>
    tensor<T> get_output(const tensor<T>& input, const tensor<T>& weights) const
    {
        int x, y, z, a;
        mlopenGetConvolutionForwardOutputDim(this->filter.get(), input.get(), weights.get(), &x, &y, &z, &a);
        return {x, y, z, a};
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_cpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);

    int in_n, in_c, in_h, in_w;
    std::tie(in_n, in_c, in_h, in_w) = input.get_lengths();

    int wei_h, wei_w;
    std::tie(std::ignore, std::ignore, wei_h, wei_w) = weights.get_lengths();

    int pad_w, pad_h, u, v, upx, upy;
    mlopenConvolutionMode_t mode = mlopenConvolution;
    mlopenGetConvolutionDescriptor(filter.get(), &mode, &pad_h, &pad_w, &u, &v, &upx, &upy);

    out.for_each([&](int o, int w, int i, int j)
    {
        int in_off_h = i * v;
        int in_off_w = j * u;

        T acc = 0;
        for(int k = 0; k < in_c; k++) { // in_channels (RGB)
            for(int x = 0; x < wei_h; x++) {
                int in_x = in_off_h - pad_h + x;
                if(in_x >= 0 && in_x < in_h) {
                    for(int y = 0; y < wei_w; y++) {
                        int in_y = in_off_w - pad_w + y;
                        if(in_y >= 0 && in_y < in_w) {
                            acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                        }
                    }
                }
            }
        }
        out(o, w, i, j) = acc+bias;
    });
    return out.data;
}

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> forward_conv_gpu(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out = filter.get_output(input, weights);
    handle_fixture handle;

    auto in_dev = handle.write(input.data);
    auto wei_dev = handle.write(weights.data);
    auto out_dev = handle.create<T>(out.data.size());

    int ret_algo_count;
    mlopenConvAlgoPerf_t perf;

    int alpha = 1, beta = 1;

    mlopenFindConvolutionForwardAlgorithm(handle.handle,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        out.get(),
        out_dev.get(),
        1,
        &ret_algo_count,
        &perf,
        mlopenConvolutionFastest,
        NULL,
        10,
		0); // MD: Not performing exhaustiveSearch by default for now

    mlopenConvolutionForward(handle.handle,
        &alpha,
        input.get(),
        in_dev.get(),
        weights.get(),
        wei_dev.get(),
        filter.get(),
        mlopenConvolutionFwdAlgoDirect,
        &beta,
        out.get(),
        out_dev.get(),
        NULL,
        0);

    out.data = handle.read<T>(out_dev, out.data.size());
    return out.data;
}

struct float_equal_fn
{
    template<class T>
    bool operator()(T x, T y) const
    {
        return std::fabs(x - y) < std::numeric_limits<T>::epsilon() * std::max(x, y);
    }
};

template<class T, mlopenConvolutionMode_t Mode>
std::vector<T> verify_forward_conv(const tensor<T>& input, const tensor<T>& weights, const conv_filter<Mode>& filter, int bias = 0)
{
    auto out_cpu = forward_conv_cpu(input, weights, filter, bias);
    auto out_gpu = forward_conv_gpu(input, weights, filter, bias);
    CHECK(std::distance(out_cpu.begin(), out_cpu.end()) == std::distance(out_gpu.begin(), out_gpu.end()));
    CHECK(std::equal(out_cpu.begin(), out_cpu.end(), out_gpu.begin(), float_equal_fn{}));
    // CHECK(out_cpu == out_gpu);
}
struct verify_forward_conv_gen
{
    template<class T, mlopenConvolutionMode_t Mode, class G1, class G2>
    std::vector<T> operator()(tensor<T>& input, G1 g1, tensor<T>& weights, G2 g2, const conv_filter<Mode>& filter, int bias = 0) const
    {
        input.generate(g1);
        weights.generate(g2);
        verify_forward_conv(input, weights, filter, bias);
    }
};

template<class F, class... Ts>
void each_args(F f, Ts&&... xs)
{
    std::initializer_list<int>{(f(std::forward<Ts>(xs)), 0)...};
}

struct cross_args_apply
{
    template<class F, class T, class... Ts>
    void operator()(F f, T&& x, Ts&&... xs) const
    {
        each_args(std::bind(f, std::forward<T>(x), std::placeholders::_1), std::forward<Ts>(xs)...);
    }
};

template<class F, class... Ts>
void cross_args(F f, Ts&&... xs)
{
    each_args(
        std::bind(cross_args_apply{}, protect(std::move(f)), std::placeholders::_1, std::forward<Ts>(xs)...),
    std::forward<Ts>(xs)...);
}

template<class T, class G>
void verify_one(G g)
{
    auto input = tensor<float>{16, 32, 8, 8}.generate(g);
    auto weights = tensor<float>{64, 32, 5, 5}.generate(g);
    conv_filter<mlopenConvolution> filter{1, 1};
    verify_forward_conv(input, weights, filter);
}

template<class T>
struct verify_both
{
    template<class G1, class G2>
    void operator()(G1 g1, G2 g2) const
    {
        ford(8,8,8,8,8,8)([&](int padh, int padw, int u, int v, int upx, int upy)
        {
            conv_filter<mlopenConvolution> filter{padh, padw, u+1, v+1, upx+1, upy+1};
            ford(8,8,8,8)([&](int x1, int y1, int x2, int y2)
            {
                if (x1 >= x2 && y1 >= y2)
                {
                    auto input = tensor<float>{5, 16, x1+1, y1+1}.generate(g1);
                    auto weights = tensor<float>{8, 16, x2+1, y2+1}.generate(g2);
                    conv_filter<mlopenConvolution> filter{};
                    verify_forward_conv(input, weights, filter);
                }
            });
        });
    }
};

template<class T, class... Gs>
void verify_all(Gs... gs)
{
    cross_args(verify_both<T>{}, gs...);
}

int main() {
    mlopenCreate(&global_handle);
    auto g0 = [](int, int, int, int) { return 0; };
    auto g1 = [](int, int, int, int) { return 1; };
    auto g_id = [](int n, int c, int h, int w) { return h == w ? 1 : 0; };
    auto g = [](int n, int c, int h, int w) { return n+c+h+w; };

#if MLOPEN_TEST_ALL
    printf("verify_all\n");
    verify_all<float>(g0,g1, g_id, g);
#else
    printf("verify_one\n");
    verify_one<float>(g);
#endif

    mlopenDestroy(global_handle);
}
<|endoftext|>


<commit_before>/// \file RemindSaveDlg.cpp
//-----------------------------------------------------------------------------

#include "stdafx.h"
#include "PasswordSafe.h"
#include "RemindSaveDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//-----------------------------------------------------------------------------
CRemindSaveDlg::CRemindSaveDlg(CWnd* pParent)
   : CDialog(CRemindSaveDlg::IDD, pParent)
{
   m_dontask = app.GetProfileInt("", "dontasksaveminimize", FALSE);
}

void CRemindSaveDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   DDX_Check(pDX, IDC_CLEARCHECK, m_dontask);
}

BEGIN_MESSAGE_MAP(CRemindSaveDlg, CDialog)
END_MESSAGE_MAP()

void CRemindSaveDlg::OnCancel()
{
   UpdateData(TRUE);
   app.WriteProfileInt("", "dontasksaveminimize", m_dontask);
   CDialog::OnCancel();
}

void CRemindSaveDlg::OnOK()
{
   UpdateData(TRUE);
   app.WriteProfileInt("", "dontasksaveminimize", m_dontask);
   CDialog::OnOK();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
<commit_msg>ThisMfcApp header<commit_after>/// \file RemindSaveDlg.cpp
//-----------------------------------------------------------------------------

#include "stdafx.h"
#include "PasswordSafe.h"

#include "ThisMfcApp.h"
#include "resource.h"

#include "RemindSaveDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//-----------------------------------------------------------------------------
CRemindSaveDlg::CRemindSaveDlg(CWnd* pParent)
   : CDialog(CRemindSaveDlg::IDD, pParent)
{
   m_dontask = app.GetProfileInt("", "dontasksaveminimize", FALSE);
}

void CRemindSaveDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   DDX_Check(pDX, IDC_CLEARCHECK, m_dontask);
}

BEGIN_MESSAGE_MAP(CRemindSaveDlg, CDialog)
END_MESSAGE_MAP()

void CRemindSaveDlg::OnCancel()
{
   UpdateData(TRUE);
   app.WriteProfileInt("", "dontasksaveminimize", m_dontask);
   CDialog::OnCancel();
}

void CRemindSaveDlg::OnOK()
{
   UpdateData(TRUE);
   app.WriteProfileInt("", "dontasksaveminimize", m_dontask);
   CDialog::OnOK();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
<|endoftext|>


<commit_before>/**
@file aux.hpp
@brief Auxiliary stdlib specializations.

@author Timothy Howard
@copyright 2013-2014 Timothy Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#pragma once

#include <Onsang/config.hpp>

#include <string>
#include <vector>
#include <deque>
#include <map>
#include <unordered_map>
#include <unordered_set>

namespace Onsang {
namespace aux {

/**
	@addtogroup etc
	@{
*/
/**
	@addtogroup aux
	@{
*/

/**
	@c std::basic_string<CharT, Traits>.
*/
template<
	typename CharT,
	class Traits = std::char_traits<CharT>
>
using basic_string
= std::basic_string<
	CharT, Traits,
	ONSANG_AUX_ALLOCATOR<CharT>
>;

/**
	@c std::vector<T>.
*/
template<
	typename T
>
using vector
= std::vector<
	T,
	ONSANG_AUX_ALLOCATOR<T>
>;

/**
	@c std::deque<T>.
*/
template<
	typename T
>
using deque
= std::deque<
	T,
	ONSANG_AUX_ALLOCATOR<T>
>;

/**
	@c std::unordered_map<Key, T, Hash, KeyEqual>.
*/
template<
	typename Key,
	typename T,
	class Compare = std::less<Key>
>
using map
= std::map<
	Key, T, Compare,
	ONSANG_AUX_ALLOCATOR<std::pair<Key const, T> >
>;

/**
	@c std::unordered_map<Key, T, Hash, KeyEqual>.
*/
template<
	typename Key,
	typename T,
	class Hash = std::hash<Key>,
	class KeyEqual = std::equal_to<Key>
>
using unordered_map
= std::unordered_map<
	Key, T, Hash, KeyEqual,
	ONSANG_AUX_ALLOCATOR<std::pair<Key const, T> >
>;

/**
	@c std::unordered_set<Key, Hash, KeyEqual>.
*/
template<
	typename Key,
	class Hash = std::hash<Key>,
	class KeyEqual = std::equal_to<Key>
>
using unordered_set
= std::unordered_set<
	Key, Hash, KeyEqual,
	ONSANG_AUX_ALLOCATOR<Key>
>;

/** @} */ // end of doc-group aux
/** @} */ // end of doc-group etc

} // namespace aux
} // namespace Onsang
<commit_msg>aux: added <memory> aliases.<commit_after>/**
@file aux.hpp
@brief Auxiliary stdlib specializations.

@author Timothy Howard
@copyright 2013-2014 Timothy Howard under the MIT license;
see @ref index or the accompanying LICENSE file for full text.
*/

#pragma once

#include <Onsang/config.hpp>

#include <memory>
#include <string>
#include <vector>
#include <deque>
#include <map>
#include <unordered_map>
#include <unordered_set>

namespace Onsang {
namespace aux {

/**
	@addtogroup etc
	@{
*/
/**
	@addtogroup aux
	@{
*/

/**
	@c std::shared_ptr<T>.
*/
template<
	typename T
>
using shared_ptr = std::shared_ptr<T>;

/**
	@c std::weak_ptr<T>.
*/
template<
	typename T
>
using weak_ptr = std::weak_ptr<T>;

/** Alias for @c std::make_shared(). */
using std::make_shared;

/** Alias for @c std::owner_less<T>. */
using std::owner_less;

/** Alias for @c std::enable_shared_from_this<T>. */
using std::enable_shared_from_this;

/**
	@c std::basic_string<CharT, Traits>.
*/
template<
	typename CharT,
	class Traits = std::char_traits<CharT>
>
using basic_string
= std::basic_string<
	CharT, Traits,
	ONSANG_AUX_ALLOCATOR<CharT>
>;

/**
	@c std::vector<T>.
*/
template<
	typename T
>
using vector
= std::vector<
	T,
	ONSANG_AUX_ALLOCATOR<T>
>;

/**
	@c std::deque<T>.
*/
template<
	typename T
>
using deque
= std::deque<
	T,
	ONSANG_AUX_ALLOCATOR<T>
>;

/**
	@c std::unordered_map<Key, T, Hash, KeyEqual>.
*/
template<
	typename Key,
	typename T,
	class Compare = std::less<Key>
>
using map
= std::map<
	Key, T, Compare,
	ONSANG_AUX_ALLOCATOR<std::pair<Key const, T> >
>;

/**
	@c std::unordered_map<Key, T, Hash, KeyEqual>.
*/
template<
	typename Key,
	typename T,
	class Hash = std::hash<Key>,
	class KeyEqual = std::equal_to<Key>
>
using unordered_map
= std::unordered_map<
	Key, T, Hash, KeyEqual,
	ONSANG_AUX_ALLOCATOR<std::pair<Key const, T> >
>;

/**
	@c std::unordered_set<Key, Hash, KeyEqual>.
*/
template<
	typename Key,
	class Hash = std::hash<Key>,
	class KeyEqual = std::equal_to<Key>
>
using unordered_set
= std::unordered_set<
	Key, Hash, KeyEqual,
	ONSANG_AUX_ALLOCATOR<Key>
>;

/** @} */ // end of doc-group aux
/** @} */ // end of doc-group etc

} // namespace aux
} // namespace Onsang
<|endoftext|>


<commit_before>/* *** This file is part of bbox ***
 *
 * Copyright (C) 2010 Andrea Marchesini <baku@ippolita.net>.
 *
 * This program is free software. It is released under the terms of
 * the BSD License. See license.txt for more details.
 */

#include "bbapplication.h"

#include "bbpreferences.h"
#include "bbcheckout.h"
#include "bbabout.h"
#include "bbsettings.h"
#include "bbobserver.h"
#include "bbactionmanager.h"
#include "bboperations.h"
#include "bbsendreceive.h"
#include "bbsvn.h"
#include "bbdebug.h"
#include "bbconst.h"

#include <QMenu>
#include <QAction>
#include <QFileDialog>
#include <QMessageBox>
#include <QTimer>
#include <QSplashScreen>
#include <QDesktopServices>
#include <QUrl>

QPointer<BBApplication> BBApplication::m_instance;

BBApplication::BBApplication(int argc, char **argv) :
    QApplication(argc, argv),
    m_errorShown(false),
    m_timer(0),
    m_iconBlink(false)
{
    BBDEBUG;

    m_instance = this;

    qsrand(time(0));
}

BBApplication::~BBApplication()
{
    BBDEBUG;
    delete m_systemTray;
}

BBApplication* BBApplication::instance()
{
    return m_instance;
}

void BBApplication::splash()
{
    BBDEBUG;
    QPixmap pixmap(BB_LOGO_IMAGE);
    QSplashScreen splash(pixmap);
    splash.show();

    for(int i=0; i<20; i++) {
        processEvents();
        usleep(100000);
    }
}

void BBApplication::init()
{
    BBDEBUG;

    systemTray();

    if (!BBSettings::instance()->isValid() &&
        !preferences()) {
        quit();
        return;
    }

    checkCheckout();

    m_observer = new BBObserver(this);

    BBActionManager::instance()->actionCleanup();

    scheduleRemoteAction();

    BBActionManager::instance()->actionAdd(BBSettings::instance()->directory());
    BBActionManager::instance()->actionLocalChanges();
}

void BBApplication::systemTray()
{
    BBDEBUG;

    QMenu *menu = new QMenu();

    QFont font;
    font.setBold(true);

    m_actionCommit = new QAction(tr("&Update and Send"), this);
    m_actionCommit->setFont(font);
    menu->addAction(m_actionCommit);
    connect (m_actionCommit,
             SIGNAL(triggered()),
             SLOT(onCommitTriggered()));

    menu->addSeparator();

    QAction *actionOpen = new QAction(tr("&Open directory"), this);
    menu->addAction(actionOpen);
    connect (actionOpen,
             SIGNAL(triggered()),
             SLOT(onOpenTriggered()));

    menu->addSeparator();

    m_actionCounter = new QAction(this);
    menu->addAction(m_actionCounter);
    connect(BBActionManager::instance(),
            SIGNAL(actionsQueued(int)),
            SLOT(onActionsQueued(int)));

    menu->addSeparator();

    QAction *preferences = new QAction(tr("&Preferences"), this);
    menu->addAction(preferences);
    connect (preferences,
             SIGNAL(triggered()),
             SLOT(preferences()));

    QAction *about = new QAction(tr("&About"), this);
    menu->addAction(about);
    connect (about,
             SIGNAL(triggered()),
             SLOT(about()));

    menu->addSeparator();

    QAction *actionQuit = new QAction(tr("&Quit"), this);
    menu->addAction(actionQuit);
    connect (actionQuit,
             SIGNAL(triggered()),
             SLOT(quit()));

    m_systemTray = new QSystemTrayIcon();
    m_systemTray->setContextMenu(menu);

    m_systemTray->setIcon(QIcon(BB_ICON_IMAGE));
    m_systemTray->show();

    connect(m_systemTray,
            SIGNAL(messageClicked()),
            SLOT(onMessageClicked()));
    connect(m_systemTray,
            SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            SLOT(onActivated(QSystemTrayIcon::ActivationReason)));

    onActionsQueued(0);
}

bool BBApplication::preferences()
{
    BBDEBUG;

    BBPreferences preferences;
    preferences.exec();

    if (!BBSettings::instance()->isValid()) {
        QMessageBox::critical(0,
                              BBPACKAGE,
                              tr("No configuration, no party."));
        return false;
    }

    checkCheckout();
    return true;
}

void BBApplication::about()
{
    BBDEBUG;

    BBAbout about;
    about.exec();
}

void BBApplication::onActionsQueued(int counter)
{
    BBDEBUG << "Counter: " << counter;

    m_actionCounter->setText(tr("Actions in queue: %1").arg(counter));
}

void BBApplication::onMessageClicked()
{
    BBDEBUG;
    m_errorShown = false;
}

void BBApplication::scheduleRemoteAction()
{
    BBDEBUG;

    BBActionManager::instance()->actionRemoteChanges();

    uint time = BBSettings::instance()->timerRemoteAction() * 10;
    int when(qrand() % ((time / 2) * 6) + time); // Random between time/2 and time
    BBDEBUG << "Next: " << when << " milliseconds.";

    QTimer::singleShot(when * 1000,
                       this,
                       SLOT(scheduleRemoteAction()));
}

void BBApplication::addError(const QString& error)
{
    BBDEBUG;

    if (m_errorShown)
        return;

    if (m_systemTray->supportsMessages()) {
        m_systemTray->showMessage(tr("Error!"), error, QSystemTrayIcon::Warning);
        m_errorShown = true;
    }
}

void BBApplication::commit()
{
    BBDEBUG;
    blink(true);

    if (BBSettings::instance()->autoCommit()) {
        if (m_sendReceive.isNull()) {
            m_sendReceive = new BBSendReceive(this);
            connect(m_sendReceive,
                    SIGNAL(done(bool)),
                    SLOT(onSendReceiveDone(bool)));
        }

        if (m_sendReceive->isRunning() == false) {
            m_systemTray->showMessage(tr("Sending and receiving..."), tr("Your changes will be shared soon."), QSystemTrayIcon::Information);
            m_sendReceive->start();
        }

    } else {
        m_systemTray->showMessage(tr("Something new!"), tr("Click to share your changes to other users"), QSystemTrayIcon::Information);
    }
}

void BBApplication::update()
{
    BBDEBUG;
    blink(true);

    m_systemTray->showMessage(tr("Something new!"), tr("Click to have changes from other users"), QSystemTrayIcon::Information);
}

void BBApplication::blink(bool enabled)
{
    BBDEBUG << enabled;

    if (enabled == true && !m_timer) {
        m_timer = startTimer(1000);
    } else if(enabled == false && m_timer) {
        m_systemTray->setIcon(QIcon(BB_ICON_IMAGE));
        killTimer(m_timer);
        m_timer = 0;
    }
}

void BBApplication::timerEvent(QTimerEvent *event)
{
    Q_UNUSED(event);
    if (m_timer == 0)
        return;

    if (!m_iconBlink)
        m_systemTray->setIcon(QIcon(BB_BLINK_IMAGE));
    else
        m_systemTray->setIcon(QIcon(BB_ICON_IMAGE));

    m_iconBlink = !m_iconBlink;
}

void BBApplication::onCommitTriggered()
{
    BBDEBUG;
    blink(false);

    BBOperations operations;
    operations.exec();
}

BBObserver* BBApplication::observer()
{
    return m_observer;
}

void BBApplication::onSendReceiveDone(bool status)
{
    BBDEBUG;
    Q_UNUSED(status);

    if (status == true)
        m_systemTray->showMessage(tr("Sending and receiving done"), tr("Your changes have been shared!"), QSystemTrayIcon::Information);
    blink(false);
}

void BBApplication::onOpenTriggered()
{
    BBDEBUG;
    QDesktopServices::openUrl(QUrl::fromLocalFile(BBSettings::instance()->directory()).toString());
}

void BBApplication::onActivated(QSystemTrayIcon::ActivationReason reason)
{
    BBDEBUG << reason;

    if (reason == QSystemTrayIcon::DoubleClick)
        onCommitTriggered();
}

void BBApplication::checkCheckout()
{
    if (BBSvn::isACheckout())
        return;

    BBCheckout checkout;
    checkout.exec();
}
<commit_msg>invisible action counter is count == 0<commit_after>/* *** This file is part of bbox ***
 *
 * Copyright (C) 2010 Andrea Marchesini <baku@ippolita.net>.
 *
 * This program is free software. It is released under the terms of
 * the BSD License. See license.txt for more details.
 */

#include "bbapplication.h"

#include "bbpreferences.h"
#include "bbcheckout.h"
#include "bbabout.h"
#include "bbsettings.h"
#include "bbobserver.h"
#include "bbactionmanager.h"
#include "bboperations.h"
#include "bbsendreceive.h"
#include "bbsvn.h"
#include "bbdebug.h"
#include "bbconst.h"

#include <QMenu>
#include <QAction>
#include <QFileDialog>
#include <QMessageBox>
#include <QTimer>
#include <QSplashScreen>
#include <QDesktopServices>
#include <QUrl>

QPointer<BBApplication> BBApplication::m_instance;

BBApplication::BBApplication(int argc, char **argv) :
    QApplication(argc, argv),
    m_errorShown(false),
    m_timer(0),
    m_iconBlink(false)
{
    BBDEBUG;

    m_instance = this;

    qsrand(time(0));
}

BBApplication::~BBApplication()
{
    BBDEBUG;
    delete m_systemTray;
}

BBApplication* BBApplication::instance()
{
    return m_instance;
}

void BBApplication::splash()
{
    BBDEBUG;
    QPixmap pixmap(BB_LOGO_IMAGE);
    QSplashScreen splash(pixmap);
    splash.show();

    for(int i=0; i<20; i++) {
        processEvents();
        usleep(100000);
    }
}

void BBApplication::init()
{
    BBDEBUG;

    systemTray();

    if (!BBSettings::instance()->isValid() &&
        !preferences()) {
        quit();
        return;
    }

    checkCheckout();

    m_observer = new BBObserver(this);

    BBActionManager::instance()->actionCleanup();

    scheduleRemoteAction();

    BBActionManager::instance()->actionAdd(BBSettings::instance()->directory());
    BBActionManager::instance()->actionLocalChanges();
}

void BBApplication::systemTray()
{
    BBDEBUG;

    QMenu *menu = new QMenu();

    QFont font;
    font.setBold(true);

    m_actionCommit = new QAction(tr("&Update and Send"), this);
    m_actionCommit->setFont(font);
    menu->addAction(m_actionCommit);
    connect (m_actionCommit,
             SIGNAL(triggered()),
             SLOT(onCommitTriggered()));

    menu->addSeparator();

    QAction *actionOpen = new QAction(tr("&Open directory"), this);
    menu->addAction(actionOpen);
    connect (actionOpen,
             SIGNAL(triggered()),
             SLOT(onOpenTriggered()));

    menu->addSeparator();

    QAction *preferences = new QAction(tr("&Preferences"), this);
    menu->addAction(preferences);
    connect (preferences,
             SIGNAL(triggered()),
             SLOT(preferences()));

    QAction *about = new QAction(tr("&About"), this);
    menu->addAction(about);
    connect (about,
             SIGNAL(triggered()),
             SLOT(about()));

    menu->addSeparator();

    m_actionCounter = new QAction(this);
    m_actionCounter->setVisible(false);
    menu->addAction(m_actionCounter);
    connect(BBActionManager::instance(),
            SIGNAL(actionsQueued(int)),
            SLOT(onActionsQueued(int)));

    QAction *actionQuit = new QAction(tr("&Quit"), this);
    menu->addAction(actionQuit);
    connect (actionQuit,
             SIGNAL(triggered()),
             SLOT(quit()));

    m_systemTray = new QSystemTrayIcon();
    m_systemTray->setContextMenu(menu);

    m_systemTray->setIcon(QIcon(BB_ICON_IMAGE));
    m_systemTray->show();

    connect(m_systemTray,
            SIGNAL(messageClicked()),
            SLOT(onMessageClicked()));
    connect(m_systemTray,
            SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            SLOT(onActivated(QSystemTrayIcon::ActivationReason)));

    onActionsQueued(0);
}

bool BBApplication::preferences()
{
    BBDEBUG;

    BBPreferences preferences;
    preferences.exec();

    if (!BBSettings::instance()->isValid()) {
        QMessageBox::critical(0,
                              BBPACKAGE,
                              tr("No configuration, no party."));
        return false;
    }

    checkCheckout();
    return true;
}

void BBApplication::about()
{
    BBDEBUG;

    BBAbout about;
    about.exec();
}

void BBApplication::onActionsQueued(int counter)
{
    BBDEBUG << "Counter: " << counter;

    m_actionCounter->setVisible((counter > 0));
    m_actionCounter->setText(tr("Actions in queue: %1").arg(counter));
}

void BBApplication::onMessageClicked()
{
    BBDEBUG;
    m_errorShown = false;
}

void BBApplication::scheduleRemoteAction()
{
    BBDEBUG;

    BBActionManager::instance()->actionRemoteChanges();

    uint time = BBSettings::instance()->timerRemoteAction() * 10;
    int when(qrand() % ((time / 2) * 6) + time); // Random between time/2 and time
    BBDEBUG << "Next: " << when << " milliseconds.";

    QTimer::singleShot(when * 1000,
                       this,
                       SLOT(scheduleRemoteAction()));
}

void BBApplication::addError(const QString& error)
{
    BBDEBUG;

    if (m_errorShown)
        return;

    if (m_systemTray->supportsMessages()) {
        m_systemTray->showMessage(tr("Error!"), error, QSystemTrayIcon::Warning);
        m_errorShown = true;
    }
}

void BBApplication::commit()
{
    BBDEBUG;
    blink(true);

    if (BBSettings::instance()->autoCommit()) {
        if (m_sendReceive.isNull()) {
            m_sendReceive = new BBSendReceive(this);
            connect(m_sendReceive,
                    SIGNAL(done(bool)),
                    SLOT(onSendReceiveDone(bool)));
        }

        if (m_sendReceive->isRunning() == false) {
            m_systemTray->showMessage(tr("Sending and receiving..."), tr("Your changes will be shared soon."), QSystemTrayIcon::Information);
            m_sendReceive->start();
        }

    } else {
        m_systemTray->showMessage(tr("Something new!"), tr("Click to share your changes to other users"), QSystemTrayIcon::Information);
    }
}

void BBApplication::update()
{
    BBDEBUG;
    blink(true);

    m_systemTray->showMessage(tr("Something new!"), tr("Click to have changes from other users"), QSystemTrayIcon::Information);
}

void BBApplication::blink(bool enabled)
{
    BBDEBUG << enabled;

    if (enabled == true && !m_timer) {
        m_timer = startTimer(1000);
    } else if(enabled == false && m_timer) {
        m_systemTray->setIcon(QIcon(BB_ICON_IMAGE));
        killTimer(m_timer);
        m_timer = 0;
    }
}

void BBApplication::timerEvent(QTimerEvent *event)
{
    Q_UNUSED(event);
    if (m_timer == 0)
        return;

    if (!m_iconBlink)
        m_systemTray->setIcon(QIcon(BB_BLINK_IMAGE));
    else
        m_systemTray->setIcon(QIcon(BB_ICON_IMAGE));

    m_iconBlink = !m_iconBlink;
}

void BBApplication::onCommitTriggered()
{
    BBDEBUG;
    blink(false);

    BBOperations operations;
    operations.exec();
}

BBObserver* BBApplication::observer()
{
    return m_observer;
}

void BBApplication::onSendReceiveDone(bool status)
{
    BBDEBUG;
    Q_UNUSED(status);

    if (status == true)
        m_systemTray->showMessage(tr("Sending and receiving done"), tr("Your changes have been shared!"), QSystemTrayIcon::Information);
    blink(false);
}

void BBApplication::onOpenTriggered()
{
    BBDEBUG;
    QDesktopServices::openUrl(QUrl::fromLocalFile(BBSettings::instance()->directory()).toString());
}

void BBApplication::onActivated(QSystemTrayIcon::ActivationReason reason)
{
    BBDEBUG << reason;

    if (reason == QSystemTrayIcon::DoubleClick)
        onCommitTriggered();
}

void BBApplication::checkCheckout()
{
    if (BBSvn::isACheckout())
        return;

    BBCheckout checkout;
    checkout.exec();
}
<|endoftext|>