<commit_before>/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */

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

#include "tile.hh"

namespace Gtk {

namespace Util {

Tile::Tile(const Glib::ustring& title,
           const Glib::ustring& summary,
           bool paint_white)
    :
    root_hbox_(),
    image_(),
    content_vbox_(),
    title_label_(),
    summary_label_(),
    title_(title),
    summary_(summary),
    paint_white_(paint_white)
{
    set_flags(Gtk::CAN_FOCUS);

    // set up root hbox
    root_hbox_.set_border_width(5);
    root_hbox_.set_spacing(2);
    add(root_hbox_);

    // pack illustration image
    image_.show();
    root_hbox_.pack_start(image_, false, false, 0);

    // prepare vbox to appear on the right hand side of the image
    content_vbox_.set_border_width(5);
    content_vbox_.set_spacing(2);

    // set up the title label
    title_label_.set_markup("" +
                            Glib::strescape(title_) +
                            "");
    title_label_.set_ellipsize(Pango::ELLIPSIZE_END);
    title_label_.set_max_width_chars(30);
    title_label_.modify_fg(Gtk::STATE_NORMAL,
                           this->get_style()->get_fg(Gtk::STATE_INSENSITIVE));
    content_vbox_.pack_start(title_label_, false, false, 0);

    // set up the summary label
    summary_label_.set_markup("" +
                              Glib::strescape(summary_) +
                              "");
    summary_label_.set_ellipsize(Pango::ELLIPSIZE_END);
    summary_label_.set_max_width_chars(30);
    summary_label_.modify_fg(Gtk::STATE_NORMAL,
                             this->get_style()->get_fg(Gtk::STATE_INSENSITIVE));
    content_vbox_.pack_start(summary_label_, false, false, 0);

    // wrap up
    content_vbox_.show_all();
    root_hbox_.pack_start(content_vbox_);
}

Tile::~Tile()
{
}

Gtk::HBox&
Tile::get_root_hbox()
{
    return root_hbox_;
}

Gtk::Image&
Tile::get_image()
{
    return image_;
}

Gtk::VBox&
Tile::get_content_vbox()
{
    return content_vbox_;
}

Tile::SignalTileSelected&
Tile::get_signal_tile_selected()
{
    return signal_tile_selected_;
}

bool
Tile::on_expose_event(GdkEventExpose* event)
{
    if (! is_visible())
        return false;

    GdkWindow* event_window = event->window;

    if (paint_white_ &&
        (gdk_window_get_window_type(event_window) == GDK_WINDOW_CHILD))
    {
        Glib::RefPtr<Gdk::Window> window = this->get_window();
        Glib::RefPtr<Gdk::GC> gc =
            this->get_style()->get_base_gc(this->get_state());

        window->draw_rectangle(gc,
                               true, // fill
                               event->area.x, event->area.y,
                               event->area.width, event->area.height);
    }

    if (this->get_flags() & Gtk::HAS_FOCUS)
    {
        int focus_pad, width, height;

        Glib::RefPtr<Gdk::Window> window = this->get_window();
        Gdk::Rectangle alloc = this->get_allocation();

        Glib::RefPtr<Gtk::Style> style = get_style();
        this->get_style_property<int>("focus_padding", focus_pad);

        // We do not need to calculate x and y origins of the box
        // for pain_* functions - just starting from (0,0) will
        // do the right thing.

        width =
            alloc.get_width() - 2 * (focus_pad + style->get_xthickness());

        height =
            alloc.get_height() - 2 * (focus_pad + style->get_ythickness());

        style->paint_box(content_vbox_.get_window(),
                         Gtk::STATE_SELECTED,
                         Gtk::SHADOW_NONE,
                         Gdk::Rectangle(&(event->area)),
                         content_vbox_,
                         "TileSelectionBox",
                         0, 0,
                         width, height);

        title_label_.set_state(Gtk::STATE_SELECTED);
        summary_label_.set_state(Gtk::STATE_SELECTED);

        style->paint_focus(window,
                           this->get_state(),
                           Gdk::Rectangle(&(event->area)),
                           *this,
                           "TileFocus",
                           0, 0,
                           width, height);
    }
    else
    {
        title_label_.set_state(Gtk::STATE_NORMAL);
        summary_label_.set_state(Gtk::STATE_NORMAL);
    }

    Gtk::Widget* child_widget = this->get_child(); // Gtk::Bin
    if (child_widget)
        propagate_expose(*child_widget, event);

    return false;
}

bool
Tile::on_button_press_event(GdkEventButton* /* event */)
{
    grab_focus();
    return false;
}

bool
Tile::on_focus_in_event(GdkEventFocus* event)
{
    signal_tile_selected_.emit();
    return Gtk::EventBox::on_focus_in_event(event);
}

} // namespace Util

} // namespace Gtk
<commit_msg>Wrote some comments on the code in Tile::on_expose_event().<commit_after>/* -*- Mode: C++; indent-tabs-mode:nil; c-basic-offset:4; -*- */

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

#include "tile.hh"

namespace Gtk {

namespace Util {

Tile::Tile(const Glib::ustring& title,
           const Glib::ustring& summary,
           bool paint_white)
    :
    root_hbox_(),
    image_(),
    content_vbox_(),
    title_label_(),
    summary_label_(),
    title_(title),
    summary_(summary),
    paint_white_(paint_white)
{
    set_flags(Gtk::CAN_FOCUS);

    // set up root hbox
    root_hbox_.set_border_width(5);
    root_hbox_.set_spacing(2);
    add(root_hbox_);

    // pack illustration image
    image_.show();
    root_hbox_.pack_start(image_, false, false, 0);

    // prepare vbox to appear on the right hand side of the image
    content_vbox_.set_border_width(5);
    content_vbox_.set_spacing(2);

    // set up the title label
    title_label_.set_markup("" +
                            Glib::strescape(title_) +
                            "");
    title_label_.set_ellipsize(Pango::ELLIPSIZE_END);
    title_label_.set_max_width_chars(30);
    title_label_.modify_fg(Gtk::STATE_NORMAL,
                           this->get_style()->get_fg(Gtk::STATE_INSENSITIVE));
    content_vbox_.pack_start(title_label_, false, false, 0);

    // set up the summary label
    summary_label_.set_markup("" +
                              Glib::strescape(summary_) +
                              "");
    summary_label_.set_ellipsize(Pango::ELLIPSIZE_END);
    summary_label_.set_max_width_chars(30);
    summary_label_.modify_fg(Gtk::STATE_NORMAL,
                             this->get_style()->get_fg(Gtk::STATE_INSENSITIVE));
    content_vbox_.pack_start(summary_label_, false, false, 0);

    // wrap up
    content_vbox_.show_all();
    root_hbox_.pack_start(content_vbox_);
}

Tile::~Tile()
{
}

Gtk::HBox&
Tile::get_root_hbox()
{
    return root_hbox_;
}

Gtk::Image&
Tile::get_image()
{
    return image_;
}

Gtk::VBox&
Tile::get_content_vbox()
{
    return content_vbox_;
}

Tile::SignalTileSelected&
Tile::get_signal_tile_selected()
{
    return signal_tile_selected_;
}

bool
Tile::on_expose_event(GdkEventExpose* event)
{
    if (! is_visible())
        return false;

    GdkWindow* event_window = event->window;

    if (paint_white_ &&
        (gdk_window_get_window_type(event_window) == GDK_WINDOW_CHILD))
    {
        // Paint white background on the widget.

        Glib::RefPtr<Gdk::Window> window = this->get_window();
        Glib::RefPtr<Gdk::GC> gc =
            this->get_style()->get_base_gc(this->get_state());

        window->draw_rectangle(gc,
                               true, // fill
                               event->area.x, event->area.y,
                               event->area.width, event->area.height);
    }

    if (this->get_flags() & Gtk::HAS_FOCUS)
    {
        int focus_pad, width, height;

        Glib::RefPtr<Gdk::Window> window = this->get_window();
        Gdk::Rectangle alloc = this->get_allocation();

        Glib::RefPtr<Gtk::Style> style = get_style();
        this->get_style_property<int>("focus_padding", focus_pad);

        // We do not need to calculate x and y origins of the box
        // for pain_* functions - just starting from (0,0) will
        // do the right thing.

        width =
            alloc.get_width() - 2 * (focus_pad + style->get_xthickness());

        height =
            alloc.get_height() - 2 * (focus_pad + style->get_ythickness());

        // Paint a backround box, the usual selection indicator
        // which is usually blue. It is important to paint on the
        // container box's window, otherwise if we'd, for instance,
        // paint on the widget itself, the box would completely cover it.

        style->paint_box(root_hbox_.get_window(),
                         Gtk::STATE_SELECTED,
                         Gtk::SHADOW_NONE,
                         Gdk::Rectangle(&(event->area)),
                         root_hbox_,
                         "TileSelectionBox",
                         0, 0,
                         width, height);

        // Change the label state to selected, so that the theme
        // engine changes the font color appropriately to contrast
        // the colour of the box.

        title_label_.set_state(Gtk::STATE_SELECTED);
        summary_label_.set_state(Gtk::STATE_SELECTED);

        // Also paint the the little line border around the widget,
        // another usual focus indicator.

        style->paint_focus(window,
                           this->get_state(),
                           Gdk::Rectangle(&(event->area)),
                           *this,
                           "TileFocus",
                           0, 0,
                           width, height);
    }
    else
    {
        title_label_.set_state(Gtk::STATE_NORMAL);
        summary_label_.set_state(Gtk::STATE_NORMAL);
    }

    Gtk::Widget* child_widget = this->get_child(); // Gtk::Bin method
    if (child_widget)
        propagate_expose(*child_widget, event);

    return false;
}

bool
Tile::on_button_press_event(GdkEventButton* /* event */)
{
    grab_focus();
    return false;
}

bool
Tile::on_focus_in_event(GdkEventFocus* event)
{
    signal_tile_selected_.emit();
    return Gtk::EventBox::on_focus_in_event(event);
}

} // namespace Util

} // namespace Gtk
<|endoftext|>


<commit_before>#include "traceloader.h"

#include "apitrace.h"
#include <QDebug>
#include <QFile>
#include <QStack>

#define FRAMES_TO_CACHE 100

static ApiTraceCall *
apiCallFromTraceCall(const trace::Call *call,
                     const QHash<QString, QUrl> &helpHash,
                     ApiTraceFrame *frame,
                     ApiTraceCall *parentCall,
                     TraceLoader *loader)
{
    ApiTraceCall *apiCall;

    if (parentCall)
        apiCall = new ApiTraceCall(parentCall, loader, call);
    else
        apiCall = new ApiTraceCall(frame, loader, call);

    apiCall->setHelpUrl(helpHash.value(apiCall->name()));

    return apiCall;
}

TraceLoader::TraceLoader(QObject *parent)
    : QObject(parent)
{
}

TraceLoader::~TraceLoader()
{
    m_parser.close();
    qDeleteAll(m_signatures);
    qDeleteAll(m_enumSignatures);
}

void TraceLoader::loadTrace(const QString &filename)
{
    if (m_helpHash.isEmpty()) {
        loadHelpFile();
    }

    if (!m_frameBookmarks.isEmpty()) {
        qDeleteAll(m_signatures);
        qDeleteAll(m_enumSignatures);
        m_signatures.clear();
        m_enumSignatures.clear();
        m_frameBookmarks.clear();
        m_createdFrames.clear();
        m_parser.close();
    }

    if (!m_parser.open(filename.toLatin1())) {
        qDebug() << "error: failed to open " << filename;
        return;
    }

    emit startedParsing();

    if (m_parser.supportsOffsets()) {
        scanTrace();
    } else {
        //Load the entire file into memory
        parseTrace();
    }
    emit guessedApi(static_cast<int>(m_parser.api));
    emit finishedParsing();
}

void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
{
    fetchFrameContents(currentFrame);
}

int TraceLoader::numberOfFrames() const
{
    return m_frameBookmarks.size();
}

int TraceLoader::numberOfCallsInFrame(int frameIdx) const
{
    if (frameIdx >= m_frameBookmarks.size()) {
        return 0;
    }
    FrameBookmarks::const_iterator itr =
            m_frameBookmarks.find(frameIdx);
    return itr->numberOfCalls;
}

void TraceLoader::loadHelpFile()
{
    QFile file(":/resources/glreference.tsv");
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QString line;
        while (!file.atEnd()) {
            line = file.readLine();
            QString function = line.section('\t', 0, 0).trimmed();
            QUrl url = QUrl(line.section('\t', 1, 1).trimmed());
            //qDebug()<<"function = "<<function<<", url = "<<url.toString();
            m_helpHash.insert(function, url);
        }
    } else {
        qWarning() << "Couldn't open reference file "
                   << file.fileName();
    }
    file.close();
}

void TraceLoader::scanTrace()
{
    QList<ApiTraceFrame*> frames;
    ApiTraceFrame *currentFrame = 0;

    trace::Call *call;
    trace::ParseBookmark startBookmark;
    int numOfFrames = 0;
    int numOfCalls = 0;
    int lastPercentReport = 0;

    m_parser.getBookmark(startBookmark);

    while ((call = m_parser.scan_call())) {
        ++numOfCalls;

        if (call->flags & trace::CALL_FLAG_END_FRAME) {
            FrameBookmark frameBookmark(startBookmark);
            frameBookmark.numberOfCalls = numOfCalls;

            currentFrame = new ApiTraceFrame();
            currentFrame->number = numOfFrames;
            currentFrame->setNumChildren(numOfCalls);
            currentFrame->setLastCallIndex(call->no);
            frames.append(currentFrame);

            m_createdFrames.append(currentFrame);
            m_frameBookmarks[numOfFrames] = frameBookmark;
            ++numOfFrames;

            if (m_parser.percentRead() - lastPercentReport >= 5) {
                emit parsed(m_parser.percentRead());
                lastPercentReport = m_parser.percentRead();
            }
            m_parser.getBookmark(startBookmark);
            numOfCalls = 0;
        }
        delete call;
    }

    if (numOfCalls) {
        //trace::File::Bookmark endBookmark = m_parser.currentBookmark();
        FrameBookmark frameBookmark(startBookmark);
        frameBookmark.numberOfCalls = numOfCalls;

        currentFrame = new ApiTraceFrame();
        currentFrame->number = numOfFrames;
        currentFrame->setNumChildren(numOfCalls);
        frames.append(currentFrame);

        m_createdFrames.append(currentFrame);
        m_frameBookmarks[numOfFrames] = frameBookmark;
        ++numOfFrames;
    }

    emit parsed(100);

    emit framesLoaded(frames);
}

void TraceLoader::parseTrace()
{
    QList<ApiTraceFrame*> frames;
    ApiTraceFrame *currentFrame = 0;
    int frameCount = 0;
    QStack<ApiTraceCall*> groups;
    QVector<ApiTraceCall*> topLevelItems;
    QVector<ApiTraceCall*> allCalls;
    quint64 binaryDataSize = 0;

    int lastPercentReport = 0;

    trace::Call *call = m_parser.parse_call();
    while (call) {
        //std::cout << *call;
        if (!currentFrame) {
            currentFrame = new ApiTraceFrame();
            currentFrame->number = frameCount;
            ++frameCount;
        }
        ApiTraceCall *apiCall =
            apiCallFromTraceCall(call, m_helpHash, currentFrame, groups.isEmpty() ? 0 : groups.top(), this);
        allCalls.append(apiCall);
        if (groups.count() == 0) {
            topLevelItems.append(apiCall);
        }
        if (call->flags & trace::CALL_FLAG_MARKER_PUSH) {
            groups.push(apiCall);
        } else if (call->flags & trace::CALL_FLAG_MARKER_POP) {
            groups.top()->finishedAddingChildren();
            groups.pop();
        }
        if (!groups.isEmpty()) {
            groups.top()->addChild(apiCall);
        }
        if (apiCall->hasBinaryData()) {
            QByteArray data =
                apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
            binaryDataSize += data.size();
        }
        if (call->flags & trace::CALL_FLAG_END_FRAME) {
            allCalls.squeeze();
            topLevelItems.squeeze();
            if (topLevelItems.count() == allCalls.count()) {
                currentFrame->setCalls(allCalls, allCalls, binaryDataSize);
            } else {
                currentFrame->setCalls(topLevelItems, allCalls, binaryDataSize);
            }
            allCalls.clear();
            groups.clear();
            topLevelItems.clear();
            frames.append(currentFrame);
            currentFrame = 0;
            binaryDataSize = 0;
            if (frames.count() >= FRAMES_TO_CACHE) {
                emit framesLoaded(frames);
                frames.clear();
            }
            if (m_parser.percentRead() - lastPercentReport >= 5) {
                emit parsed(m_parser.percentRead());
                lastPercentReport = m_parser.percentRead();
            }
        }
        delete call;
        call = m_parser.parse_call();
    }

    //last frames won't have markers
    //  it's just a bunch of Delete calls for every object
    //  after the last SwapBuffers
    if (currentFrame) {
        allCalls.squeeze();
        if (topLevelItems.count() == allCalls.count()) {
            currentFrame->setCalls(allCalls, allCalls, binaryDataSize);
        } else {
            currentFrame->setCalls(topLevelItems, allCalls, binaryDataSize);
        }
        frames.append(currentFrame);
        currentFrame = 0;
    }
    if (frames.count()) {
        emit framesLoaded(frames);
    }
}

ApiTraceCallSignature * TraceLoader::signature(unsigned id)
{
    if (id >= m_signatures.count()) {
        m_signatures.resize(id + 1);
        return NULL;
    } else {
        return m_signatures[id];
    }
}

void TraceLoader::addSignature(unsigned id, ApiTraceCallSignature *signature)
{
    m_signatures[id] = signature;
}

ApiTraceEnumSignature * TraceLoader::enumSignature(unsigned id)
{
    if (id >= m_enumSignatures.count()) {
        m_enumSignatures.resize(id + 1);
        return NULL;
    } else {
        return m_enumSignatures[id];
    }
}

void TraceLoader::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
{
    m_enumSignatures[id] = signature;
}

void TraceLoader::searchNext(const ApiTrace::SearchRequest &request)
{
    Q_ASSERT(m_parser.supportsOffsets());
    if (m_parser.supportsOffsets()) {
        int startFrame = m_createdFrames.indexOf(request.frame);
        const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame];
        m_parser.setBookmark(frameBookmark.start);
        trace::Call *call = 0;
        while ((call = m_parser.parse_call())) {

            if (callContains(call, request.text, request.cs)) {
                unsigned frameIdx = callInFrame(call->no);
                ApiTraceFrame *frame = m_createdFrames[frameIdx];
                const QVector<ApiTraceCall*> calls =
                        fetchFrameContents(frame);
                for (int i = 0; i < calls.count(); ++i) {
                    if (calls[i]->index() == call->no) {
                        emit searchResult(request, ApiTrace::SearchResult_Found,
                                          calls[i]);
                        break;
                    }
                }
                delete call;
                return;
            }

            delete call;
        }
    }
    emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
}

void TraceLoader::searchPrev(const ApiTrace::SearchRequest &request)
{
    Q_ASSERT(m_parser.supportsOffsets());
    if (m_parser.supportsOffsets()) {
        int startFrame = m_createdFrames.indexOf(request.frame);
        trace::Call *call = 0;
        QList<trace::Call*> frameCalls;
        int frameIdx = startFrame;

        const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
        int numCallsToParse = frameBookmark.numberOfCalls;
        m_parser.setBookmark(frameBookmark.start);

        while ((call = m_parser.parse_call())) {

            frameCalls.append(call);
            --numCallsToParse;

            if (numCallsToParse == 0) {
                bool foundCall = searchCallsBackwards(frameCalls,
                                                      frameIdx,
                                                      request);

                qDeleteAll(frameCalls);
                frameCalls.clear();
                if (foundCall) {
                    return;
                }

                --frameIdx;

                if (frameIdx >= 0) {
                    const FrameBookmark &frameBookmark =
                            m_frameBookmarks[frameIdx];
                    m_parser.setBookmark(frameBookmark.start);
                    numCallsToParse = frameBookmark.numberOfCalls;
                }
            }
        }
    }
    emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
}

bool TraceLoader::searchCallsBackwards(const QList<trace::Call*> &calls,
                                       int frameIdx,
                                       const ApiTrace::SearchRequest &request)
{
    for (int i = calls.count() - 1; i >= 0; --i) {
        trace::Call *call = calls[i];
        if (callContains(call, request.text, request.cs)) {
            ApiTraceFrame *frame = m_createdFrames[frameIdx];
            const QVector<ApiTraceCall*> apiCalls =
                    fetchFrameContents(frame);
            for (int i = 0; i < apiCalls.count(); ++i) {
                if (apiCalls[i]->index() == call->no) {
                    emit searchResult(request,
                                      ApiTrace::SearchResult_Found,
                                      apiCalls[i]);
                    break;
                }
            }
            return true;
        }
    }
    return false;
}

int TraceLoader::callInFrame(int callIdx) const
{
    unsigned numCalls = 0;

    for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) {
        const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
        unsigned firstCall = numCalls;
        unsigned endCall = numCalls + frameBookmark.numberOfCalls;
        if (firstCall <= callIdx && endCall > callIdx) {
            return frameIdx;
        }
        numCalls = endCall;
    }
    Q_ASSERT(!"call not in the trace");
    return 0;
}

bool TraceLoader::callContains(trace::Call *call,
                               const QString &str,
                               Qt::CaseSensitivity sensitivity)
{
    /*
     * FIXME: do string comparison directly on trace::Call
     */
    ApiTraceCall *apiCall = apiCallFromTraceCall(call, m_helpHash,
                                                 0, 0, this);
    bool result = apiCall->contains(str, sensitivity);
    delete apiCall;
    return result;
}

QVector<ApiTraceCall*>
TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame)
{
    Q_ASSERT(currentFrame);

    if (currentFrame->isLoaded()) {
        return currentFrame->calls();
    }

    if (m_parser.supportsOffsets()) {
        unsigned frameIdx = currentFrame->number;
        int numOfCalls = numberOfCallsInFrame(frameIdx);

        if (numOfCalls) {
            quint64 binaryDataSize = 0;
            QStack<ApiTraceCall*> groups;
            QVector<ApiTraceCall*> topLevelItems;
            QVector<ApiTraceCall*> allCalls(numOfCalls);
            const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];

            m_parser.setBookmark(frameBookmark.start);

            trace::Call *call;
            int parsedCalls = 0;
            while ((call = m_parser.parse_call())) {
                ApiTraceCall *apiCall =
                    apiCallFromTraceCall(call, m_helpHash,
                                         currentFrame, groups.isEmpty() ? 0 : groups.top(), this);
                Q_ASSERT(apiCall);
                Q_ASSERT(parsedCalls < allCalls.size());
                allCalls[parsedCalls++] = apiCall;
                if (groups.count() == 0) {
                    topLevelItems.append(apiCall);
                }
                if (!groups.isEmpty()) {
                    groups.top()->addChild(apiCall);
                }
                if (call->flags & trace::CALL_FLAG_MARKER_PUSH) {
                    groups.push(apiCall);
                } else if (call->flags & trace::CALL_FLAG_MARKER_POP) {
                    groups.top()->finishedAddingChildren();
                    groups.pop();
                }
                if (apiCall->hasBinaryData()) {
                    QByteArray data =
                        apiCall->arguments()[
                            apiCall->binaryDataIndex()].toByteArray();
                    binaryDataSize += data.size();
                }

                delete call;

                if (apiCall->flags() & trace::CALL_FLAG_END_FRAME) {
                    break;
                }

            }
            // There can be fewer parsed calls when call in different
            // threads cross the frame boundary
            Q_ASSERT(parsedCalls <= numOfCalls);
            Q_ASSERT(parsedCalls <= allCalls.size());
            allCalls.resize(parsedCalls);
            allCalls.squeeze();

            Q_ASSERT(parsedCalls <= currentFrame->numChildrenToLoad());
            if (topLevelItems.count() == allCalls.count()) {
                emit frameContentsLoaded(currentFrame, allCalls,
                                         allCalls, binaryDataSize);
            } else {
                emit frameContentsLoaded(currentFrame, topLevelItems,
                                         allCalls, binaryDataSize);
            }
            return allCalls;
        }
    }
    return QVector<ApiTraceCall*>();
}

void TraceLoader::findFrameStart(ApiTraceFrame *frame)
{
    if (!frame->isLoaded()) {
        loadFrame(frame);
    }
    emit foundFrameStart(frame);
}

void TraceLoader::findFrameEnd(ApiTraceFrame *frame)
{
    if (!frame->isLoaded()) {
        loadFrame(frame);
    }
    emit foundFrameEnd(frame);
}

void TraceLoader::findCallIndex(int index)
{
    int frameIdx = callInFrame(index);
    ApiTraceFrame *frame = m_createdFrames[frameIdx];
    QVector<ApiTraceCall*> calls = fetchFrameContents(frame);
    QVector<ApiTraceCall*>::const_iterator itr;
    ApiTraceCall *call = 0;
    for (itr = calls.constBegin(); itr != calls.constEnd(); ++itr) {
        if ((*itr)->index() == index) {
            call = *itr;
            break;
        }
    }
    if (call) {
        emit foundCallIndex(call);
    }
}

void TraceLoader::search(const ApiTrace::SearchRequest &request)
{
    if (request.direction == ApiTrace::SearchRequest::Next) {
        searchNext(request);
    } else {
        searchPrev(request);
    }
}

#include "traceloader.moc"
<commit_msg>qapitrace: fix an inline string FIXME request<commit_after>#include "traceloader.h"

#include "apitrace.h"
#include <QDebug>
#include <QFile>
#include <QStack>

#define FRAMES_TO_CACHE 100

static ApiTraceCall *
apiCallFromTraceCall(const trace::Call *call,
                     const QHash<QString, QUrl> &helpHash,
                     ApiTraceFrame *frame,
                     ApiTraceCall *parentCall,
                     TraceLoader *loader)
{
    ApiTraceCall *apiCall;

    if (parentCall)
        apiCall = new ApiTraceCall(parentCall, loader, call);
    else
        apiCall = new ApiTraceCall(frame, loader, call);

    apiCall->setHelpUrl(helpHash.value(apiCall->name()));

    return apiCall;
}

TraceLoader::TraceLoader(QObject *parent)
    : QObject(parent)
{
}

TraceLoader::~TraceLoader()
{
    m_parser.close();
    qDeleteAll(m_signatures);
    qDeleteAll(m_enumSignatures);
}

void TraceLoader::loadTrace(const QString &filename)
{
    if (m_helpHash.isEmpty()) {
        loadHelpFile();
    }

    if (!m_frameBookmarks.isEmpty()) {
        qDeleteAll(m_signatures);
        qDeleteAll(m_enumSignatures);
        m_signatures.clear();
        m_enumSignatures.clear();
        m_frameBookmarks.clear();
        m_createdFrames.clear();
        m_parser.close();
    }

    if (!m_parser.open(filename.toLatin1())) {
        qDebug() << "error: failed to open " << filename;
        return;
    }

    emit startedParsing();

    if (m_parser.supportsOffsets()) {
        scanTrace();
    } else {
        //Load the entire file into memory
        parseTrace();
    }
    emit guessedApi(static_cast<int>(m_parser.api));
    emit finishedParsing();
}

void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
{
    fetchFrameContents(currentFrame);
}

int TraceLoader::numberOfFrames() const
{
    return m_frameBookmarks.size();
}

int TraceLoader::numberOfCallsInFrame(int frameIdx) const
{
    if (frameIdx >= m_frameBookmarks.size()) {
        return 0;
    }
    FrameBookmarks::const_iterator itr =
            m_frameBookmarks.find(frameIdx);
    return itr->numberOfCalls;
}

void TraceLoader::loadHelpFile()
{
    QFile file(":/resources/glreference.tsv");
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QString line;
        while (!file.atEnd()) {
            line = file.readLine();
            QString function = line.section('\t', 0, 0).trimmed();
            QUrl url = QUrl(line.section('\t', 1, 1).trimmed());
            //qDebug()<<"function = "<<function<<", url = "<<url.toString();
            m_helpHash.insert(function, url);
        }
    } else {
        qWarning() << "Couldn't open reference file "
                   << file.fileName();
    }
    file.close();
}

void TraceLoader::scanTrace()
{
    QList<ApiTraceFrame*> frames;
    ApiTraceFrame *currentFrame = 0;

    trace::Call *call;
    trace::ParseBookmark startBookmark;
    int numOfFrames = 0;
    int numOfCalls = 0;
    int lastPercentReport = 0;

    m_parser.getBookmark(startBookmark);

    while ((call = m_parser.scan_call())) {
        ++numOfCalls;

        if (call->flags & trace::CALL_FLAG_END_FRAME) {
            FrameBookmark frameBookmark(startBookmark);
            frameBookmark.numberOfCalls = numOfCalls;

            currentFrame = new ApiTraceFrame();
            currentFrame->number = numOfFrames;
            currentFrame->setNumChildren(numOfCalls);
            currentFrame->setLastCallIndex(call->no);
            frames.append(currentFrame);

            m_createdFrames.append(currentFrame);
            m_frameBookmarks[numOfFrames] = frameBookmark;
            ++numOfFrames;

            if (m_parser.percentRead() - lastPercentReport >= 5) {
                emit parsed(m_parser.percentRead());
                lastPercentReport = m_parser.percentRead();
            }
            m_parser.getBookmark(startBookmark);
            numOfCalls = 0;
        }
        delete call;
    }

    if (numOfCalls) {
        //trace::File::Bookmark endBookmark = m_parser.currentBookmark();
        FrameBookmark frameBookmark(startBookmark);
        frameBookmark.numberOfCalls = numOfCalls;

        currentFrame = new ApiTraceFrame();
        currentFrame->number = numOfFrames;
        currentFrame->setNumChildren(numOfCalls);
        frames.append(currentFrame);

        m_createdFrames.append(currentFrame);
        m_frameBookmarks[numOfFrames] = frameBookmark;
        ++numOfFrames;
    }

    emit parsed(100);

    emit framesLoaded(frames);
}

void TraceLoader::parseTrace()
{
    QList<ApiTraceFrame*> frames;
    ApiTraceFrame *currentFrame = 0;
    int frameCount = 0;
    QStack<ApiTraceCall*> groups;
    QVector<ApiTraceCall*> topLevelItems;
    QVector<ApiTraceCall*> allCalls;
    quint64 binaryDataSize = 0;

    int lastPercentReport = 0;

    trace::Call *call = m_parser.parse_call();
    while (call) {
        //std::cout << *call;
        if (!currentFrame) {
            currentFrame = new ApiTraceFrame();
            currentFrame->number = frameCount;
            ++frameCount;
        }
        ApiTraceCall *apiCall =
            apiCallFromTraceCall(call, m_helpHash, currentFrame, groups.isEmpty() ? 0 : groups.top(), this);
        allCalls.append(apiCall);
        if (groups.count() == 0) {
            topLevelItems.append(apiCall);
        }
        if (call->flags & trace::CALL_FLAG_MARKER_PUSH) {
            groups.push(apiCall);
        } else if (call->flags & trace::CALL_FLAG_MARKER_POP) {
            groups.top()->finishedAddingChildren();
            groups.pop();
        }
        if (!groups.isEmpty()) {
            groups.top()->addChild(apiCall);
        }
        if (apiCall->hasBinaryData()) {
            QByteArray data =
                apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
            binaryDataSize += data.size();
        }
        if (call->flags & trace::CALL_FLAG_END_FRAME) {
            allCalls.squeeze();
            topLevelItems.squeeze();
            if (topLevelItems.count() == allCalls.count()) {
                currentFrame->setCalls(allCalls, allCalls, binaryDataSize);
            } else {
                currentFrame->setCalls(topLevelItems, allCalls, binaryDataSize);
            }
            allCalls.clear();
            groups.clear();
            topLevelItems.clear();
            frames.append(currentFrame);
            currentFrame = 0;
            binaryDataSize = 0;
            if (frames.count() >= FRAMES_TO_CACHE) {
                emit framesLoaded(frames);
                frames.clear();
            }
            if (m_parser.percentRead() - lastPercentReport >= 5) {
                emit parsed(m_parser.percentRead());
                lastPercentReport = m_parser.percentRead();
            }
        }
        delete call;
        call = m_parser.parse_call();
    }

    //last frames won't have markers
    //  it's just a bunch of Delete calls for every object
    //  after the last SwapBuffers
    if (currentFrame) {
        allCalls.squeeze();
        if (topLevelItems.count() == allCalls.count()) {
            currentFrame->setCalls(allCalls, allCalls, binaryDataSize);
        } else {
            currentFrame->setCalls(topLevelItems, allCalls, binaryDataSize);
        }
        frames.append(currentFrame);
        currentFrame = 0;
    }
    if (frames.count()) {
        emit framesLoaded(frames);
    }
}

ApiTraceCallSignature * TraceLoader::signature(unsigned id)
{
    if (id >= m_signatures.count()) {
        m_signatures.resize(id + 1);
        return NULL;
    } else {
        return m_signatures[id];
    }
}

void TraceLoader::addSignature(unsigned id, ApiTraceCallSignature *signature)
{
    m_signatures[id] = signature;
}

ApiTraceEnumSignature * TraceLoader::enumSignature(unsigned id)
{
    if (id >= m_enumSignatures.count()) {
        m_enumSignatures.resize(id + 1);
        return NULL;
    } else {
        return m_enumSignatures[id];
    }
}

void TraceLoader::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
{
    m_enumSignatures[id] = signature;
}

void TraceLoader::searchNext(const ApiTrace::SearchRequest &request)
{
    Q_ASSERT(m_parser.supportsOffsets());
    if (m_parser.supportsOffsets()) {
        int startFrame = m_createdFrames.indexOf(request.frame);
        const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame];
        m_parser.setBookmark(frameBookmark.start);
        trace::Call *call = 0;
        while ((call = m_parser.parse_call())) {

            if (callContains(call, request.text, request.cs)) {
                unsigned frameIdx = callInFrame(call->no);
                ApiTraceFrame *frame = m_createdFrames[frameIdx];
                const QVector<ApiTraceCall*> calls =
                        fetchFrameContents(frame);
                for (int i = 0; i < calls.count(); ++i) {
                    if (calls[i]->index() == call->no) {
                        emit searchResult(request, ApiTrace::SearchResult_Found,
                                          calls[i]);
                        break;
                    }
                }
                delete call;
                return;
            }

            delete call;
        }
    }
    emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
}

void TraceLoader::searchPrev(const ApiTrace::SearchRequest &request)
{
    Q_ASSERT(m_parser.supportsOffsets());
    if (m_parser.supportsOffsets()) {
        int startFrame = m_createdFrames.indexOf(request.frame);
        trace::Call *call = 0;
        QList<trace::Call*> frameCalls;
        int frameIdx = startFrame;

        const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
        int numCallsToParse = frameBookmark.numberOfCalls;
        m_parser.setBookmark(frameBookmark.start);

        while ((call = m_parser.parse_call())) {

            frameCalls.append(call);
            --numCallsToParse;

            if (numCallsToParse == 0) {
                bool foundCall = searchCallsBackwards(frameCalls,
                                                      frameIdx,
                                                      request);

                qDeleteAll(frameCalls);
                frameCalls.clear();
                if (foundCall) {
                    return;
                }

                --frameIdx;

                if (frameIdx >= 0) {
                    const FrameBookmark &frameBookmark =
                            m_frameBookmarks[frameIdx];
                    m_parser.setBookmark(frameBookmark.start);
                    numCallsToParse = frameBookmark.numberOfCalls;
                }
            }
        }
    }
    emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
}

bool TraceLoader::searchCallsBackwards(const QList<trace::Call*> &calls,
                                       int frameIdx,
                                       const ApiTrace::SearchRequest &request)
{
    for (int i = calls.count() - 1; i >= 0; --i) {
        trace::Call *call = calls[i];
        if (callContains(call, request.text, request.cs)) {
            ApiTraceFrame *frame = m_createdFrames[frameIdx];
            const QVector<ApiTraceCall*> apiCalls =
                    fetchFrameContents(frame);
            for (int i = 0; i < apiCalls.count(); ++i) {
                if (apiCalls[i]->index() == call->no) {
                    emit searchResult(request,
                                      ApiTrace::SearchResult_Found,
                                      apiCalls[i]);
                    break;
                }
            }
            return true;
        }
    }
    return false;
}

int TraceLoader::callInFrame(int callIdx) const
{
    unsigned numCalls = 0;

    for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) {
        const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
        unsigned firstCall = numCalls;
        unsigned endCall = numCalls + frameBookmark.numberOfCalls;
        if (firstCall <= callIdx && endCall > callIdx) {
            return frameIdx;
        }
        numCalls = endCall;
    }
    Q_ASSERT(!"call not in the trace");
    return 0;
}

bool TraceLoader::callContains(trace::Call *call,
                               const QString &str,
                               Qt::CaseSensitivity sensitivity)
{
    if (sensitivity == Qt::CaseSensitive) {
        return (bool) strstr (call->name(), str.toAscii().data());
    } else {
        return (bool) strcasestr (call->name(), str.toAscii().data());
    }
}

QVector<ApiTraceCall*>
TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame)
{
    Q_ASSERT(currentFrame);

    if (currentFrame->isLoaded()) {
        return currentFrame->calls();
    }

    if (m_parser.supportsOffsets()) {
        unsigned frameIdx = currentFrame->number;
        int numOfCalls = numberOfCallsInFrame(frameIdx);

        if (numOfCalls) {
            quint64 binaryDataSize = 0;
            QStack<ApiTraceCall*> groups;
            QVector<ApiTraceCall*> topLevelItems;
            QVector<ApiTraceCall*> allCalls(numOfCalls);
            const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];

            m_parser.setBookmark(frameBookmark.start);

            trace::Call *call;
            int parsedCalls = 0;
            while ((call = m_parser.parse_call())) {
                ApiTraceCall *apiCall =
                    apiCallFromTraceCall(call, m_helpHash,
                                         currentFrame, groups.isEmpty() ? 0 : groups.top(), this);
                Q_ASSERT(apiCall);
                Q_ASSERT(parsedCalls < allCalls.size());
                allCalls[parsedCalls++] = apiCall;
                if (groups.count() == 0) {
                    topLevelItems.append(apiCall);
                }
                if (!groups.isEmpty()) {
                    groups.top()->addChild(apiCall);
                }
                if (call->flags & trace::CALL_FLAG_MARKER_PUSH) {
                    groups.push(apiCall);
                } else if (call->flags & trace::CALL_FLAG_MARKER_POP) {
                    groups.top()->finishedAddingChildren();
                    groups.pop();
                }
                if (apiCall->hasBinaryData()) {
                    QByteArray data =
                        apiCall->arguments()[
                            apiCall->binaryDataIndex()].toByteArray();
                    binaryDataSize += data.size();
                }

                delete call;

                if (apiCall->flags() & trace::CALL_FLAG_END_FRAME) {
                    break;
                }

            }
            // There can be fewer parsed calls when call in different
            // threads cross the frame boundary
            Q_ASSERT(parsedCalls <= numOfCalls);
            Q_ASSERT(parsedCalls <= allCalls.size());
            allCalls.resize(parsedCalls);
            allCalls.squeeze();

            Q_ASSERT(parsedCalls <= currentFrame->numChildrenToLoad());
            if (topLevelItems.count() == allCalls.count()) {
                emit frameContentsLoaded(currentFrame, allCalls,
                                         allCalls, binaryDataSize);
            } else {
                emit frameContentsLoaded(currentFrame, topLevelItems,
                                         allCalls, binaryDataSize);
            }
            return allCalls;
        }
    }
    return QVector<ApiTraceCall*>();
}

void TraceLoader::findFrameStart(ApiTraceFrame *frame)
{
    if (!frame->isLoaded()) {
        loadFrame(frame);
    }
    emit foundFrameStart(frame);
}

void TraceLoader::findFrameEnd(ApiTraceFrame *frame)
{
    if (!frame->isLoaded()) {
        loadFrame(frame);
    }
    emit foundFrameEnd(frame);
}

void TraceLoader::findCallIndex(int index)
{
    int frameIdx = callInFrame(index);
    ApiTraceFrame *frame = m_createdFrames[frameIdx];
    QVector<ApiTraceCall*> calls = fetchFrameContents(frame);
    QVector<ApiTraceCall*>::const_iterator itr;
    ApiTraceCall *call = 0;
    for (itr = calls.constBegin(); itr != calls.constEnd(); ++itr) {
        if ((*itr)->index() == index) {
            call = *itr;
            break;
        }
    }
    if (call) {
        emit foundCallIndex(call);
    }
}

void TraceLoader::search(const ApiTrace::SearchRequest &request)
{
    if (request.direction == ApiTrace::SearchRequest::Next) {
        searchNext(request);
    } else {
        searchPrev(request);
    }
}

#include "traceloader.moc"
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: continuousactivitybase.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 20:35:20 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// must be first
#include <canvas/debug.hxx>
#include <canvas/verbosetrace.hxx>

#include <continuousactivitybase.hxx>

namespace presentation
{
    namespace internal
    {
        ContinuousActivityBase::ContinuousActivityBase( const ActivityParameters& rParms ) :
            SimpleContinuousActivityBase( rParms )
        {
        }

        void ContinuousActivityBase::simplePerform( double      nSimpleTime,
                                                    sal_uInt32  nRepeatCount ) const
        {
            perform( calcAcceleratedTime( nSimpleTime ),
                     nRepeatCount );
        }
    }
}
<commit_msg>INTEGRATION: CWS pchfix02 (1.3.50); FILE MERGED 2006/09/01 17:39:36 kaib 1.3.50.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: continuousactivitybase.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 08:32:05 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_slideshow.hxx"

// must be first
#include <canvas/debug.hxx>
#include <canvas/verbosetrace.hxx>

#include <continuousactivitybase.hxx>

namespace presentation
{
    namespace internal
    {
        ContinuousActivityBase::ContinuousActivityBase( const ActivityParameters& rParms ) :
            SimpleContinuousActivityBase( rParms )
        {
        }

        void ContinuousActivityBase::simplePerform( double      nSimpleTime,
                                                    sal_uInt32  nRepeatCount ) const
        {
            perform( calcAcceleratedTime( nSimpleTime ),
                     nRepeatCount );
        }
    }
}
<|endoftext|>


<commit_before>#include "usbutil.h"
#include "buffers.h"

// TODO move this up to cantranslator.pde
USB_HANDLE USB_INPUT_HANDLE = 0;

void sendMessage(CanUsbDevice* usbDevice, uint8_t* message, int messageSize) {
#ifdef DEBUG
    Serial.print("sending message: ");
    Serial.println((char*)message);
#endif
    // Make sure the USB write is 100% complete before messing with this buffer
    // after we copy the message into it - the Microchip library doesn't copy
    // the data to its own internal buffer. See #171 for background on this
    // issue.
    int i = 0;
    bool usbConnected = usbDevice->configured;
    while(usbDevice->device.HandleBusy(USB_INPUT_HANDLE)) {
        i++;
        if(i > 200000) {
            // stop waiting, USB probably isn't connected
            usbConnected = false;
            // break;
        }
    }

    strncpy(usbDevice->sendBuffer, (char*)message, messageSize);
    usbDevice->sendBuffer[messageSize] = '\n';
    messageSize += 1;

    if(!usbConnected) {
        // Serial transfers are really, really slow, so we don't want to send unless
        // explicitly set to do so at compile-time. Alternatively, we could send
        // on serial whenever we detect below that we're probably not connected to a
        // USB device, but explicit is better than implicit.
        usbDevice->serial->device->write((const uint8_t*)usbDevice->sendBuffer, messageSize);
    } else {
        int nextByteIndex = 0;
        while(nextByteIndex < messageSize) {
            while(usbDevice->device.HandleBusy(USB_INPUT_HANDLE));
            int bytesToTransfer = min(USB_PACKET_SIZE,
                    messageSize - nextByteIndex);
            uint8_t* currentByte = (uint8_t*)(usbDevice->sendBuffer
                    + nextByteIndex);
            USB_INPUT_HANDLE = usbDevice->device.GenWrite(usbDevice->endpoint,
                    currentByte, bytesToTransfer);
            nextByteIndex += bytesToTransfer;
        }
    }
}

void initializeUsb(CanUsbDevice* usbDevice) {
    Serial.print("Initializing USB.....");
    usbDevice->device.InitializeSystem(false);
    Serial.println("Done.");
}

USB_HANDLE armForRead(CanUsbDevice* usbDevice, char* buffer) {
    buffer[0] = 0;
    return usbDevice->device.GenRead(usbDevice->endpoint, (uint8_t*)buffer,
            usbDevice->endpointSize);
}

USB_HANDLE readFromHost(CanUsbDevice* usbDevice, USB_HANDLE handle,
        bool (*callback)(char*)) {
    if(!usbDevice->device.HandleBusy(handle)) {
        // TODO see #569
        delay(200);
        if(usbDevice->receiveBuffer[0] != NULL) {
            strncpy((char*)(usbDevice->packetBuffer +
                        usbDevice->packetBufferIndex), usbDevice->receiveBuffer,
                        ENDPOINT_SIZE);
            usbDevice->packetBufferIndex += ENDPOINT_SIZE;
            processBuffer(usbDevice->packetBuffer,
                &usbDevice->packetBufferIndex, PACKET_BUFFER_SIZE, callback);
        }
        return armForRead(usbDevice, usbDevice->receiveBuffer);
    }
    return handle;
}
<commit_msg>Don't play the waiting game with USB - just assume it's unplugged after a while.<commit_after>#include "usbutil.h"
#include "buffers.h"

// TODO move this up to cantranslator.pde
USB_HANDLE USB_INPUT_HANDLE = 0;

void sendMessage(CanUsbDevice* usbDevice, uint8_t* message, int messageSize) {
#ifdef DEBUG
    Serial.print("sending message: ");
    Serial.println((char*)message);
#endif
    // Make sure the USB write is 100% complete before messing with this buffer
    // after we copy the message into it - the Microchip library doesn't copy
    // the data to its own internal buffer. See #171 for background on this
    // issue.
    int i = 0;
    while(usbDevice->configured && usbDevice->device.HandleBusy(USB_INPUT_HANDLE)) {
        i++;
        if(i > 1000000) {
            // USB was probably unplugged, mark it as unplugged and continue on
            // sending over serial.
            usbDevice->configured = false;
        }
    }
    Serial.println(i);

    strncpy(usbDevice->sendBuffer, (char*)message, messageSize);
    usbDevice->sendBuffer[messageSize] = '\n';
    messageSize += 1;

    if(!usbDevice->configured) {
        // Serial transfers are really, really slow, so we don't want to send unless
        // explicitly set to do so at compile-time. Alternatively, we could send
        // on serial whenever we detect below that we're probably not connected to a
        // USB device, but explicit is better than implicit.
        usbDevice->serial->device->write((const uint8_t*)usbDevice->sendBuffer, messageSize);
    } else {
        int nextByteIndex = 0;
        while(nextByteIndex < messageSize) {
            while(usbDevice->device.HandleBusy(USB_INPUT_HANDLE));
            int bytesToTransfer = min(USB_PACKET_SIZE,
                    messageSize - nextByteIndex);
            uint8_t* currentByte = (uint8_t*)(usbDevice->sendBuffer
                    + nextByteIndex);
            USB_INPUT_HANDLE = usbDevice->device.GenWrite(usbDevice->endpoint,
                    currentByte, bytesToTransfer);
            nextByteIndex += bytesToTransfer;
        }
    }
}

void initializeUsb(CanUsbDevice* usbDevice) {
    Serial.print("Initializing USB.....");
    usbDevice->device.InitializeSystem(false);
    Serial.println("Done.");
}

USB_HANDLE armForRead(CanUsbDevice* usbDevice, char* buffer) {
    buffer[0] = 0;
    return usbDevice->device.GenRead(usbDevice->endpoint, (uint8_t*)buffer,
            usbDevice->endpointSize);
}

USB_HANDLE readFromHost(CanUsbDevice* usbDevice, USB_HANDLE handle,
        bool (*callback)(char*)) {
    if(!usbDevice->device.HandleBusy(handle)) {
        // TODO see #569
        delay(200);
        if(usbDevice->receiveBuffer[0] != NULL) {
            strncpy((char*)(usbDevice->packetBuffer +
                        usbDevice->packetBufferIndex), usbDevice->receiveBuffer,
                        ENDPOINT_SIZE);
            usbDevice->packetBufferIndex += ENDPOINT_SIZE;
            processBuffer(usbDevice->packetBuffer,
                &usbDevice->packetBufferIndex, PACKET_BUFFER_SIZE, callback);
        }
        return armForRead(usbDevice, usbDevice->receiveBuffer);
    }
    return handle;
}
<|endoftext|>


<commit_before>#include "sixtracklib/cuda/wrappers/track_job_wrappers.h"

#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <string>

#include <gtest/gtest.h>
#include <cuda_runtime_api.h>

#include "sixtracklib/testlib.h"
#include "sixtracklib/common/definitions.h"
#include "sixtracklib/common/control/definitions.h"
#include "sixtracklib/common/buffer.h"
#include "sixtracklib/common/particles/particles_addr.h"
#include "sixtracklib/common/particles.h"
#include "sixtracklib/common/be_drift.h"
#include "sixtracklib/cuda/controller.h"
#include "sixtracklib/cuda/argument.h"
#include "sixtracklib/cuda/control/kernel_config.h"
#include "sixtracklib/cuda/control/node_info.h"

namespace SIXTRL_CXX_NAMESPACE
{
    namespace tests
    {
        bool run_test(
            std::string const& node_id_str,
            ::NS(buffer_size_t) const num_elements,
            ::NS(buffer_size_t) const num_blocks,
            ::NS(buffer_size_t) const num_threads_per_block,
            ::NS(buffer_size_t) const min_num_particles =
                ::NS(buffer_size_t{ 1 },
            ::NS(buffer_size_t) const max_num_particles =
                ::NS(buffer_size_t{ 2 },
            double const probabilty_to_not_particles = double{ 0 },
            ::NS(buffer_size_t) const seed = ::NS(buffer_size_t){ 20190517 } );
    }
}

TEST( C99_CudaWrappersExtractParticleAddrTest, SingleParticleSetTest )
{
    namespace st = SIXTRL_CXX_NAMESPACE;

    ASSERT_TRUE( st::tests::run_test( "", 1, 1,    1   ) );
    ASSERT_TRUE( st::tests::run_test( "", 1, 2048, 1   ) );
    ASSERT_TRUE( st::tests::run_test( "", 1, 1,    256 ) );
}

TEST( C99_CudaWrappersExtractParticleAddrTest, MultiParticleSetTest )
{
    ASSERT_TRUE( st::tests::run_test( "", 2048, 1,    1   ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 2048, 1   ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 1,    256 ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 8,    256 ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 16,   256 ) );
}

TEST( C99_CudaWrappersExtractParticleAddrTest, MultiParticleSetWithNonParticles )
{
    ASSERT_TRUE( st::tests::run_test( "", 2048, 1,    1  , 1, 2, 0.1 ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 2048, 1  , 1, 2, 0.1 ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 1,    256, 1, 2, 0.1 ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 8,    256, 1, 2, 0.1 ) );
    ASSERT_TRUE( st::tests::run_test( "", 2048, 16,   256, 1, 2, 0.1 ) );
}

namespace SIXTRL_CXX_NAMESPACE
{
    namespace tests
    {
        bool run_test(
            ::NS(CudaController)* SIXTRL_RESTRICT ctrl,
            ::NS(CudaKernelConfig)* SIXTRL_RESTRICT ptr_kernel_config,
            ::NS(buffer_size_t) const num_elements,
            ::NS(buffer_size_t) const min_num_particles,
            ::NS(buffer_size_t) const max_num_particles,
            double const probabilty_to_not_particles,
            ::NS(buffer_size_t) const seed = ::NS(buffer_size_t){ 20190517 } )
        {
            using particles_t        = ::NS(Particles);
            using particles_addr_t   = ::NS(ParticlesAddr);
            using c_buffer_t         = ::NS(Buffer);

            using cuda_ctrl_t        = ::NS(CudaController);
            using cuda_arg_t         = ::NS(CudaArgument);
            using cuda_kernel_conf_t = ::NS(CudaKernelConfig);
            using kernel_id_t        = ::NS(ctrl_kernel_id_t);
            using size_t             = ::NS(buffer_size_t);
            using status_t           = ::NS(arch_status_t);
            using result_register_t  = ::NS(arch_debugging_t);
            using address_t          = ::NS(buffer_addr_t);
            using addr_diff_t        = ::NS(buffer_addr_diff_t);

            bool success = false;

            status_t status = ::NS(ARCH_STATUS_SUCCESS);

            if( ( ctrl == nullptr ) ||
                ( ptr_kernel_config == nullptr ) ||
                ( ptr_kernel_config->needsUpdate() ) )
            {
                return success;
            }

            /* -------------------------------------------------------------------- */
            /* Prepare the cmp particles and particles addr buffer; the former is
             * used to verify the results of the test */

            c_buffer_t* particles_buffer  = ::NS(Buffer_new)( size_t{ 0 } );
            c_buffer_t* cmp_paddr_buffer  = ::NS(Buffer_new)( size_t{ 0 } );

            SIXTRL_ASSERT( cmp_paddr_buffer != nullptr );
            SIXTRL_ASSERT( particles_buffer != nullptr );

            size_t const slot_size =
                ::NS(Buffer_get_slot_size)( particles_buffer );

            SIXTRL_ASSERT( slot_size > size_t{ 0 } );

            size_t constexpr prng_seed = size_t{ 20190517 };

            size_t constexpr num_psets           = size_t{ 1 };
            size_t constexpr min_num_particles   = size_t{ 100 };
            size_t constexpr max_num_particles   = size_t{ 200 };

            /* Enforce that we always have  a NS(Particles) instance in this test */
            double const probabilty_to_not_particles = double{ 0.0 };

            status = ::NS(TestParticlesAddr_prepare_buffers)( particles_buffer,
                cmp_paddr_buffer, num_psets, min_num_particles, max_num_particles,
                    probabilty_to_not_particles, prng_seed );

            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            status = ::NS(ParticlesAddr_buffer_store_all_addresses)(
                cmp_paddr_buffer, particles_buffer );

            SIXRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            status = ::NS(TestParticlesAddr_verify_structure)(
                cmp_paddr_buffer, particles_buffer );

            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            status = ::NS(TestParticlesAddr_verify_addresses)(
                cmp_paddr_buffer, particles_buffer );

            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            /* --------------------------------------------------------------------- */
            /* Prepare the actual paddr_buffer that is used for the test */

            c_buffer_t* paddr_buffer = ::NS(Buffer_new)( size_t{ 0 } );

            SIXTRL_ASSERT( paddr_buffer != nullptr );

            status = ::NS(ParticlesAddr_prepare_buffer_based_on_particles_buffer)(
                paddr_buffer, particles_buffer );

            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            /* -------------------------------------------------------------------- */
            /* Init the Cuda controller and arguments for the addresses
             * and the particles */

            success = true;

            cuda_arg_t* particles_arg = ::NS(CudaArgument_new)( ctrl );
            SIXTRL_ASSERT( particles_arg != nullptr );

            cuda_arg_t* addresses_arg = ::NS(CudaArgument_new)( ctrl );
            SIXTRL_ASSERT( addresses_arg != nullptr );

            result_register_t result_register = ::NS(ARCH_DEBUGGING_GENERAL_FAILURE);

            cuda_arg_t* result_register_arg =
            ::NS(CudaArgument_new_raw_argument)(
                &result_register, sizeof( result_register ), ctrl );

            SIXTRL_ASSERT( result_register_arg != nullptr );

            /* Send the particles and the addresses buffer to the node */

            status = ::NS(Argument_send_buffer)( particles_arg, particles_buffer );
            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            status = ::NS(Argument_send_buffer)( addresses_arg, paddr_buffer );
            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

            /* reset the particles address buffer at the host side,
             * so that we can be certain about the success of the operation */

            ::NS(Buffer_clear)( paddr_buffer, true );
            ::NS(Buffer_reset)( paddr_buffer );

            SIXTRL_ASSERT( ::NS(Buffer_get_num_of_objects)( paddr_buffer ) ==
                size_t{ 0 } );

            /* ===================================================================== */
            /* !!! START OF THE ACTUAL TEST !!! */

            ::NS(Particles_buffer_store_all_addresses_cuda_wrapper)(
                ptr_kernel_config, addresses_arg, particles_arg,
                    result_register_arg );

            status = ::NS(Argument_receive_buffer)( paddr_buffer );
            SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS );
            SIXTRL_ASSERT( !::NS(Buffer_needs_remapping)( paddr_buffer ) );

            status = ::NS(Argument_receive_raw_arg)(
                &result_register, sizeof( result_register ) );
            SIXTRL_ASSERT( status == :NS(ARCH_STATUS_SUCCESS) );

            if( result_register != ::NS(ARCH_DEBUGGING_REGISTER_EMPTY) )
            {
                success = false;
            }

            success &= ( ::NS(TestParticlesAddr_verify_structure)( paddr_buffer,
               particles_buffer ) == ::NS(ARCH_STATUS_SUCCESS) );

            if( success )
            {
                status = ::NS(Argument_receive_buffer_without_remap)(
                    particles_arg, particles_buffer );
                SIXTRL_ASSERT( status == ::NS(ARCH_STATUS_SUCCESS) );

                address_t const device_begin_addr =
                ::NS(ManagedBuffer_get_stored_begin_addr)(
                    particles_buffer, slot_size );

                address_t const host_begin_addr =
                ::NS(ManagedBuffer_get_buffer_begin_addr)(
                    particles_buffer, slot_size );

                /* addr_offset = host_begin_addr - device_begin_addr -->
                 * host_begin_addr = device_begin_addr + addr_offset */

                addr_diff_t const addr_offset =
                    host_begin_addr - device_begin_addr;

            }

            /* Cleanup: */

            ::NS(Argument_delete)( particles_arg );
            ::NS(Argument_delete)( addresses_arg );
            ::NS(Argument_delete)( result_register_arg );

            ::NS(Buffer_delete)( paddr_buffer );
            ::NS(Buffer_delete)( particles_buffer );
            ::NS(Buffer_delete)( cmp_paddr_buffer );

            ::NS(Controller_delete)( ctrl );

            return success;
        }
    }
}

/* end: tests/sixtracklib/cuda/wrappers/test_track_job_wrappers_c99.cpp */
<commit_msg>tests/cuda: remove no longer needed tests file<commit_after><|endoftext|>


<commit_before>// __BEGIN_LICENSE__
// Copyright (C) 2006-2010 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <memory>
#include <gtest/gtest.h>

#include <vw/Core/Cache.h>
#include <vw/Core/FundamentalTypes.h>

using namespace vw;

// A dummy BlockGenerator that generates blocks of 1-byte data.
class BlockGenerator {

protected:
  int m_dimension;
  vw::uint8 m_fill_value;

public:
  BlockGenerator(int dimension, vw::uint8 fill_value = 0) :
    m_dimension(dimension), m_fill_value(fill_value) {}

  typedef vw::uint8 value_type;

  size_t size() const {
    return m_dimension * m_dimension * sizeof(vw::uint8);
  }

  boost::shared_ptr< value_type > generate() const {

    boost::shared_ptr< value_type > ptr(new vw::uint8[m_dimension*m_dimension]);
    (&(*ptr))[0] = m_fill_value;
    return ptr;
  }
};

class CacheTest : public ::testing::Test {
  public:
    typedef Cache::Handle<BlockGenerator> HandleT;
    typedef std::vector<HandleT> ContainerT;

    vw::Cache cache;
    ContainerT cache_handles;

    const static int dimension = 1024;
    const static int num_actual_blocks = 10;
    const static int num_cache_blocks = 3;

    CacheTest() :
      cache( num_cache_blocks*dimension*dimension ), cache_handles( num_actual_blocks ) {
        for (uint8 i = 0; i < num_actual_blocks; ++i) {
          cache_handles[i] = cache.insert( BlockGenerator( dimension, i ) );
        }
    }
};

TEST_F(CacheTest, CacheLineLRU) {
  // Test assumes cache size less than total data
  ASSERT_LT(+num_cache_blocks, +num_actual_blocks);

  for (int i = 0; i < num_actual_blocks; ++i) {

    Cache::Handle<BlockGenerator> &h = cache_handles[i];

    ASSERT_EQ(h.size(), dimension*dimension*sizeof(BlockGenerator::value_type));

    ASSERT_FALSE(h.valid());
    EXPECT_EQ(i, *h);
    EXPECT_TRUE(h.valid());

    boost::shared_ptr<BlockGenerator::value_type> ptr = h;
    EXPECT_EQ(i, *ptr);

    // LRU cache, so the last num_cache_blocks should still be valid
    for (int j = 0; i-j >= 0; ++j)
    {
      SCOPED_TRACE(::testing::Message() << "Cache block " << i);
      if (j < num_cache_blocks) {
        EXPECT_TRUE(cache_handles[i-j].valid());
      } else {
        EXPECT_FALSE(cache_handles[i-j].valid());
      }
    }
  }
}

TEST_F(CacheTest, Priority) {
  // Test assumes cache size less than total data
  ASSERT_LT(+num_cache_blocks, +num_actual_blocks);

  // prime the cache
  for (int i = 0; i < num_actual_blocks; ++i) {
    EXPECT_EQ(i, *cache_handles[i]);
  }

  //make sure last element is valid
  ASSERT_TRUE(cache_handles[num_actual_blocks-1].valid());

  // deprioritize the last element, and ensure it's still valid
  cache_handles[num_actual_blocks-1].deprioritize();
  ASSERT_TRUE(cache_handles[num_actual_blocks-1].valid());

  // bring the first element back into cache
  ASSERT_FALSE(cache_handles[0].valid());
  EXPECT_EQ( 0, *cache_handles[0] );
  EXPECT_TRUE(cache_handles[0].valid());

  // make sure that the deprioritized element dropped out of cache
  EXPECT_FALSE(cache_handles[num_actual_blocks-1].valid());
}

// Every copy increases the fill_value by one
class GenGen : public BlockGenerator {
  public:
    GenGen() : BlockGenerator(1, 0) {}

    GenGen(const GenGen& obj) :
      BlockGenerator(obj.m_dimension, boost::numeric_cast<uint8>(obj.m_fill_value+1)) {}
};

TEST(Cache, Types) {
  // This test makes sure cache handles can hold polymorphic types
  // and gives the user control over copying vs. not.
  typedef Cache::Handle<GenGen> obj_t;
  typedef Cache::Handle<boost::shared_ptr<GenGen> > sptr_t;

  vw::Cache cache(sizeof(vw::uint8));

  GenGen value;
  boost::shared_ptr<GenGen> shared(new GenGen());

  obj_t  h1 = cache.insert(value);
  sptr_t h2 = cache.insert(shared);

  // Make sure the value hasn't generated yet
  ASSERT_FALSE(h1.valid());
  ASSERT_FALSE(h2.valid());

  // value should have copied once
  EXPECT_EQ(1, *h1);
  // shared_ptr should not have copied
  EXPECT_EQ(0, *h2);
}

TEST(Cache, Stats) {
  typedef Cache::Handle<BlockGenerator> handle_t;

  // Cache can hold 2 items
  vw::Cache cache(2*sizeof(handle_t::value_type));

  handle_t h[3] = {
    cache.insert(BlockGenerator(1, 0)),
    cache.insert(BlockGenerator(1, 1)),
    cache.insert(BlockGenerator(1, 2))};

  EXPECT_EQ(0u, cache.hits());
  EXPECT_EQ(0u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // miss
  EXPECT_EQ(0, *h[0]);

  EXPECT_EQ(0u, cache.hits());
  EXPECT_EQ(1u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // hit
  EXPECT_EQ(0, *h[0]);

  EXPECT_EQ(1u, cache.hits());
  EXPECT_EQ(1u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // miss
  EXPECT_EQ(1, *h[1]);

  EXPECT_EQ(1u, cache.hits());
  EXPECT_EQ(2u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // hit
  EXPECT_EQ(1, *h[1]);

  EXPECT_EQ(2u, cache.hits());
  EXPECT_EQ(2u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // miss, eviction
  EXPECT_EQ(2, *h[2]);

  EXPECT_EQ(2u, cache.hits());
  EXPECT_EQ(3u, cache.misses());
  EXPECT_EQ(1u, cache.evictions());

  // hit
  EXPECT_EQ(2, *h[2]);

  EXPECT_EQ(3u, cache.hits());
  EXPECT_EQ(3u, cache.misses());
  EXPECT_EQ(1u, cache.evictions());

  // hit
  EXPECT_EQ(1, *h[1]);

  EXPECT_EQ(4u, cache.hits());
  EXPECT_EQ(3u, cache.misses());
  EXPECT_EQ(1u, cache.evictions());

  // miss, eviction
  EXPECT_EQ(0, *h[0]);

  EXPECT_EQ(4u, cache.hits());
  EXPECT_EQ(4u, cache.misses());
  EXPECT_EQ(2u, cache.evictions());

  cache.clear_stats();

  EXPECT_EQ(0u, cache.hits());
  EXPECT_EQ(0u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());
}
<commit_msg>fix leaky test.<commit_after>// __BEGIN_LICENSE__
// Copyright (C) 2006-2010 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <memory>
#include <gtest/gtest.h>

#include <vw/Core/Cache.h>
#include <vw/Core/FundamentalTypes.h>
#include <boost/shared_array.hpp>

using namespace vw;

// A dummy BlockGenerator that generates blocks of 1-byte data.
class BlockGenerator {

protected:
  int m_dimension;
  vw::uint8 m_fill_value;

public:
  BlockGenerator(int dimension, vw::uint8 fill_value = 0) :
    m_dimension(dimension), m_fill_value(fill_value) {}

  typedef vw::uint8 value_type;

  size_t size() const {
    return m_dimension * m_dimension * sizeof(vw::uint8);
  }

  boost::shared_ptr< value_type > generate() const {

    boost::shared_ptr< value_type > ptr(new vw::uint8[m_dimension*m_dimension], boost::checked_array_deleter<value_type>());
    (&(*ptr))[0] = m_fill_value;
    return ptr;
  }
};

class CacheTest : public ::testing::Test {
  public:
    typedef Cache::Handle<BlockGenerator> HandleT;
    typedef std::vector<HandleT> ContainerT;

    vw::Cache cache;
    ContainerT cache_handles;

    const static int dimension = 1024;
    const static int num_actual_blocks = 10;
    const static int num_cache_blocks = 3;

    CacheTest() :
      cache( num_cache_blocks*dimension*dimension ), cache_handles( num_actual_blocks ) {
        for (uint8 i = 0; i < num_actual_blocks; ++i) {
          cache_handles[i] = cache.insert( BlockGenerator( dimension, i ) );
        }
    }
};

TEST_F(CacheTest, CacheLineLRU) {
  // Test assumes cache size less than total data
  ASSERT_LT(+num_cache_blocks, +num_actual_blocks);

  for (int i = 0; i < num_actual_blocks; ++i) {

    Cache::Handle<BlockGenerator> &h = cache_handles[i];

    ASSERT_EQ(h.size(), dimension*dimension*sizeof(BlockGenerator::value_type));

    ASSERT_FALSE(h.valid());
    EXPECT_EQ(i, *h);
    EXPECT_TRUE(h.valid());

    boost::shared_ptr<BlockGenerator::value_type> ptr = h;
    EXPECT_EQ(i, *ptr);

    // LRU cache, so the last num_cache_blocks should still be valid
    for (int j = 0; i-j >= 0; ++j)
    {
      SCOPED_TRACE(::testing::Message() << "Cache block " << i);
      if (j < num_cache_blocks) {
        EXPECT_TRUE(cache_handles[i-j].valid());
      } else {
        EXPECT_FALSE(cache_handles[i-j].valid());
      }
    }
  }
}

TEST_F(CacheTest, Priority) {
  // Test assumes cache size less than total data
  ASSERT_LT(+num_cache_blocks, +num_actual_blocks);

  // prime the cache
  for (int i = 0; i < num_actual_blocks; ++i) {
    EXPECT_EQ(i, *cache_handles[i]);
  }

  //make sure last element is valid
  ASSERT_TRUE(cache_handles[num_actual_blocks-1].valid());

  // deprioritize the last element, and ensure it's still valid
  cache_handles[num_actual_blocks-1].deprioritize();
  ASSERT_TRUE(cache_handles[num_actual_blocks-1].valid());

  // bring the first element back into cache
  ASSERT_FALSE(cache_handles[0].valid());
  EXPECT_EQ( 0, *cache_handles[0] );
  EXPECT_TRUE(cache_handles[0].valid());

  // make sure that the deprioritized element dropped out of cache
  EXPECT_FALSE(cache_handles[num_actual_blocks-1].valid());
}

// Every copy increases the fill_value by one
class GenGen : public BlockGenerator {
  public:
    GenGen() : BlockGenerator(1, 0) {}

    GenGen(const GenGen& obj) :
      BlockGenerator(obj.m_dimension, boost::numeric_cast<uint8>(obj.m_fill_value+1)) {}
};

TEST(Cache, Types) {
  // This test makes sure cache handles can hold polymorphic types
  // and gives the user control over copying vs. not.
  typedef Cache::Handle<GenGen> obj_t;
  typedef Cache::Handle<boost::shared_ptr<GenGen> > sptr_t;

  vw::Cache cache(sizeof(vw::uint8));

  GenGen value;
  boost::shared_ptr<GenGen> shared(new GenGen());

  obj_t  h1 = cache.insert(value);
  sptr_t h2 = cache.insert(shared);

  // Make sure the value hasn't generated yet
  ASSERT_FALSE(h1.valid());
  ASSERT_FALSE(h2.valid());

  // value should have copied once
  EXPECT_EQ(1, *h1);
  // shared_ptr should not have copied
  EXPECT_EQ(0, *h2);
}

TEST(Cache, Stats) {
  typedef Cache::Handle<BlockGenerator> handle_t;

  // Cache can hold 2 items
  vw::Cache cache(2*sizeof(handle_t::value_type));

  handle_t h[3] = {
    cache.insert(BlockGenerator(1, 0)),
    cache.insert(BlockGenerator(1, 1)),
    cache.insert(BlockGenerator(1, 2))};

  EXPECT_EQ(0u, cache.hits());
  EXPECT_EQ(0u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // miss
  EXPECT_EQ(0, *h[0]);

  EXPECT_EQ(0u, cache.hits());
  EXPECT_EQ(1u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // hit
  EXPECT_EQ(0, *h[0]);

  EXPECT_EQ(1u, cache.hits());
  EXPECT_EQ(1u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // miss
  EXPECT_EQ(1, *h[1]);

  EXPECT_EQ(1u, cache.hits());
  EXPECT_EQ(2u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // hit
  EXPECT_EQ(1, *h[1]);

  EXPECT_EQ(2u, cache.hits());
  EXPECT_EQ(2u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());

  // miss, eviction
  EXPECT_EQ(2, *h[2]);

  EXPECT_EQ(2u, cache.hits());
  EXPECT_EQ(3u, cache.misses());
  EXPECT_EQ(1u, cache.evictions());

  // hit
  EXPECT_EQ(2, *h[2]);

  EXPECT_EQ(3u, cache.hits());
  EXPECT_EQ(3u, cache.misses());
  EXPECT_EQ(1u, cache.evictions());

  // hit
  EXPECT_EQ(1, *h[1]);

  EXPECT_EQ(4u, cache.hits());
  EXPECT_EQ(3u, cache.misses());
  EXPECT_EQ(1u, cache.evictions());

  // miss, eviction
  EXPECT_EQ(0, *h[0]);

  EXPECT_EQ(4u, cache.hits());
  EXPECT_EQ(4u, cache.misses());
  EXPECT_EQ(2u, cache.evictions());

  cache.clear_stats();

  EXPECT_EQ(0u, cache.hits());
  EXPECT_EQ(0u, cache.misses());
  EXPECT_EQ(0u, cache.evictions());
}
<|endoftext|>


<commit_before>#include "event_base.h"
#include "logging.h"
#include "util.h"
#include <map>
#include <string.h>
#include <fcntl.h>
#include "poller.h"
#include "conn.h"
using namespace std;

namespace handy {

namespace {

struct TimerRepeatable {
    int64_t at; //current timer timeout timestamp
    int64_t interval;
    TimerId timerid;
    Task cb;
};

struct IdleNode {
    TcpConnPtr con_;
    int64_t updated_;
    TcpCallBack cb_;
};

}

struct IdleIdImp {
    IdleIdImp() {}
    typedef list<IdleNode>::iterator Iter;
    IdleIdImp(list<IdleNode>* lst, Iter iter): lst_(lst), iter_(iter){}
    list<IdleNode>* lst_;
    Iter iter_;
};

struct EventsImp {
    EventBase* base_;
    PollerBase* poller_;
    std::atomic<bool> exit_;
    int wakeupFds_[2];
    int nextTimeout_;
    SafeQueue<Task> tasks_;

    std::map<TimerId, TimerRepeatable> timerReps_;
    std::map<TimerId, Task> timers_;
    std::atomic<int64_t> timerSeq_;
    std::map<int, std::list<IdleNode>> idleConns_;
    std::set<TcpConnPtr> reconnectConns_;
    bool idleEnabled;

    EventsImp(EventBase* base, int taskCap);
    ~EventsImp();
    void init();
    void callIdles();
    IdleId registerIdle(int idle, const TcpConnPtr& con, const TcpCallBack& cb);
    void unregisterIdle(const IdleId& id);
    void updateIdle(const IdleId& id);
    void handleTimeouts();
    void refreshNearest(const TimerId* tid=NULL);
    void repeatableTimeout(TimerRepeatable* tr);

    //eventbase functions
    EventBase& exit();
    bool exited() { return exit_; }
    void safeCall(Task&& task) { tasks_.push(move(task)); wakeup(); }
    void loop() { while (!exit_) loop_once(10000); loop_once(0); }
    void loop_once(int waitMs) { poller_->loop_once(std::min(waitMs, nextTimeout_)); handleTimeouts(); }
    void wakeup() {
        int r = write(wakeupFds_[1], "", 1);
        fatalif(r<=0, "write error wd %d %d %s", r, errno, strerror(errno));
    }

    bool cancel(TimerId timerid);
    TimerId runAt(int64_t milli, Task&& task, int64_t interval);
};

EventBase::EventBase(int taskCapacity) {
    imp_.reset(new EventsImp(this, taskCapacity));
    imp_->init();
}

EventBase::~EventBase() {}

EventBase& EventBase::exit() { return imp_->exit(); }

bool EventBase::exited() { return imp_->exited(); }

void EventBase::safeCall(Task&& task) { imp_->safeCall(move(task)); }

void EventBase::wakeup(){ imp_->wakeup(); }

void EventBase::loop() { imp_->loop(); }

void EventBase::loop_once(int waitMs) { imp_->loop_once(waitMs); }

bool EventBase::cancel(TimerId timerid) { return imp_->cancel(timerid); }

TimerId EventBase::runAt(int64_t milli, Task&& task, int64_t interval) {
    return imp_->runAt(milli, std::move(task), interval);
}

EventsImp::EventsImp(EventBase* base, int taskCap):
    base_(base), poller_(new PlatformPoller()), exit_(false), nextTimeout_(1<<30), tasks_(taskCap),
    timerSeq_(0), idleEnabled(false)
{
}

void EventsImp::init() {
    int r = pipe(wakeupFds_);
    fatalif(r, "pipe failed %d %s", errno, strerror(errno));
    r = util::addFdFlag(wakeupFds_[0], FD_CLOEXEC);
    fatalif(r, "addFdFlag failed %d %s", errno, strerror(errno));
    r = util::addFdFlag(wakeupFds_[1], FD_CLOEXEC);
    fatalif(r, "addFdFlag failed %d %s", errno, strerror(errno));
    trace("wakeup pipe created %d %d", wakeupFds_[0], wakeupFds_[1]);
    Channel* ch = new Channel(base_, wakeupFds_[0], kReadEvent);
    ch->onRead([=] {
        char buf[1024];
        int r = ch->fd() >= 0 ? ::read(ch->fd(), buf, sizeof buf) : 0;
        if (r > 0) {
            Task task;
            while (tasks_.pop_wait(&task, 0)) {
                task();
            }
        } else if (r == 0) {
            delete ch;
        } else if (errno == EINTR) {
        } else {
            fatal("wakeup channel read error %d %d %s", r, errno, strerror(errno));
        }
    });
}

EventBase& EventsImp::exit() {
    exit_ = true;
    wakeup();
    timerReps_.clear();
    timers_.clear();
    idleConns_.clear();
    for (auto recon: reconnectConns_) { //重连的连接无法通过channel清理，因此单独清理
        recon->cleanup(recon);
    }
    return *base_; }

void EventsImp::handleTimeouts() {
    int64_t now = util::timeMilli();
    TimerId tid { now, 1L<<62 };
    while (timers_.size() && timers_.begin()->first < tid) {
        Task task = move(timers_.begin()->second);
        timers_.erase(timers_.begin());
        task();
    }
    refreshNearest();
}

EventsImp::~EventsImp() {
    delete poller_;
    ::close(wakeupFds_[1]);
}

void EventsImp::callIdles() {
    int64_t now = util::timeMilli() / 1000;
    for (auto& l: idleConns_) {
        int idle = l.first;
        auto lst = l.second;
        while(lst.size()) {
            IdleNode& node = lst.front();
            if (node.updated_ + idle > now) {
                break;
            }
            node.updated_ = now;
            lst.splice(lst.end(), lst, lst.begin());
            node.cb_(node.con_);
        }
    }
}

IdleId EventsImp::registerIdle(int idle, const TcpConnPtr& con, const TcpCallBack& cb) {
    if (!idleEnabled) {
        base_->runAfter(1000, [this] { callIdles(); }, 1000);
        idleEnabled = true;
    }
    auto& lst = idleConns_[idle];
    lst.push_back(IdleNode {con, util::timeMilli()/1000, move(cb) });
    trace("register idle");
    return IdleId(new IdleIdImp(&lst, --lst.end()));
}

void EventsImp::unregisterIdle(const IdleId& id) {
    trace("unregister idle");
    id->lst_->erase(id->iter_);
}

void EventsImp::updateIdle(const IdleId& id) {
    trace("update idle");
    id->iter_->updated_ = util::timeMilli() / 1000;
    id->lst_->splice(id->lst_->end(), *id->lst_, id->iter_);
}

void EventsImp::refreshNearest(const TimerId* tid){
    if (timers_.empty()) {
        nextTimeout_ = 1 << 30;
    } else {
        const TimerId &t = timers_.begin()->first;
        nextTimeout_ = t.first - util::timeMilli();
        nextTimeout_ = nextTimeout_ < 0 ? 0 : nextTimeout_;
    }
}

void EventsImp::repeatableTimeout(TimerRepeatable* tr) {
    tr->at += tr->interval;
    tr->timerid = { tr->at, ++timerSeq_ };
    timers_[tr->timerid] = [this, tr] { repeatableTimeout(tr); };
    refreshNearest(&tr->timerid);
    tr->cb();
}

TimerId EventsImp::runAt(int64_t milli, Task&& task, int64_t interval) {
    if (exit_) {
        return TimerId();
    }
    if (interval) {
        TimerId tid { -milli, ++timerSeq_};
        TimerRepeatable& rtr = timerReps_[tid];
        rtr = { milli, interval, { milli, ++timerSeq_ }, move(task) };
        TimerRepeatable* tr = &rtr;
        timers_[tr->timerid] = [this, tr] { repeatableTimeout(tr); };
        refreshNearest(&tr->timerid);
        return tid;
    } else {
        TimerId tid { milli, ++timerSeq_};
        timers_.insert({tid, move(task)});
        refreshNearest(&tid);
        return tid;
    }
}

bool EventsImp::cancel(TimerId timerid) {
    if (timerid.first < 0) {
        auto p = timerReps_.find(timerid);
        auto ptimer = timers_.find(p->second.timerid);
        if (ptimer != timers_.end()) {
            timers_.erase(ptimer);
        }
        timerReps_.erase(p);
        return true;
    } else {
        auto p = timers_.find(timerid);
        if (p != timers_.end()) {
            timers_.erase(p);
            return true;
        }
        return false;
    }
}

void MultiBase::loop() {
    int sz = bases_.size();
    vector<thread> ths(sz -1);
    for(int i = 0; i < sz -1; i++) {
        thread t([this, i]{ bases_[i].loop();});
        ths[i].swap(t);
    }
    bases_.back().loop();
    for (int i = 0; i < sz -1; i++) {
        ths[i].join();
    }
}

Channel::Channel(EventBase* base, int fd, int events): base_(base), fd_(fd), events_(events) {
    fatalif(net::setNonBlock(fd_) < 0, "channel set non block failed");
    static atomic<int64_t> id(0);
    id_ = ++id;
    poller_ = base_->imp_->poller_;
    poller_->addChannel(this);
}

Channel::~Channel() {
    close();
}

void Channel::enableRead(bool enable) {
    if (enable) {
        events_ |= kReadEvent;
    } else {
        events_ &= ~kReadEvent;
    }
    poller_->updateChannel(this);
}

void Channel::enableWrite(bool enable) {
    if (enable) {
        events_ |= kWriteEvent;
    } else {
        events_ &= ~kWriteEvent;
    }
    poller_->updateChannel(this);
}

void Channel::enableReadWrite(bool readable, bool writable) {
    if (readable) {
        events_ |= kReadEvent;
    } else {
        events_ &= ~kReadEvent;
    }
    if (writable) {
        events_ |= kWriteEvent;
    } else {
        events_ &= ~kWriteEvent;
    }
    poller_->updateChannel(this);
}

void Channel::close() {
    if (fd_>=0) {
        trace("close channel %ld fd %d", (long)id_, fd_);
        poller_->removeChannel(this);
        ::close(fd_);
        fd_ = -1;
        handleRead();
    }
}

bool Channel::readEnabled() { return events_ & kReadEvent; }
bool Channel::writeEnabled() { return events_ & kWriteEvent; }

void handyUnregisterIdle(EventBase* base, const IdleId& idle) {
    base->imp_->unregisterIdle(idle);
}

void handyUpdateIdle(EventBase* base, const IdleId& idle) {
    base->imp_->updateIdle(idle);
}

TcpConn::TcpConn()
:base_(NULL), channel_(NULL), state_(State::Invalid), destPort_(-1), connectTimeout_(0), reconnectInterval_(-1)
{
}

TcpConn::~TcpConn() {
    trace("tcp destroyed %s - %s", local_.toString().c_str(), peer_.toString().c_str());
    delete channel_;
}

void TcpConn::addIdleCB(int idle, const TcpCallBack& cb) {
    if (channel_) {
        idleIds_.push_back(getBase()->imp_->registerIdle(idle, shared_from_this(), cb));
    }
}

void TcpConn::reconnect() {
    auto con = shared_from_this();
    getBase()->imp_->reconnectConns_.insert(con);
    getBase()->runAfter(reconnectInterval_, [this, con]() {
        getBase()->imp_->reconnectConns_.erase(con);
        connect(getBase(), destHost_, (short)destPort_, connectTimeout_, localIp_);
    });
    delete channel_;
    channel_ = NULL;
}

}<commit_msg>move clean operation from exit to loop, keep exit async call ok<commit_after>#include "event_base.h"
#include "logging.h"
#include "util.h"
#include <map>
#include <string.h>
#include <fcntl.h>
#include "poller.h"
#include "conn.h"
using namespace std;

namespace handy {

namespace {

struct TimerRepeatable {
    int64_t at; //current timer timeout timestamp
    int64_t interval;
    TimerId timerid;
    Task cb;
};

struct IdleNode {
    TcpConnPtr con_;
    int64_t updated_;
    TcpCallBack cb_;
};

}

struct IdleIdImp {
    IdleIdImp() {}
    typedef list<IdleNode>::iterator Iter;
    IdleIdImp(list<IdleNode>* lst, Iter iter): lst_(lst), iter_(iter){}
    list<IdleNode>* lst_;
    Iter iter_;
};

struct EventsImp {
    EventBase* base_;
    PollerBase* poller_;
    std::atomic<bool> exit_;
    int wakeupFds_[2];
    int nextTimeout_;
    SafeQueue<Task> tasks_;

    std::map<TimerId, TimerRepeatable> timerReps_;
    std::map<TimerId, Task> timers_;
    std::atomic<int64_t> timerSeq_;
    std::map<int, std::list<IdleNode>> idleConns_;
    std::set<TcpConnPtr> reconnectConns_;
    bool idleEnabled;

    EventsImp(EventBase* base, int taskCap);
    ~EventsImp();
    void init();
    void callIdles();
    IdleId registerIdle(int idle, const TcpConnPtr& con, const TcpCallBack& cb);
    void unregisterIdle(const IdleId& id);
    void updateIdle(const IdleId& id);
    void handleTimeouts();
    void refreshNearest(const TimerId* tid=NULL);
    void repeatableTimeout(TimerRepeatable* tr);

    //eventbase functions
    EventBase& exit() {exit_ = true; wakeup(); return *base_;}
    bool exited() { return exit_; }
    void safeCall(Task&& task) { tasks_.push(move(task)); wakeup(); }
    void loop();
    void loop_once(int waitMs) { poller_->loop_once(std::min(waitMs, nextTimeout_)); handleTimeouts(); }
    void wakeup() {
        int r = write(wakeupFds_[1], "", 1);
        fatalif(r<=0, "write error wd %d %d %s", r, errno, strerror(errno));
    }

    bool cancel(TimerId timerid);
    TimerId runAt(int64_t milli, Task&& task, int64_t interval);
};

EventBase::EventBase(int taskCapacity) {
    imp_.reset(new EventsImp(this, taskCapacity));
    imp_->init();
}

EventBase::~EventBase() {}

EventBase& EventBase::exit() { return imp_->exit(); }

bool EventBase::exited() { return imp_->exited(); }

void EventBase::safeCall(Task&& task) { imp_->safeCall(move(task)); }

void EventBase::wakeup(){ imp_->wakeup(); }

void EventBase::loop() { imp_->loop(); }

void EventBase::loop_once(int waitMs) { imp_->loop_once(waitMs); }

bool EventBase::cancel(TimerId timerid) { return imp_->cancel(timerid); }

TimerId EventBase::runAt(int64_t milli, Task&& task, int64_t interval) {
    return imp_->runAt(milli, std::move(task), interval);
}

EventsImp::EventsImp(EventBase* base, int taskCap):
    base_(base), poller_(new PlatformPoller()), exit_(false), nextTimeout_(1<<30), tasks_(taskCap),
    timerSeq_(0), idleEnabled(false)
{
}

void EventsImp::loop() {
    while (!exit_)
        loop_once(10000);
    timerReps_.clear();
    timers_.clear();
    idleConns_.clear();
    for (auto recon: reconnectConns_) { //重连的连接无法通过channel清理，因此单独清理
        recon->cleanup(recon);
    }
    loop_once(0);
}

void EventsImp::init() {
    int r = pipe(wakeupFds_);
    fatalif(r, "pipe failed %d %s", errno, strerror(errno));
    r = util::addFdFlag(wakeupFds_[0], FD_CLOEXEC);
    fatalif(r, "addFdFlag failed %d %s", errno, strerror(errno));
    r = util::addFdFlag(wakeupFds_[1], FD_CLOEXEC);
    fatalif(r, "addFdFlag failed %d %s", errno, strerror(errno));
    trace("wakeup pipe created %d %d", wakeupFds_[0], wakeupFds_[1]);
    Channel* ch = new Channel(base_, wakeupFds_[0], kReadEvent);
    ch->onRead([=] {
        char buf[1024];
        int r = ch->fd() >= 0 ? ::read(ch->fd(), buf, sizeof buf) : 0;
        if (r > 0) {
            Task task;
            while (tasks_.pop_wait(&task, 0)) {
                task();
            }
        } else if (r == 0) {
            delete ch;
        } else if (errno == EINTR) {
        } else {
            fatal("wakeup channel read error %d %d %s", r, errno, strerror(errno));
        }
    });
}

void EventsImp::handleTimeouts() {
    int64_t now = util::timeMilli();
    TimerId tid { now, 1L<<62 };
    while (timers_.size() && timers_.begin()->first < tid) {
        Task task = move(timers_.begin()->second);
        timers_.erase(timers_.begin());
        task();
    }
    refreshNearest();
}

EventsImp::~EventsImp() {
    delete poller_;
    ::close(wakeupFds_[1]);
}

void EventsImp::callIdles() {
    int64_t now = util::timeMilli() / 1000;
    for (auto& l: idleConns_) {
        int idle = l.first;
        auto lst = l.second;
        while(lst.size()) {
            IdleNode& node = lst.front();
            if (node.updated_ + idle > now) {
                break;
            }
            node.updated_ = now;
            lst.splice(lst.end(), lst, lst.begin());
            node.cb_(node.con_);
        }
    }
}

IdleId EventsImp::registerIdle(int idle, const TcpConnPtr& con, const TcpCallBack& cb) {
    if (!idleEnabled) {
        base_->runAfter(1000, [this] { callIdles(); }, 1000);
        idleEnabled = true;
    }
    auto& lst = idleConns_[idle];
    lst.push_back(IdleNode {con, util::timeMilli()/1000, move(cb) });
    trace("register idle");
    return IdleId(new IdleIdImp(&lst, --lst.end()));
}

void EventsImp::unregisterIdle(const IdleId& id) {
    trace("unregister idle");
    id->lst_->erase(id->iter_);
}

void EventsImp::updateIdle(const IdleId& id) {
    trace("update idle");
    id->iter_->updated_ = util::timeMilli() / 1000;
    id->lst_->splice(id->lst_->end(), *id->lst_, id->iter_);
}

void EventsImp::refreshNearest(const TimerId* tid){
    if (timers_.empty()) {
        nextTimeout_ = 1 << 30;
    } else {
        const TimerId &t = timers_.begin()->first;
        nextTimeout_ = t.first - util::timeMilli();
        nextTimeout_ = nextTimeout_ < 0 ? 0 : nextTimeout_;
    }
}

void EventsImp::repeatableTimeout(TimerRepeatable* tr) {
    tr->at += tr->interval;
    tr->timerid = { tr->at, ++timerSeq_ };
    timers_[tr->timerid] = [this, tr] { repeatableTimeout(tr); };
    refreshNearest(&tr->timerid);
    tr->cb();
}

TimerId EventsImp::runAt(int64_t milli, Task&& task, int64_t interval) {
    if (exit_) {
        return TimerId();
    }
    if (interval) {
        TimerId tid { -milli, ++timerSeq_};
        TimerRepeatable& rtr = timerReps_[tid];
        rtr = { milli, interval, { milli, ++timerSeq_ }, move(task) };
        TimerRepeatable* tr = &rtr;
        timers_[tr->timerid] = [this, tr] { repeatableTimeout(tr); };
        refreshNearest(&tr->timerid);
        return tid;
    } else {
        TimerId tid { milli, ++timerSeq_};
        timers_.insert({tid, move(task)});
        refreshNearest(&tid);
        return tid;
    }
}

bool EventsImp::cancel(TimerId timerid) {
    if (timerid.first < 0) {
        auto p = timerReps_.find(timerid);
        auto ptimer = timers_.find(p->second.timerid);
        if (ptimer != timers_.end()) {
            timers_.erase(ptimer);
        }
        timerReps_.erase(p);
        return true;
    } else {
        auto p = timers_.find(timerid);
        if (p != timers_.end()) {
            timers_.erase(p);
            return true;
        }
        return false;
    }
}

void MultiBase::loop() {
    int sz = bases_.size();
    vector<thread> ths(sz -1);
    for(int i = 0; i < sz -1; i++) {
        thread t([this, i]{ bases_[i].loop();});
        ths[i].swap(t);
    }
    bases_.back().loop();
    for (int i = 0; i < sz -1; i++) {
        ths[i].join();
    }
}

Channel::Channel(EventBase* base, int fd, int events): base_(base), fd_(fd), events_(events) {
    fatalif(net::setNonBlock(fd_) < 0, "channel set non block failed");
    static atomic<int64_t> id(0);
    id_ = ++id;
    poller_ = base_->imp_->poller_;
    poller_->addChannel(this);
}

Channel::~Channel() {
    close();
}

void Channel::enableRead(bool enable) {
    if (enable) {
        events_ |= kReadEvent;
    } else {
        events_ &= ~kReadEvent;
    }
    poller_->updateChannel(this);
}

void Channel::enableWrite(bool enable) {
    if (enable) {
        events_ |= kWriteEvent;
    } else {
        events_ &= ~kWriteEvent;
    }
    poller_->updateChannel(this);
}

void Channel::enableReadWrite(bool readable, bool writable) {
    if (readable) {
        events_ |= kReadEvent;
    } else {
        events_ &= ~kReadEvent;
    }
    if (writable) {
        events_ |= kWriteEvent;
    } else {
        events_ &= ~kWriteEvent;
    }
    poller_->updateChannel(this);
}

void Channel::close() {
    if (fd_>=0) {
        trace("close channel %ld fd %d", (long)id_, fd_);
        poller_->removeChannel(this);
        ::close(fd_);
        fd_ = -1;
        handleRead();
    }
}

bool Channel::readEnabled() { return events_ & kReadEvent; }
bool Channel::writeEnabled() { return events_ & kWriteEvent; }

void handyUnregisterIdle(EventBase* base, const IdleId& idle) {
    base->imp_->unregisterIdle(idle);
}

void handyUpdateIdle(EventBase* base, const IdleId& idle) {
    base->imp_->updateIdle(idle);
}

TcpConn::TcpConn()
:base_(NULL), channel_(NULL), state_(State::Invalid), destPort_(-1), connectTimeout_(0), reconnectInterval_(-1)
{
}

TcpConn::~TcpConn() {
    trace("tcp destroyed %s - %s", local_.toString().c_str(), peer_.toString().c_str());
    delete channel_;
}

void TcpConn::addIdleCB(int idle, const TcpCallBack& cb) {
    if (channel_) {
        idleIds_.push_back(getBase()->imp_->registerIdle(idle, shared_from_this(), cb));
    }
}

void TcpConn::reconnect() {
    auto con = shared_from_this();
    getBase()->imp_->reconnectConns_.insert(con);
    getBase()->runAfter(reconnectInterval_, [this, con]() {
        getBase()->imp_->reconnectConns_.erase(con);
        connect(getBase(), destHost_, (short)destPort_, connectTimeout_, localIp_);
    });
    delete channel_;
    channel_ = NULL;
}

}<|endoftext|>