<commit_before>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QITYPE_DETAILS_TYPETUPLE_HXX_
#define _QITYPE_DETAILS_TYPETUPLE_HXX_

#include <qi/preproc.hpp>

namespace qi
{
  namespace detail {
    template<typename T> void setFromStorage(T& ref, void* storage)
    {
      ref = *(T*)typeOf<T>()->ptrFromStorage(&storage);
    }
  }
}

#define QI_TYPE_STRUCT_DECLARE(name)                                      \
namespace qi {                                                            \
  template<> struct TypeImpl<name>: public TypeTuple                      \
  {                                                                       \
  public:                                                                 \
    virtual std::vector<Type*> memberTypes();                             \
    virtual void* get(void* storage, unsigned int index);                 \
    virtual void set(void** storage, unsigned int index, void* valStorage); \
    _QI_BOUNCE_TYPE_METHODS(DefaultTypeImplMethods<name>);                \
 }; }

#define __QI_TUPLE_TYPE(_, what, field) res.push_back(typeOf(ptr->field));
#define __QI_TUPLE_GET(_, what, field) if (i == index) return typeOf(ptr->field)->initializeStorage(&ptr->field); i++;
#define __QI_TUPLE_SET(_, what, field) if (i == index) detail::setFromStorage(ptr->field, valueStorage); i++;
#define __QI_TYPE_STRUCT_IMPLEMENT(name, inl, onSet, ...)                                    \
namespace qi {                                                                        \
  inl std::vector<Type*> TypeImpl<name>::memberTypes()                                \
  {                                                                                \
    name* ptr = 0;                                                                    \
    std::vector<Type*> res;                                                           \
    QI_VAARGS_APPLY(__QI_TUPLE_TYPE, _, __VA_ARGS__);                                 \
    return res;                                                                       \
  }                                                                                \
  inl void* TypeImpl<name>::get(void* storage, unsigned int index)                    \
  {                                                                                \
    unsigned int i = 0;                                                                        \
    name* ptr = (name*)ptrFromStorage(&storage);                                      \
    QI_VAARGS_APPLY(__QI_TUPLE_GET, _, __VA_ARGS__);                                  \
    return 0;                                                                         \
  }                                                                                \
  inl void TypeImpl<name>::set(void** storage, unsigned int index, void* valueStorage)\
  {                                                                                \
    unsigned int i=0;                                                                 \
    name* ptr = (name*)ptrFromStorage(storage);                                       \
    QI_VAARGS_APPLY(__QI_TUPLE_SET, _, __VA_ARGS__);                                  \
    onSet                                                                      \
  }\
}

/// Allow the QI_TYPE_STRUCT macro and variants to access private members
#define QI_TYPE_STRUCT_PRIVATE_ACCESS(name) \
friend class qi::TypeImpl<name>;

/** Declare a simple struct to the type system.
 * First argument is the structure name. Remaining arguments are the structure
 * fields.
 * This macro must be called outside any namespace.
 * This macro should be called in the header file defining the structure 'name',
 * or in a header included by all source files using the structure.
 * See QI_TYPE_STRUCT_REGISTER for a similar macro that can be called from a
 * single source file.
 */
#define QI_TYPE_STRUCT(name, ...) \
  QI_TYPE_STRUCT_DECLARE(name) \
  __QI_TYPE_STRUCT_IMPLEMENT(name, inline, /**/, __VA_ARGS__)

/** Similar to QI_TYPE_STRUCT, but evaluates 'onSet' after writting to an instance.
 * The instance is accessible through the variable 'ptr'.
 */
#define QI_TYPE_STRUCT_EX(name, onSet, ...) \
  QI_TYPE_STRUCT_DECLARE(name) \
  __QI_TYPE_STRUCT_IMPLEMENT(name, inline, onSet, __VA_ARGS__)

#define QI_TYPE_STRUCT_IMPLEMENT(name, ...) \
  __QI_TYPE_STRUCT_IMPLEMENT(name, /**/, /**/, __VA_ARGS__)

/** Similar to QI_TYPE_STRUCT, but using the runtime factory instead of the
 * compile-time template.
 *
 */
#define QI_TYPE_STRUCT_REGISTER(name, ...) \
namespace _qi_ {                           \
    QI_TYPE_STRUCT(name, __VA_ARGS__);     \
}                                          \
QI_TYPE_REGISTER_CUSTOM(name, _qi_::qi::TypeImpl<name>)

/** Declares that name is equivalent to type bounceTo, and that instances
 * can be converted using the conversion function with signature 'bounceTo* (name*)'.
 * This macro should be called in a header included by all code using the 'name'
 * class.
 * See QI_TYPE_STRUCT_BOUNCE_REGISTER for a similar macro that can be called from a
 * single source file.
 */
#define QI_TYPE_STRUCT_BOUNCE(name, bounceTo, conversion)                 \
namespace qi {                                                            \
template<> class TypeImpl<name>: public ::qi::TypeTupleBouncer<name, bounceTo>  \
{                                                                         \
public:                                                                   \
  void adaptStorage(void** storage, void** adapted)                       \
  {                                                                       \
    name* ptr = (name*)ptrFromStorage(storage);                           \
    bounceTo * tptr = conversion(ptr);                                    \
    *adapted = bounceType()->initializeStorage(tptr);                     \
  }                                                                       \
};}

/** Similar to QI_TYPE_STRUCT_BOUNCE, but using the runtime factory instead of the
 * compile-time template.
 */
#define QI_TYPE_STRUCT_BOUNCE_REGISTER(name, bounceTo, conversion)        \
namespace _qi_ {                                                          \
    QI_TYPE_STRUCT_BOUNCE(name, bounceTo, conversion);                    \
}                                                                         \
QI_TYPE_REGISTER_CUSTOM(name, _qi_::qi::TypeImpl<name>)

namespace qi {
  template<typename T, typename TO> class TypeTupleBouncer: public TypeTuple
  {
  public:
    TypeTuple* bounceType()
    {
      static Type* result = 0;
      if (!result)
        result = typeOf<TO>();
      return static_cast<TypeTuple*>(result);
    }

    virtual void adaptStorage(void** storage, void** adapted) = 0;

    typedef DefaultTypeImplMethods<T> Methods;
    virtual std::vector<Type*> memberTypes()
    {
      return bounceType()->memberTypes();
    }

    virtual void* get(void* storage, unsigned int index)
    {
      void* astorage;
      adaptStorage(&storage, &astorage);
      return bounceType()->get(astorage, index);
    }

    virtual void set(void** storage, unsigned int index, void* valStorage)
    {
      void* astorage;
      adaptStorage(storage, &astorage);
      bounceType()->set(&astorage, index, valStorage);
    }

    _QI_BOUNCE_TYPE_METHODS(Methods);
  };

  template<typename F, typename S>
  class TypeImpl<std::pair<F, S> >: public TypeTuple
  {
  public:
    typedef DefaultTypeImplMethods<std::pair<F, S> > Methods;
    typedef typename std::pair<F, S> BackendType;
    std::vector<Type*> memberTypes()
    {
      static std::vector<Type*>* result=0;
      if (!result)
      {
        result = new std::vector<Type*>();
        result->push_back(typeOf<F>());
        result->push_back(typeOf<S>());
      }
      return *result;
    }
    void* get(void* storage, unsigned int index)
    {
      BackendType* ptr = (BackendType*)ptrFromStorage(&storage);
      // Will work if F or S are references
      if (!index)
        return typeOf<F>()->initializeStorage(const_cast<void*>((void*)&ptr->first));
      else
        return typeOf<S>()->initializeStorage(const_cast<void*>((void*)&ptr->second));
    }
    void set(void** storage, unsigned int index, void* valStorage)
    {
      BackendType* ptr = (BackendType*)ptrFromStorage(storage);
      if (!index)
        detail::TypeManagerDefault<F>::copy(const_cast<void*>((void*)&ptr->first), typeOf<F>()->ptrFromStorage(&valStorage));
      else
        detail::TypeManagerDefault<S>::copy(const_cast<void*>((void*)&ptr->second), typeOf<S>()->ptrFromStorage(&valStorage));
    }
    _QI_BOUNCE_TYPE_METHODS(Methods);
  };

}
#endif  // _QITYPE_DETAILS_TYPETUPLE_HXX_
<commit_msg>QI_TYPE_STRUCT_REGISTER: fix by not assuming we are in ns qi in dependant macros.<commit_after>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QITYPE_DETAILS_TYPETUPLE_HXX_
#define _QITYPE_DETAILS_TYPETUPLE_HXX_

#include <qi/preproc.hpp>

namespace qi
{
  namespace detail {
    template<typename T> void setFromStorage(T& ref, void* storage)
    {
      ref = *(T*)typeOf<T>()->ptrFromStorage(&storage);
    }
  }
}

#define QI_TYPE_STRUCT_DECLARE(name)                                      \
namespace qi {                                                            \
  template<> struct TypeImpl<name>: public ::qi::TypeTuple                \
  {                                                                       \
  public:                                                                 \
    virtual std::vector< ::qi::Type*> memberTypes();                             \
    virtual void* get(void* storage, unsigned int index);                 \
    virtual void set(void** storage, unsigned int index, void* valStorage); \
    _QI_BOUNCE_TYPE_METHODS(::qi::DefaultTypeImplMethods<name>);            \
 }; }

#define __QI_TUPLE_TYPE(_, what, field) res.push_back(::qi::typeOf(ptr->field));
#define __QI_TUPLE_GET(_, what, field) if (i == index) return ::qi::typeOf(ptr->field)->initializeStorage(&ptr->field); i++;
#define __QI_TUPLE_SET(_, what, field) if (i == index) ::qi::detail::setFromStorage(ptr->field, valueStorage); i++;
#define __QI_TYPE_STRUCT_IMPLEMENT(name, inl, onSet, ...)                                    \
namespace qi {                                                                        \
  inl std::vector< ::qi::Type*> TypeImpl<name>::memberTypes()                                \
  {                                                                                \
    name* ptr = 0;                                                                    \
    std::vector< ::qi::Type*> res;                                                           \
    QI_VAARGS_APPLY(__QI_TUPLE_TYPE, _, __VA_ARGS__);                                 \
    return res;                                                                       \
  }                                                                                \
  inl void* TypeImpl<name>::get(void* storage, unsigned int index)                    \
  {                                                                                \
    unsigned int i = 0;                                                                        \
    name* ptr = (name*)ptrFromStorage(&storage);                                      \
    QI_VAARGS_APPLY(__QI_TUPLE_GET, _, __VA_ARGS__);                                  \
    return 0;                                                                         \
  }                                                                                \
  inl void TypeImpl<name>::set(void** storage, unsigned int index, void* valueStorage)\
  {                                                                                \
    unsigned int i=0;                                                                 \
    name* ptr = (name*)ptrFromStorage(storage);                                       \
    QI_VAARGS_APPLY(__QI_TUPLE_SET, _, __VA_ARGS__);                                  \
    onSet                                                                      \
  }\
}

/// Allow the QI_TYPE_STRUCT macro and variants to access private members
#define QI_TYPE_STRUCT_PRIVATE_ACCESS(name) \
friend class qi::TypeImpl<name>;

/** Declare a simple struct to the type system.
 * First argument is the structure name. Remaining arguments are the structure
 * fields.
 * This macro must be called outside any namespace.
 * This macro should be called in the header file defining the structure 'name',
 * or in a header included by all source files using the structure.
 * See QI_TYPE_STRUCT_REGISTER for a similar macro that can be called from a
 * single source file.
 */
#define QI_TYPE_STRUCT(name, ...) \
  QI_TYPE_STRUCT_DECLARE(name) \
  __QI_TYPE_STRUCT_IMPLEMENT(name, inline, /**/, __VA_ARGS__)

/** Similar to QI_TYPE_STRUCT, but evaluates 'onSet' after writting to an instance.
 * The instance is accessible through the variable 'ptr'.
 */
#define QI_TYPE_STRUCT_EX(name, onSet, ...) \
  QI_TYPE_STRUCT_DECLARE(name) \
  __QI_TYPE_STRUCT_IMPLEMENT(name, inline, onSet, __VA_ARGS__)

#define QI_TYPE_STRUCT_IMPLEMENT(name, ...) \
  __QI_TYPE_STRUCT_IMPLEMENT(name, /**/, /**/, __VA_ARGS__)

/** Similar to QI_TYPE_STRUCT, but using the runtime factory instead of the
 * compile-time template.
 *
 */
#define QI_TYPE_STRUCT_REGISTER(name, ...) \
namespace _qi_ {                           \
    QI_TYPE_STRUCT(name, __VA_ARGS__);     \
}                                          \
QI_TYPE_REGISTER_CUSTOM(name, _qi_::qi::TypeImpl<name>)

/** Declares that name is equivalent to type bounceTo, and that instances
 * can be converted using the conversion function with signature 'bounceTo* (name*)'.
 * This macro should be called in a header included by all code using the 'name'
 * class.
 * See QI_TYPE_STRUCT_BOUNCE_REGISTER for a similar macro that can be called from a
 * single source file.
 */
#define QI_TYPE_STRUCT_BOUNCE(name, bounceTo, conversion)                 \
namespace qi {                                                            \
template<> class TypeImpl<name>: public ::qi::TypeTupleBouncer<name, bounceTo>  \
{                                                                         \
public:                                                                   \
  void adaptStorage(void** storage, void** adapted)                       \
  {                                                                       \
    name* ptr = (name*)ptrFromStorage(storage);                           \
    bounceTo * tptr = conversion(ptr);                                    \
    *adapted = bounceType()->initializeStorage(tptr);                     \
  }                                                                       \
};}

/** Similar to QI_TYPE_STRUCT_BOUNCE, but using the runtime factory instead of the
 * compile-time template.
 */
#define QI_TYPE_STRUCT_BOUNCE_REGISTER(name, bounceTo, conversion)        \
namespace _qi_ {                                                          \
    QI_TYPE_STRUCT_BOUNCE(name, bounceTo, conversion);                    \
}                                                                         \
QI_TYPE_REGISTER_CUSTOM(name, _qi_::qi::TypeImpl<name>)

namespace qi {
  template<typename T, typename TO> class TypeTupleBouncer: public TypeTuple
  {
  public:
    TypeTuple* bounceType()
    {
      static Type* result = 0;
      if (!result)
        result = typeOf<TO>();
      return static_cast<TypeTuple*>(result);
    }

    virtual void adaptStorage(void** storage, void** adapted) = 0;

    typedef DefaultTypeImplMethods<T> Methods;
    virtual std::vector<Type*> memberTypes()
    {
      return bounceType()->memberTypes();
    }

    virtual void* get(void* storage, unsigned int index)
    {
      void* astorage;
      adaptStorage(&storage, &astorage);
      return bounceType()->get(astorage, index);
    }

    virtual void set(void** storage, unsigned int index, void* valStorage)
    {
      void* astorage;
      adaptStorage(storage, &astorage);
      bounceType()->set(&astorage, index, valStorage);
    }

    _QI_BOUNCE_TYPE_METHODS(Methods);
  };

  template<typename F, typename S>
  class TypeImpl<std::pair<F, S> >: public TypeTuple
  {
  public:
    typedef DefaultTypeImplMethods<std::pair<F, S> > Methods;
    typedef typename std::pair<F, S> BackendType;
    std::vector<Type*> memberTypes()
    {
      static std::vector<Type*>* result=0;
      if (!result)
      {
        result = new std::vector<Type*>();
        result->push_back(typeOf<F>());
        result->push_back(typeOf<S>());
      }
      return *result;
    }
    void* get(void* storage, unsigned int index)
    {
      BackendType* ptr = (BackendType*)ptrFromStorage(&storage);
      // Will work if F or S are references
      if (!index)
        return typeOf<F>()->initializeStorage(const_cast<void*>((void*)&ptr->first));
      else
        return typeOf<S>()->initializeStorage(const_cast<void*>((void*)&ptr->second));
    }
    void set(void** storage, unsigned int index, void* valStorage)
    {
      BackendType* ptr = (BackendType*)ptrFromStorage(storage);
      if (!index)
        detail::TypeManagerDefault<F>::copy(const_cast<void*>((void*)&ptr->first), typeOf<F>()->ptrFromStorage(&valStorage));
      else
        detail::TypeManagerDefault<S>::copy(const_cast<void*>((void*)&ptr->second), typeOf<S>()->ptrFromStorage(&valStorage));
    }
    _QI_BOUNCE_TYPE_METHODS(Methods);
  };

}
#endif  // _QITYPE_DETAILS_TYPETUPLE_HXX_
<|endoftext|>


<commit_before>#include "forumreader.h"

#include "website_backend/gumboparserimpl.h"
#include "common/filedownloader.h"

namespace {
template <typename T>
void dumpFutureObj(QFuture<T> future, QString name)
{
#ifdef RBR_PRINT_DEBUG_OUTPUT
    qDebug() << "----------------------------------------------------";
    qDebug() << "Future name:"        << name;
    qDebug() << "Future is started:"  << future.isStarted();
    qDebug() << "Future is running:"  << future.isRunning();
    qDebug() << "Future is finished:" << future.isFinished();
    qDebug() << "Future is paused:"   << future.isPaused();
    qDebug() << "Future is canceled:" << future.isCanceled();
    qDebug() << "Future has result:"  << future.isResultReadyAt(0);
    qDebug() << "----------------------------------------------------";
#else
    Q_UNUSED(future);
    Q_UNUSED(name);
#endif
}
}

ForumReader::ForumReader() :
    m_forumPageCountWatcher(),
    m_forumPageParserWatcher(),
    m_pagePosts(),
    m_pageCount(0),
    m_pageNo(0),
    m_lastError(ResultCode::Ok)
{
    connect(&m_downloader, &FileDownloader::downloadProgress, this, &ForumReader::onForumPageDownloadProgress);
    connect(&m_downloader, &FileDownloader::downloadFinished, this, &ForumReader::onForumPageDownloaded);
    connect(&m_downloader, &FileDownloader::downloadFailed,   this, &ForumReader::onForumPageDownloadFailed);

    connect(&m_forumPageCountWatcher, &IntFutureWatcher::finished, this, &ForumReader::onForumPageCountParsed);
    connect(&m_forumPageCountWatcher, &IntFutureWatcher::canceled, this, &ForumReader::onForumPageCountParsingCanceled);

    connect(&m_forumPageParserWatcher, &ParserFutureWatcher::finished, this, &ForumReader::onForumPageParsed);
    connect(&m_forumPageParserWatcher, &ParserFutureWatcher::canceled, this, &ForumReader::onForumPageParsingCanceled);
}

ForumReader::~ForumReader()
{
}

QString ForumReader::applicationDirPath() const
{
    QString result = qApp->applicationDirPath();
    if (!result.endsWith("/")) result += "/";
    return result;
}

QUrl ForumReader::convertToUrl(QString urlStr) const
{
    return QUrl(urlStr);
}

#ifdef FORUM_READER_SYNC_API
int ForumReader::parsePageCount(QString urlStr)
{
    // Cleanup
    m_pagePosts.clear();
    m_pageCount = 0;

    // 1) Download the first forum web page
    QByteArray htmlRawData;
    if (!FileDownloader::downloadUrl(urlStr, htmlRawData)) { Q_ASSERT(0); return 0; }

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    int resultFpp = fpp.getPageCount(htmlRawData, m_pageCount);
    Q_ASSERT(resultFpp == 0); if (resultFpp != 0) { m_pageCount = 0; return 0; }
    return m_pageCount;
}

bool ForumReader::parseForumPage(QString urlStr, int pageNo)
{
    // Cleanup
    m_pagePosts.clear();
    m_pageCount = 0;
    m_pageNo = pageNo;

    // 1) Download the first forum web page
    QByteArray htmlRawData;
    if (!FileDownloader::downloadUrl(urlStr, htmlRawData)) { Q_ASSERT(0); return false; }

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    int resultFpp = fpp.getPageCount(htmlRawData, m_pageCount);
    Q_ASSERT(resultFpp == 0); if (resultFpp != 0) { m_pageCount = 0; return false; }

    // 3) Parse the page HTML to get the page user posts
    resultFpp = fpp.getPagePosts(htmlRawData, m_pagePosts);
    Q_ASSERT(resultFpp == 0); if (resultFpp != 0) { m_pagePosts.clear(); return false; }
    return true;
}
#endif

namespace {
int parsePageCountAsync(QString urlStr)
{
    int result = 0;

    // 1) Download the first forum web page
    QByteArray htmlRawData;
    if (!FileDownloader::downloadUrl(urlStr, htmlRawData))
    {
        Q_ASSERT(0);
        return result;
    }

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    ResultCode resultFpp = fpp.getPageCount(htmlRawData, result);
    if (resultFpp != ResultCode::Ok)
    {
        Q_ASSERT(0);
        result = 0;
        return result;
    }

    return result;
}
}

void ForumReader::startPageCountAsync(QString urlStr)
{
    dumpFutureObj(m_forumPageCountWatcher.future(), "m_forumPageCountWatcher");

    // Wait for previous operation finish (if any)
    // NOTE: QtConcurrent::run() return future that cannot be canceled
    //m_forumPageCountWatcher.cancel();
    m_forumPageCountWatcher.waitForFinished();

    auto forumPageCountFuture = QtConcurrent::run(std::bind(parsePageCountAsync, urlStr));
    m_forumPageCountWatcher.setFuture(forumPageCountFuture);
}

namespace {
// NOTE: QtConcurrent require to return collection; return result code will be much more straightforward to reader
BankiRuForum::UserPosts parsePageAsync(QByteArray rawHtmlData, int& pageCount, ResultCode& errorCode)
{
    BankiRuForum::UserPosts result;
    errorCode = ResultCode::Ok;

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    errorCode = fpp.getPageCount(rawHtmlData, pageCount);
    if (errorCode != ResultCode::Ok)
    {
        Q_ASSERT(0);
        return result;
    }

    // 3) Parse the page HTML to get the page user posts
    errorCode = fpp.getPagePosts(rawHtmlData, result);
    if (errorCode != ResultCode::Ok)
    {
        Q_ASSERT(0);
        return result;
    }

    return result;
}
}

void ForumReader::startPageParseAsync(QString urlStr, int pageNo)
{
    dumpFutureObj(m_forumPageParserWatcher.future(), "m_forumPageParserWatcher");

    // Cleanup
    m_pagePosts.clear();
    // FIXME: implement updatePageNumber() method and call it here
//    m_pageCount = 0;
    m_pageNo = pageNo;

    m_downloader.startDownloadAsync(urlStr);

    // FIXME: a better way? server don't return Content-Length header;
    //        a HTML page size is unknown, and the only way to get it - download the entire page;
    //        however we know forum HTML page average size - it is around 400 Kb
    emit pageContentParseProgressRange(0, 400000);
}

void ForumReader::onForumPageCountParsed()
{
    dumpFutureObj(m_forumPageCountWatcher.future(), "m_forumPageCountWatcher");

    int pageCount = m_forumPageCountWatcher.result();
    Q_ASSERT(pageCount > 0);
    m_pageCount = pageCount;
    emit pageCountParsed(pageCount);
}

void ForumReader::onForumPageCountParsingCanceled()
{
    Q_ASSERT_X(0, Q_FUNC_INFO, "QtConcurrent::run result cannot be canceled");
}

void ForumReader::onForumPageDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
//    qDebug() << Q_FUNC_INFO << bytesReceived << "/" << bytesTotal;

    // NOTE: currently banki.ru server don't return Content-Length header
    Q_ASSERT(bytesTotal <= 0);

    // NOTE: HTML page size should not exceed 2^32 bytes, i hope :)
    emit pageContentParseProgress((int)bytesReceived);
}

void ForumReader::onForumPageDownloaded()
{
    m_pageData = m_downloader.downloadedData();
    Q_ASSERT(!m_pageData.isEmpty());

    // Wait for previous operation finish (if any)
    // NOTE: QtConcurrent::run() return future that cannot be canceled
    //m_forumPageParserWatcher.cancel();
    m_forumPageParserWatcher.waitForFinished();

    auto forumPageParseFuture = QtConcurrent::run(
                std::bind(parsePageAsync, m_pageData, std::ref(m_pageCount), std::ref(m_lastError)));
    m_forumPageParserWatcher.setFuture(forumPageParseFuture);
}

void ForumReader::onForumPageDownloadFailed(ResultCode code)
{
    // FIXME: implement
}

void ForumReader::onForumPageParsed()
{
    dumpFutureObj(m_forumPageParserWatcher.future(), "m_forumPageParserWatcher");

    m_pagePosts = m_forumPageParserWatcher.result();
    emit pageContentParsed(m_pageNo);
}

void ForumReader::onForumPageParsingCanceled()
{
    Q_ASSERT_X(0, Q_FUNC_INFO, "QFuture returned by QtConcurrent::run() cannot be canceled");
}

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

int ForumReader::pageCount() const
{
    return m_pageCount;
}

int ForumReader::postCount() const
{
    return m_pagePosts.size();
}

QString ForumReader::postAuthorQml(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].first.getQmlString(qrand());
}

int ForumReader::postAvatarMaxWidth() const
{
    int maxWidth = 100;
    for(int i = 0; i < m_pagePosts.size(); ++i)
    {
        if (m_pagePosts[i].first.m_userAvatar.isNull()) continue;
        int width = m_pagePosts[i].first.m_userAvatar->m_width;
        if(width > maxWidth) maxWidth = width;
    }
    return maxWidth;
}

QDateTime ForumReader::postDateTime(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_date;
}

QString ForumReader::postText(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());
    if (m_pagePosts[index].second.m_data.empty()) return QString();

    return m_pagePosts[index].second.getQmlString(qrand());
}

QString ForumReader::postLastEdit(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_lastEdit;
}

int ForumReader::postLikeCount(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_likeCounter;
}

QString ForumReader::postAuthorSignature(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_userSignature;
}
<commit_msg>QML frontend: the last warning was fixed<commit_after>#include "forumreader.h"

#include "website_backend/gumboparserimpl.h"
#include "common/filedownloader.h"

namespace {
template <typename T>
void dumpFutureObj(QFuture<T> future, QString name)
{
#ifdef RBR_PRINT_DEBUG_OUTPUT
    qDebug() << "----------------------------------------------------";
    qDebug() << "Future name:"        << name;
    qDebug() << "Future is started:"  << future.isStarted();
    qDebug() << "Future is running:"  << future.isRunning();
    qDebug() << "Future is finished:" << future.isFinished();
    qDebug() << "Future is paused:"   << future.isPaused();
    qDebug() << "Future is canceled:" << future.isCanceled();
    qDebug() << "Future has result:"  << future.isResultReadyAt(0);
    qDebug() << "----------------------------------------------------";
#else
    Q_UNUSED(future);
    Q_UNUSED(name);
#endif
}
}

ForumReader::ForumReader() :
    m_forumPageCountWatcher(),
    m_forumPageParserWatcher(),
    m_pagePosts(),
    m_pageCount(0),
    m_pageNo(0),
    m_lastError(ResultCode::Ok)
{
    connect(&m_downloader, &FileDownloader::downloadProgress, this, &ForumReader::onForumPageDownloadProgress);
    connect(&m_downloader, &FileDownloader::downloadFinished, this, &ForumReader::onForumPageDownloaded);
    connect(&m_downloader, &FileDownloader::downloadFailed,   this, &ForumReader::onForumPageDownloadFailed);

    connect(&m_forumPageCountWatcher, &IntFutureWatcher::finished, this, &ForumReader::onForumPageCountParsed);
    connect(&m_forumPageCountWatcher, &IntFutureWatcher::canceled, this, &ForumReader::onForumPageCountParsingCanceled);

    connect(&m_forumPageParserWatcher, &ParserFutureWatcher::finished, this, &ForumReader::onForumPageParsed);
    connect(&m_forumPageParserWatcher, &ParserFutureWatcher::canceled, this, &ForumReader::onForumPageParsingCanceled);
}

ForumReader::~ForumReader()
{
}

QString ForumReader::applicationDirPath() const
{
    QString result = qApp->applicationDirPath();
    if (!result.endsWith("/")) result += "/";
    return result;
}

QUrl ForumReader::convertToUrl(QString urlStr) const
{
    return QUrl(urlStr);
}

#ifdef FORUM_READER_SYNC_API
int ForumReader::parsePageCount(QString urlStr)
{
    // Cleanup
    m_pagePosts.clear();
    m_pageCount = 0;

    // 1) Download the first forum web page
    QByteArray htmlRawData;
    if (!FileDownloader::downloadUrl(urlStr, htmlRawData)) { Q_ASSERT(0); return 0; }

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    int resultFpp = fpp.getPageCount(htmlRawData, m_pageCount);
    Q_ASSERT(resultFpp == 0); if (resultFpp != 0) { m_pageCount = 0; return 0; }
    return m_pageCount;
}

bool ForumReader::parseForumPage(QString urlStr, int pageNo)
{
    // Cleanup
    m_pagePosts.clear();
    m_pageCount = 0;
    m_pageNo = pageNo;

    // 1) Download the first forum web page
    QByteArray htmlRawData;
    if (!FileDownloader::downloadUrl(urlStr, htmlRawData)) { Q_ASSERT(0); return false; }

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    int resultFpp = fpp.getPageCount(htmlRawData, m_pageCount);
    Q_ASSERT(resultFpp == 0); if (resultFpp != 0) { m_pageCount = 0; return false; }

    // 3) Parse the page HTML to get the page user posts
    resultFpp = fpp.getPagePosts(htmlRawData, m_pagePosts);
    Q_ASSERT(resultFpp == 0); if (resultFpp != 0) { m_pagePosts.clear(); return false; }
    return true;
}
#endif

namespace {
int parsePageCountAsync(QString urlStr)
{
    int result = 0;

    // 1) Download the first forum web page
    QByteArray htmlRawData;
    if (!FileDownloader::downloadUrl(urlStr, htmlRawData))
    {
        Q_ASSERT(0);
        return result;
    }

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    ResultCode resultFpp = fpp.getPageCount(htmlRawData, result);
    if (resultFpp != ResultCode::Ok)
    {
        Q_ASSERT(0);
        result = 0;
        return result;
    }

    return result;
}
}

void ForumReader::startPageCountAsync(QString urlStr)
{
    dumpFutureObj(m_forumPageCountWatcher.future(), "m_forumPageCountWatcher");

    // Wait for previous operation finish (if any)
    // NOTE: QtConcurrent::run() return future that cannot be canceled
    //m_forumPageCountWatcher.cancel();
    m_forumPageCountWatcher.waitForFinished();

    auto forumPageCountFuture = QtConcurrent::run(std::bind(parsePageCountAsync, urlStr));
    m_forumPageCountWatcher.setFuture(forumPageCountFuture);
}

namespace {
// NOTE: QtConcurrent require to return collection; return result code will be much more straightforward to reader
BankiRuForum::UserPosts parsePageAsync(QByteArray rawHtmlData, int& pageCount, ResultCode& errorCode)
{
    BankiRuForum::UserPosts result;
    errorCode = ResultCode::Ok;

    // 2) Parse the page HTML to get the page number
    BankiRuForum::ForumPageParser fpp;
    errorCode = fpp.getPageCount(rawHtmlData, pageCount);
    if (errorCode != ResultCode::Ok)
    {
        Q_ASSERT(0);
        return result;
    }

    // 3) Parse the page HTML to get the page user posts
    errorCode = fpp.getPagePosts(rawHtmlData, result);
    if (errorCode != ResultCode::Ok)
    {
        Q_ASSERT(0);
        return result;
    }

    return result;
}
}

void ForumReader::startPageParseAsync(QString urlStr, int pageNo)
{
    dumpFutureObj(m_forumPageParserWatcher.future(), "m_forumPageParserWatcher");

    // Cleanup
    m_pagePosts.clear();
    // FIXME: implement updatePageNumber() method and call it here
//    m_pageCount = 0;
    m_pageNo = pageNo;

    m_downloader.startDownloadAsync(urlStr);

    // FIXME: a better way? server don't return Content-Length header;
    //        a HTML page size is unknown, and the only way to get it - download the entire page;
    //        however we know forum HTML page average size - it is around 400 Kb
    emit pageContentParseProgressRange(0, 400000);
}

void ForumReader::onForumPageCountParsed()
{
    dumpFutureObj(m_forumPageCountWatcher.future(), "m_forumPageCountWatcher");

    int pageCount = m_forumPageCountWatcher.result();
    Q_ASSERT(pageCount > 0);
    m_pageCount = pageCount;
    emit pageCountParsed(pageCount);
}

void ForumReader::onForumPageCountParsingCanceled()
{
    Q_ASSERT_X(0, Q_FUNC_INFO, "QtConcurrent::run result cannot be canceled");
}

void ForumReader::onForumPageDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
//    qDebug() << Q_FUNC_INFO << bytesReceived << "/" << bytesTotal;

    // NOTE: currently banki.ru server don't return Content-Length header
    Q_ASSERT(bytesTotal <= 0);

    // NOTE: HTML page size should not exceed 2^32 bytes, i hope :)
    emit pageContentParseProgress((int)bytesReceived);
}

void ForumReader::onForumPageDownloaded()
{
    m_pageData = m_downloader.downloadedData();
    Q_ASSERT(!m_pageData.isEmpty());

    // Wait for previous operation finish (if any)
    // NOTE: QtConcurrent::run() return future that cannot be canceled
    //m_forumPageParserWatcher.cancel();
    m_forumPageParserWatcher.waitForFinished();

    auto forumPageParseFuture = QtConcurrent::run(
                std::bind(parsePageAsync, m_pageData, std::ref(m_pageCount), std::ref(m_lastError)));
    m_forumPageParserWatcher.setFuture(forumPageParseFuture);
}

void ForumReader::onForumPageDownloadFailed(ResultCode code)
{
    // FIXME: implement
    Q_UNUSED(code);
}

void ForumReader::onForumPageParsed()
{
    dumpFutureObj(m_forumPageParserWatcher.future(), "m_forumPageParserWatcher");

    m_pagePosts = m_forumPageParserWatcher.result();
    emit pageContentParsed(m_pageNo);
}

void ForumReader::onForumPageParsingCanceled()
{
    Q_ASSERT_X(0, Q_FUNC_INFO, "QFuture returned by QtConcurrent::run() cannot be canceled");
}

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

int ForumReader::pageCount() const
{
    return m_pageCount;
}

int ForumReader::postCount() const
{
    return m_pagePosts.size();
}

QString ForumReader::postAuthorQml(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].first.getQmlString(qrand());
}

int ForumReader::postAvatarMaxWidth() const
{
    int maxWidth = 100;
    for(int i = 0; i < m_pagePosts.size(); ++i)
    {
        if (m_pagePosts[i].first.m_userAvatar.isNull()) continue;
        int width = m_pagePosts[i].first.m_userAvatar->m_width;
        if(width > maxWidth) maxWidth = width;
    }
    return maxWidth;
}

QDateTime ForumReader::postDateTime(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_date;
}

QString ForumReader::postText(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());
    if (m_pagePosts[index].second.m_data.empty()) return QString();

    return m_pagePosts[index].second.getQmlString(qrand());
}

QString ForumReader::postLastEdit(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_lastEdit;
}

int ForumReader::postLikeCount(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_likeCounter;
}

QString ForumReader::postAuthorSignature(int index) const
{
    Q_ASSERT(index >= 0 && index < m_pagePosts.size());

    return m_pagePosts[index].second.m_userSignature;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
 *                                                                           *
 *   This program is free software; you can redistribute it and/or modify    *
 *   it under the terms of the GNU Lesser General Public License as          *
 *   published by the Free Software Foundation; either version 2.1 of the    *
 *   License, or (at your option) version 3, or any later version accepted   *
 *   by the membership of KDE e.V. (or its successor approved by the         *
 *   membership of KDE e.V.), which shall act as a proxy defined in          *
 *   Section 6 of version 3 of the license.                                  *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
 *   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,                                *
 *   see <http://www.gnu.org/licenses/>.                                     *
 *****************************************************************************/

#include "qtcurve_plugin.h"
#include "qtcurve.h"

#include "config-qt5.h"

#include <qtcurve-utils/qtprops.h>
#include <qtcurve-utils/x11shadow.h>
#include <qtcurve-utils/x11blur.h>

#include <QApplication>

#ifdef Qt5X11Extras_FOUND
#  include <qtcurve-utils/x11base.h>
#  include <QX11Info>
#endif

#ifdef QTC_QT5_ENABLE_QTQUICK2
#  include <QQuickWindow>
#  include <QQuickItem>
#endif
#include <QDebug>

#include <qtcurve-utils/log.h>

namespace QtCurve {

__attribute__((hot)) static void
polishQuickControl(QObject *obj)
{
#ifdef QTC_QT5_ENABLE_QTQUICK2
    if (QQuickWindow *window = qobject_cast<QQuickWindow*>(obj)) {
        // QtQuickControl support
        // This is still VERY experimental.
        // Need a lot more testing and refactoring.
        if (Style *style = getStyle(qApp)) {
            if (window->inherits("QQuickPopupWindow")) {
                if (window->inherits("QQuickMenuPopupWindow")) {
                    window->setColor(QColor(0, 0, 0, 0));
                }
                qtcX11ShadowInstall(window->winId());
            } else {
                QColor color = window->color();
                int opacity = style->options().bgndOpacity;
                if (color.alpha() == 255 && opacity != 100) {
                    qreal opacityF = opacity / 100.0;
                    window->setColor(QColor::fromRgbF(color.redF() * opacityF,
                                                      color.greenF() * opacityF,
                                                      color.blueF() * opacityF,
                                                      opacityF));
                    qtcX11BlurTrigger(window->winId(), true, 0, nullptr);
                }
            }
        }
    } else if (QQuickItem *item = qobject_cast<QQuickItem*>(obj)) {
        if (QQuickWindow *window = item->window()) {
            if (getStyle(qApp)) {
                window->setColor(QColor(0, 0, 0, 0));
                qtcX11BlurTrigger(window->winId(), true, 0, nullptr);
            }
        }
    }
#else
    QTC_UNUSED(obj);
#endif
}

__attribute__((hot)) static bool
qtcEventCallback(void **cbdata)
{
    QObject *receiver = (QObject*)cbdata[0];
    QTC_RET_IF_FAIL(receiver, false);
    QEvent *event = (QEvent*)cbdata[1];
    if (qtcUnlikely(event->type() == QEvent::DynamicPropertyChange)) {
        QDynamicPropertyChangeEvent *prop_event =
            static_cast<QDynamicPropertyChangeEvent*>(event);
        // eat the property change events from ourselves
        if (prop_event->propertyName() == QTC_PROP_NAME) {
            return true;
        }
    }
    QWidget *widget = qtcToWidget(receiver);
    if (qtcUnlikely(widget && !qtcGetWid(widget))) {
        if (Style *style = getStyle(widget)) {
            style->prePolish(widget);
        }
    } else if (widget && event->type() == QEvent::UpdateRequest) {
        QtcQWidgetProps props(widget);
        props->opacity = 100;
    } else {
        polishQuickControl(receiver);
    }
    return false;
}

static StylePlugin *firstPlInstance = nullptr;
static QList<Style*> *styleInstances = nullptr;

QStyle*
StylePlugin::create(const QString &key)
{
    if (!firstPlInstance) {
        firstPlInstance = this;
        styleInstances = &m_styleInstances;
    }

    init();
    Style *qtc;
    if (key.toLower() == "qtcurve") {
        qtc = new Style;
        qtc->m_plugin = this;
        // keep track of all style instances we allocate, for instance
        // for KLineEdit widgets which apparently insist on overriding
        // certain things (cf. KLineEditStyle). We want to be able to
        // delete those instances as properly and as early as
        // possible during the global destruction phase.
        m_styleInstances << qtc;
    } else {
        qtc = nullptr;
    }
    return qtc;
}

void StylePlugin::unregisterCallback()
{
    if (m_eventNotifyCallbackInstalled) {
        qtcInfo("Unregistering the event notify callback (for plugin %p)\n", this);
        QInternal::unregisterCallback(QInternal::EventNotifyCallback,
                                    qtcEventCallback);
        m_eventNotifyCallbackInstalled = false;
    }
}

StylePlugin::~StylePlugin()
{
    qtcInfo("Deleting QtCurve plugin (%p)\n", this);
    if (!m_styleInstances.isEmpty()) {
        qtcWarn("there remain(s) %d Style instance(s)\n", m_styleInstances.count());
        foreach (Style *that, m_styleInstances) {
            // don't let ~Style() touch m_styleInstances from here.
            that->m_plugin = nullptr;
            // each instance should already have disconnected from the D-Bus
            // and disconnected from receiving select signals.
            delete that;
        }
        m_styleInstances.clear();
    }
    if (firstPlInstance == this) {
        firstPlInstance = nullptr;
        styleInstances = nullptr;
    }
}

void
StylePlugin::init()
{
    std::call_once(m_ref_flag, [this] {
            QInternal::registerCallback(QInternal::EventNotifyCallback,
                                        qtcEventCallback);
            m_eventNotifyCallbackInstalled = true;
            if (QCoreApplication::instance()) {
                connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &StylePlugin::unregisterCallback);
            }
#ifdef QTC_QT5_ENABLE_QTQUICK2
            QQuickWindow::setDefaultAlphaBuffer(true);
#endif
#ifdef Qt5X11Extras_FOUND
            if (qApp->platformName() == "xcb") {
                qtcX11InitXcb(QX11Info::connection(), QX11Info::appScreen());
            }
#endif
        });
}

__attribute__((constructor)) int atLibOpen()
{
    qtcDebug("Opening QtCurve\n");
    return 0;
}

__attribute__((destructor)) int atLibClose()
{
    qtcInfo("Closing QtCurve\n");
    if (firstPlInstance) {
        qtcInfo("Plugin instance %p still open with %d open Style instance(s)\n",
            firstPlInstance, styleInstances->count());
    }
    return 0;
}

}
<commit_msg>Don't delete existing style instances from the plugin dtor if parented.<commit_after>/*****************************************************************************
 *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
 *                                                                           *
 *   This program is free software; you can redistribute it and/or modify    *
 *   it under the terms of the GNU Lesser General Public License as          *
 *   published by the Free Software Foundation; either version 2.1 of the    *
 *   License, or (at your option) version 3, or any later version accepted   *
 *   by the membership of KDE e.V. (or its successor approved by the         *
 *   membership of KDE e.V.), which shall act as a proxy defined in          *
 *   Section 6 of version 3 of the license.                                  *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
 *   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,                                *
 *   see <http://www.gnu.org/licenses/>.                                     *
 *****************************************************************************/

#include "qtcurve_plugin.h"
#include "qtcurve.h"

#include "config-qt5.h"

#include <qtcurve-utils/qtprops.h>
#include <qtcurve-utils/x11shadow.h>
#include <qtcurve-utils/x11blur.h>

#include <QApplication>

#ifdef Qt5X11Extras_FOUND
#  include <qtcurve-utils/x11base.h>
#  include <QX11Info>
#endif

#ifdef QTC_QT5_ENABLE_QTQUICK2
#  include <QQuickWindow>
#  include <QQuickItem>
#endif
#include <QDebug>

#include <qtcurve-utils/log.h>

namespace QtCurve {

__attribute__((hot)) static void
polishQuickControl(QObject *obj)
{
#ifdef QTC_QT5_ENABLE_QTQUICK2
    if (QQuickWindow *window = qobject_cast<QQuickWindow*>(obj)) {
        // QtQuickControl support
        // This is still VERY experimental.
        // Need a lot more testing and refactoring.
        if (Style *style = getStyle(qApp)) {
            if (window->inherits("QQuickPopupWindow")) {
                if (window->inherits("QQuickMenuPopupWindow")) {
                    window->setColor(QColor(0, 0, 0, 0));
                }
                qtcX11ShadowInstall(window->winId());
            } else {
                QColor color = window->color();
                int opacity = style->options().bgndOpacity;
                if (color.alpha() == 255 && opacity != 100) {
                    qreal opacityF = opacity / 100.0;
                    window->setColor(QColor::fromRgbF(color.redF() * opacityF,
                                                      color.greenF() * opacityF,
                                                      color.blueF() * opacityF,
                                                      opacityF));
                    qtcX11BlurTrigger(window->winId(), true, 0, nullptr);
                }
            }
        }
    } else if (QQuickItem *item = qobject_cast<QQuickItem*>(obj)) {
        if (QQuickWindow *window = item->window()) {
            if (getStyle(qApp)) {
                window->setColor(QColor(0, 0, 0, 0));
                qtcX11BlurTrigger(window->winId(), true, 0, nullptr);
            }
        }
    }
#else
    QTC_UNUSED(obj);
#endif
}

__attribute__((hot)) static bool
qtcEventCallback(void **cbdata)
{
    QObject *receiver = (QObject*)cbdata[0];
    QTC_RET_IF_FAIL(receiver, false);
    QEvent *event = (QEvent*)cbdata[1];
    if (qtcUnlikely(event->type() == QEvent::DynamicPropertyChange)) {
        QDynamicPropertyChangeEvent *prop_event =
            static_cast<QDynamicPropertyChangeEvent*>(event);
        // eat the property change events from ourselves
        if (prop_event->propertyName() == QTC_PROP_NAME) {
            return true;
        }
    }
    QWidget *widget = qtcToWidget(receiver);
    if (qtcUnlikely(widget && !qtcGetWid(widget))) {
        if (Style *style = getStyle(widget)) {
            style->prePolish(widget);
        }
    } else if (widget && event->type() == QEvent::UpdateRequest) {
        QtcQWidgetProps props(widget);
        props->opacity = 100;
    } else {
        polishQuickControl(receiver);
    }
    return false;
}

static StylePlugin *firstPlInstance = nullptr;
static QList<Style*> *styleInstances = nullptr;

QStyle*
StylePlugin::create(const QString &key)
{
    if (!firstPlInstance) {
        firstPlInstance = this;
        styleInstances = &m_styleInstances;
    }

    init();
    Style *qtc;
    if (key.toLower() == "qtcurve") {
        qtc = new Style;
        qtc->m_plugin = this;
        // keep track of all style instances we allocate, for instance
        // for KLineEdit widgets which apparently insist on overriding
        // certain things (cf. KLineEditStyle). We want to be able to
        // delete those instances as properly and as early as
        // possible during the global destruction phase.
        m_styleInstances << qtc;
    } else {
        qtc = nullptr;
    }
    return qtc;
}

void StylePlugin::unregisterCallback()
{
    if (m_eventNotifyCallbackInstalled) {
        qtcInfo("Unregistering the event notify callback (for plugin %p)\n", this);
        QInternal::unregisterCallback(QInternal::EventNotifyCallback,
                                    qtcEventCallback);
        m_eventNotifyCallbackInstalled = false;
    }
}

StylePlugin::~StylePlugin()
{
    qtcInfo("Deleting QtCurve plugin (%p)\n", this);
    if (!m_styleInstances.isEmpty()) {
        qtcWarn("there remain(s) %d Style instance(s)\n", m_styleInstances.count());
        foreach (Style *that, m_styleInstances) {
            // don't let ~Style() touch m_styleInstances from here.
            that->m_plugin = nullptr;
            // each instance should already have disconnected from the D-Bus
            // and disconnected from receiving select signals.
            if (!that->parent()) {
                delete that;
            } else {
                qtcDebug("Ignoring Style instance %p with parent %p\n", that, that->parent());
            }
        }
        m_styleInstances.clear();
    }
    if (firstPlInstance == this) {
        firstPlInstance = nullptr;
        styleInstances = nullptr;
    }
}

void
StylePlugin::init()
{
    std::call_once(m_ref_flag, [this] {
            QInternal::registerCallback(QInternal::EventNotifyCallback,
                                        qtcEventCallback);
            m_eventNotifyCallbackInstalled = true;
            if (QCoreApplication::instance()) {
                connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &StylePlugin::unregisterCallback);
            }
#ifdef QTC_QT5_ENABLE_QTQUICK2
            QQuickWindow::setDefaultAlphaBuffer(true);
#endif
#ifdef Qt5X11Extras_FOUND
            if (qApp->platformName() == "xcb") {
                qtcX11InitXcb(QX11Info::connection(), QX11Info::appScreen());
            }
#endif
        });
}

__attribute__((constructor)) int atLibOpen()
{
    qtcDebug("Opening QtCurve\n");
    return 0;
}

__attribute__((destructor)) int atLibClose()
{
    qtcInfo("Closing QtCurve\n");
    if (firstPlInstance) {
        qtcInfo("Plugin instance %p still open with %d open Style instance(s)\n",
            firstPlInstance, styleInstances->count());
    }
    return 0;
}

}
<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    BoolSPts.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Toolkit. No part of this file
or its contents may be copied, reproduced or altered in any way
without the express written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
#include "BoolSPts.hh"

// Description:
// Construct with sample resolution of (50,50,50) and automatic
// computation of sample bounds. Initial boolean operation is union.
vtkBooleanStructuredPoints::vtkBooleanStructuredPoints()
{
  this->SampleDimensions[0] = 50;
  this->SampleDimensions[1] = 50;
  this->SampleDimensions[2] = 50;

  this->ModelBounds[0] = 0.0;
  this->ModelBounds[1] = 0.0;
  this->ModelBounds[2] = 0.0;
  this->ModelBounds[3] = 0.0;
  this->ModelBounds[4] = 0.0;
  this->ModelBounds[5] = 0.0;

  this->OperationType = UNION_OPERATOR;
  // this->Operator = this->Union;
}

vtkBooleanStructuredPoints::~vtkBooleanStructuredPoints()
{
}

unsigned long int vtkBooleanStructuredPoints::GetMTime()
{
  unsigned long dtime = this->vtkStructuredPoints::GetMTime();
  unsigned long ftime = this->vtkFilter::_GetMTime();
  return (dtime > ftime ? dtime : ftime);
}

// Description:
// Add another structured point set to the list of objects to boolean.
void vtkBooleanStructuredPoints::AddInput(vtkStructuredPoints *sp)
{
  if ( ! this->InputList.IsItemPresent(sp) )
    {
    this->Modified();
    this->InputList.AddItem(sp);
    }
}

// Description:
// Remove an object from the list of objects to boolean.
void vtkBooleanStructuredPoints::RemoveInput(vtkStructuredPoints *sp)
{
  if ( this->InputList.IsItemPresent(sp) )
    {
    this->Modified();
    this->InputList.RemoveItem(sp);
    }
}

void vtkBooleanStructuredPoints::Update()
{
  unsigned long int mtime, dsMtime;
  vtkDataSet *ds;

  // make sure input is available
  if ( this->InputList.GetNumberOfItems() < 1 ) return;

  // prevent chasing our tail
  if (this->Updating) return;

  this->Updating = 1;
  for (mtime=0, this->InputList.InitTraversal(); ds = this->InputList.GetNextItem(); )
    {
    ds->Update();
    dsMtime = ds->GetMTime();
    if ( dsMtime > mtime ) mtime = dsMtime;
    }
  this->Updating = 0;

  if (mtime > this->GetMTime() || this->GetMTime() > this->ExecuteTime ||
  this->GetDataReleased() )
    {
    if ( this->StartMethod ) (*this->StartMethod)(this->StartMethodArg);
    this->Execute();
    this->ExecuteTime.Modified();
    this->SetDataReleased(0);
    if ( this->EndMethod ) (*this->EndMethod)(this->EndMethodArg);
    }

  for (this->InputList.InitTraversal(); ds = this->InputList.GetNextItem(); )
    if ( ds->ShouldIReleaseData() ) ds->ReleaseData();
}

// Initialize object prior to performing Boolean operations
void vtkBooleanStructuredPoints::InitializeBoolean()
{
  vtkScalars *inScalars=NULL;
  vtkScalars *newScalars;
  vtkStructuredPoints *sp;
  float *bounds;
  int numPts;
  int i, j;

  this->Initialize();
  this->SetDimensions(this->SampleDimensions);
  numPts = this->GetNumberOfPoints();

  // If ModelBounds unset, use input, else punt
  if ( this->ModelBounds[0] >= this->ModelBounds[1] ||
  this->ModelBounds[2] >= this->ModelBounds[3] ||
  this->ModelBounds[4] >= this->ModelBounds[5] )
    {
    if ( this->InputList.GetNumberOfItems() > 0 )
      {
       this->ModelBounds[0] = this->ModelBounds[2] = this->ModelBounds[4] = LARGE_FLOAT;
       this->ModelBounds[1] = this->ModelBounds[3] = this->ModelBounds[5] = -LARGE_FLOAT;
      for ( this->InputList.InitTraversal(); sp = this->InputList.GetNextItem(); )
        {
        bounds = sp->GetBounds();
        for (j=0; j < 3; j++)
          {
          if ( bounds[2*j] < this->ModelBounds[2*j] )
            this->ModelBounds[2*j] = bounds[2*j];

          if ( bounds[2*j+1] > this->ModelBounds[2*j+1] )
            this->ModelBounds[2*j+1] = bounds[2*j+1];
          }
        }
      }
    else
      {
      this->ModelBounds[0] = this->ModelBounds[2] = this->ModelBounds[4] = 0.0;
      this->ModelBounds[1] = this->ModelBounds[3] = this->ModelBounds[5] = 1000.0;
      }
    }

  // Update origin and aspect ratio
  for (i=0; i<3; i++)
    {
    this->Origin[i] = this->ModelBounds[2*i];
    this->AspectRatio[i] = (this->ModelBounds[2*i+1] - this->ModelBounds[2*i])
                           / (this->SampleDimensions[i] - 1);
    }

  // Create output scalar (same type as input)
  if ( this->InputList.GetNumberOfItems() > 0 )
    {
    this->InputList.InitTraversal(); sp = this->InputList.GetNextItem();
    inScalars = sp->GetPointData()->GetScalars();
    }

  if ( inScalars != NULL )
    {
    newScalars = inScalars->MakeObject(numPts); //copy
    }
  else
    {
    newScalars = new vtkFloatScalars(numPts);
    }
  this->PointData.SetScalars(newScalars);
  newScalars->Delete();

}

// Perform Boolean operations on input volumes
void vtkBooleanStructuredPoints::Execute()
{
  vtkStructuredPoints *sp;

  this->InitializeBoolean();

  for ( this->InputList.InitTraversal(); sp = this->InputList.GetNextItem(); )
    {
    this->Append(sp);
    }
}

// Description:
// Perform Boolean operations by appending to current output data.
void vtkBooleanStructuredPoints::Append(vtkStructuredPoints *sp)
{
  vtkScalars *currentScalars, *inScalars;
  float *in_bounds;
  float *dest_bounds;
  int i,j,k;
  float *in_aspect;
  float in_x,in_y,in_z;
  int in_i,in_j,in_k;
  int in_kval,in_jval;
  int dest_kval,dest_jval;
  int *in_dimensions;

  if ( (currentScalars = this->PointData.GetScalars()) == NULL )
    {
    this->InitializeBoolean();
    currentScalars = this->PointData.GetScalars();
    }

  inScalars = sp->GetPointData()->GetScalars();

  in_bounds = sp->GetBounds();
  dest_bounds = this->GetModelBounds();
  in_aspect = sp->GetAspectRatio();
  in_dimensions = sp->GetDimensions();

  // now perform operation on data
  switch (this->OperationType)
    {
    case UNION_OPERATOR :
      {
      // for each cell
      for (k = 0; k < this->SampleDimensions[2]; k++)
	{
	in_z = dest_bounds[4] + k*this->AspectRatio[2];
	in_k = int ((float)(in_z - in_bounds[4])/in_aspect[2]);
	if ((in_k >= 0)&&(in_k < in_dimensions[2]))
	  {
	  in_kval = in_k*in_dimensions[0]*in_dimensions[1];
	  dest_kval = k*this->SampleDimensions[0]*this->SampleDimensions[1];
	  for (j = 0; j < this->SampleDimensions[1]; j++)
	    {
	    in_y = dest_bounds[2] + j*this->AspectRatio[1];
	    in_j = (int) ((float)(in_y - in_bounds[2])/in_aspect[1]);
	    if ((in_j >= 0)&&(in_j < in_dimensions[1]))
	      {
	      in_jval = in_j*in_dimensions[0];
	      dest_jval = j*this->SampleDimensions[0];
	      for (i = 0; i < this->SampleDimensions[0]; i++)
		{
		in_x = dest_bounds[0] + i*this->AspectRatio[0];
		in_i = (int) ((float)(in_x - in_bounds[0])/in_aspect[0]);
		if ((in_i >= 0)&&(in_i < in_dimensions[0]))
		  {
		  if (inScalars->GetScalar(in_kval+in_jval+in_i))
		    {
		    currentScalars->SetScalar(dest_kval + dest_jval+i,1);
		    }
		  }
		}
	      }
	    }
	  }
	}
      }
      break;
    }
}

// Description:
// Set the i-j-k dimensions on which to perform boolean operation.
void vtkBooleanStructuredPoints::SetSampleDimensions(int i, int j, int k)
{
  int dim[3];

  dim[0] = i;
  dim[1] = j;
  dim[2] = k;

  this->SetSampleDimensions(dim);
}

void vtkBooleanStructuredPoints::SetSampleDimensions(int dim[3])
{
  int i;

  vtkDebugMacro(<< " setting SampleDimensions to (" << dim[0] << "," << dim[1] << "," << dim[2] << ")");

  if ( dim[0] != this->SampleDimensions[0] || dim[1] != SampleDimensions[1] ||
  dim[2] != SampleDimensions[2] )
    {
    if ( dim[0]<0 || dim[1]<0 || dim[2]<0 )
      {
      vtkErrorMacro (<< "Bad Sample Dimensions, retaining previous values");
      return;
      }

    for ( i=0; i<3; i++) this->SampleDimensions[i] = dim[i];

    this->Modified();
    }
}

// Description:
// Set the size of the volume oon which to perform the sampling.
void vtkBooleanStructuredPoints::SetModelBounds(float *bounds)
{
  vtkBooleanStructuredPoints::SetModelBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);
}

void vtkBooleanStructuredPoints::SetModelBounds(float xmin, float xmax, float ymin, float ymax, float zmin, float zmax)
{
  if (this->ModelBounds[0] != xmin || this->ModelBounds[1] != xmax ||
  this->ModelBounds[2] != ymin || this->ModelBounds[3] != ymax ||
  this->ModelBounds[4] != zmin || this->ModelBounds[5] != zmax )
    {
    float length;

    this->Modified();
    this->ModelBounds[0] = xmin;
    this->ModelBounds[1] = xmax;
    this->ModelBounds[2] = ymin;
    this->ModelBounds[3] = ymax;
    this->ModelBounds[4] = zmin;
    this->ModelBounds[5] = zmax;

    this->Origin[0] = xmin;
    this->Origin[1] = ymin;
    this->Origin[2] = zmin;

    if ( (length = xmax - xmin) == 0.0 ) length = 1.0;
    this->AspectRatio[0] = 1.0;
    this->AspectRatio[1] = (ymax - ymin) / length;
    this->AspectRatio[2] = (zmax - zmin) / length;
    }
}

int vtkBooleanStructuredPoints::GetDataReleased()
{
  return this->DataReleased;
}

void vtkBooleanStructuredPoints::SetDataReleased(int flag)
{
  this->DataReleased = flag;
}

void vtkBooleanStructuredPoints::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkStructuredPoints::PrintSelf(os,indent);
  vtkFilter::_PrintSelf(os,indent);

  os << indent << "Input DataSets:\n";
  this->InputList.PrintSelf(os,indent.GetNextIndent());
}

<commit_msg>ENH: Made variable names consistent.<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    BoolSPts.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Toolkit. No part of this file
or its contents may be copied, reproduced or altered in any way
without the express written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
#include "BoolSPts.hh"

// Description:
// Construct with sample resolution of (50,50,50) and automatic
// computation of sample bounds. Initial boolean operation is union.
vtkBooleanStructuredPoints::vtkBooleanStructuredPoints()
{
  this->SampleDimensions[0] = 50;
  this->SampleDimensions[1] = 50;
  this->SampleDimensions[2] = 50;

  this->ModelBounds[0] = 0.0;
  this->ModelBounds[1] = 0.0;
  this->ModelBounds[2] = 0.0;
  this->ModelBounds[3] = 0.0;
  this->ModelBounds[4] = 0.0;
  this->ModelBounds[5] = 0.0;

  this->OperationType = UNION_OPERATOR;
  // this->Operator = this->Union;
}

vtkBooleanStructuredPoints::~vtkBooleanStructuredPoints()
{
}

unsigned long int vtkBooleanStructuredPoints::GetMTime()
{
  unsigned long dtime = this->vtkStructuredPoints::GetMTime();
  unsigned long ftime = this->vtkFilter::_GetMTime();
  return (dtime > ftime ? dtime : ftime);
}

// Description:
// Add another structured point set to the list of objects to boolean.
void vtkBooleanStructuredPoints::AddInput(vtkStructuredPoints *sp)
{
  if ( ! this->InputList.IsItemPresent(sp) )
    {
    this->Modified();
    this->InputList.AddItem(sp);
    }
}

// Description:
// Remove an object from the list of objects to boolean.
void vtkBooleanStructuredPoints::RemoveInput(vtkStructuredPoints *sp)
{
  if ( this->InputList.IsItemPresent(sp) )
    {
    this->Modified();
    this->InputList.RemoveItem(sp);
    }
}

void vtkBooleanStructuredPoints::Update()
{
  unsigned long int mtime, dsMtime;
  vtkDataSet *ds;

  // make sure input is available
  if ( this->InputList.GetNumberOfItems() < 1 ) return;

  // prevent chasing our tail
  if (this->Updating) return;

  this->Updating = 1;
  for (mtime=0, this->InputList.InitTraversal(); ds = this->InputList.GetNextItem(); )
    {
    ds->Update();
    dsMtime = ds->GetMTime();
    if ( dsMtime > mtime ) mtime = dsMtime;
    }
  this->Updating = 0;

  if (mtime > this->GetMTime() || this->GetMTime() > this->ExecuteTime ||
  this->GetDataReleased() )
    {
    if ( this->StartMethod ) (*this->StartMethod)(this->StartMethodArg);
    this->Execute();
    this->ExecuteTime.Modified();
    this->SetDataReleased(0);
    if ( this->EndMethod ) (*this->EndMethod)(this->EndMethodArg);
    }

  for (this->InputList.InitTraversal(); ds = this->InputList.GetNextItem(); )
    if ( ds->ShouldIReleaseData() ) ds->ReleaseData();
}

// Initialize object prior to performing Boolean operations
void vtkBooleanStructuredPoints::InitializeBoolean()
{
  vtkScalars *inScalars=NULL;
  vtkScalars *newScalars;
  vtkStructuredPoints *sp;
  float *bounds;
  int numPts;
  int i, j;

  this->Initialize();
  this->SetDimensions(this->SampleDimensions);
  numPts = this->GetNumberOfPoints();

  // If ModelBounds unset, use input, else punt
  if ( this->ModelBounds[0] >= this->ModelBounds[1] ||
  this->ModelBounds[2] >= this->ModelBounds[3] ||
  this->ModelBounds[4] >= this->ModelBounds[5] )
    {
    if ( this->InputList.GetNumberOfItems() > 0 )
      {
       this->ModelBounds[0] = this->ModelBounds[2] = this->ModelBounds[4] = LARGE_FLOAT;
       this->ModelBounds[1] = this->ModelBounds[3] = this->ModelBounds[5] = -LARGE_FLOAT;
      for ( this->InputList.InitTraversal(); sp = this->InputList.GetNextItem(); )
        {
        bounds = sp->GetBounds();
        for (j=0; j < 3; j++)
          {
          if ( bounds[2*j] < this->ModelBounds[2*j] )
            this->ModelBounds[2*j] = bounds[2*j];

          if ( bounds[2*j+1] > this->ModelBounds[2*j+1] )
            this->ModelBounds[2*j+1] = bounds[2*j+1];
          }
        }
      }
    else
      {
      this->ModelBounds[0] = this->ModelBounds[2] = this->ModelBounds[4] = 0.0;
      this->ModelBounds[1] = this->ModelBounds[3] = this->ModelBounds[5] = 1000.0;
      }
    }

  // Update origin and aspect ratio
  for (i=0; i<3; i++)
    {
    this->Origin[i] = this->ModelBounds[2*i];
    this->AspectRatio[i] = (this->ModelBounds[2*i+1] - this->ModelBounds[2*i])
                           / (this->SampleDimensions[i] - 1);
    }

  // Create output scalar (same type as input)
  if ( this->InputList.GetNumberOfItems() > 0 )
    {
    this->InputList.InitTraversal(); sp = this->InputList.GetNextItem();
    inScalars = sp->GetPointData()->GetScalars();
    }

  if ( inScalars != NULL )
    {
    newScalars = inScalars->MakeObject(numPts); //copy
    }
  else
    {
    newScalars = new vtkFloatScalars(numPts);
    }
  this->PointData.SetScalars(newScalars);
  newScalars->Delete();

}

// Perform Boolean operations on input volumes
void vtkBooleanStructuredPoints::Execute()
{
  vtkStructuredPoints *sp;

  this->InitializeBoolean();

  for ( this->InputList.InitTraversal(); sp = this->InputList.GetNextItem(); )
    {
    this->Append(sp);
    }
}

// Description:
// Perform Boolean operations by appending to current output data.
void vtkBooleanStructuredPoints::Append(vtkStructuredPoints *sp)
{
  vtkScalars *currentScalars, *inScalars;
  float *inBounds;
  float *destBounds;
  int i,j,k;
  float *in_aspect;
  float inX,inY,inZ;
  int inI,inJ,inK;
  int inKval,inJval;
  int destKval,destJval;
  int *inDimensions;

  if ( (currentScalars = this->PointData.GetScalars()) == NULL )
    {
    this->InitializeBoolean();
    currentScalars = this->PointData.GetScalars();
    }

  inScalars = sp->GetPointData()->GetScalars();

  inBounds = sp->GetBounds();
  destBounds = this->GetModelBounds();
  in_aspect = sp->GetAspectRatio();
  inDimensions = sp->GetDimensions();

  // now perform operation on data
  switch (this->OperationType)
    {
    case UNION_OPERATOR :
      {
      // for each cell
      for (k = 0; k < this->SampleDimensions[2]; k++)
	{
	inZ = destBounds[4] + k*this->AspectRatio[2];
	inK = int ((float)(inZ - inBounds[4])/in_aspect[2]);
	if ((inK >= 0)&&(inK < inDimensions[2]))
	  {
	  inKval = inK*inDimensions[0]*inDimensions[1];
	  destKval = k*this->SampleDimensions[0]*this->SampleDimensions[1];
	  for (j = 0; j < this->SampleDimensions[1]; j++)
	    {
	    inY = destBounds[2] + j*this->AspectRatio[1];
	    inJ = (int) ((float)(inY - inBounds[2])/in_aspect[1]);
	    if ((inJ >= 0)&&(inJ < inDimensions[1]))
	      {
	      inJval = inJ*inDimensions[0];
	      destJval = j*this->SampleDimensions[0];
	      for (i = 0; i < this->SampleDimensions[0]; i++)
		{
		inX = destBounds[0] + i*this->AspectRatio[0];
		inI = (int) ((float)(inX - inBounds[0])/in_aspect[0]);
		if ((inI >= 0)&&(inI < inDimensions[0]))
		  {
		  if (inScalars->GetScalar(inKval+inJval+inI))
		    {
		    currentScalars->SetScalar(destKval + destJval+i,1);
		    }
		  }
		}
	      }
	    }
	  }
	}
      }
      break;
    }
}

// Description:
// Set the i-j-k dimensions on which to perform boolean operation.
void vtkBooleanStructuredPoints::SetSampleDimensions(int i, int j, int k)
{
  int dim[3];

  dim[0] = i;
  dim[1] = j;
  dim[2] = k;

  this->SetSampleDimensions(dim);
}

void vtkBooleanStructuredPoints::SetSampleDimensions(int dim[3])
{
  int i;

  vtkDebugMacro(<< " setting SampleDimensions to (" << dim[0] << "," << dim[1] << "," << dim[2] << ")");

  if ( dim[0] != this->SampleDimensions[0] || dim[1] != SampleDimensions[1] ||
  dim[2] != SampleDimensions[2] )
    {
    if ( dim[0]<0 || dim[1]<0 || dim[2]<0 )
      {
      vtkErrorMacro (<< "Bad Sample Dimensions, retaining previous values");
      return;
      }

    for ( i=0; i<3; i++) this->SampleDimensions[i] = dim[i];

    this->Modified();
    }
}

// Description:
// Set the size of the volume on which to perform the sampling.
void vtkBooleanStructuredPoints::SetModelBounds(float *bounds)
{
  vtkBooleanStructuredPoints::SetModelBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]);
}

void vtkBooleanStructuredPoints::SetModelBounds(float xmin, float xmax, float ymin, float ymax, float zmin, float zmax)
{
  if (this->ModelBounds[0] != xmin || this->ModelBounds[1] != xmax ||
  this->ModelBounds[2] != ymin || this->ModelBounds[3] != ymax ||
  this->ModelBounds[4] != zmin || this->ModelBounds[5] != zmax )
    {
    float length;

    this->Modified();
    this->ModelBounds[0] = xmin;
    this->ModelBounds[1] = xmax;
    this->ModelBounds[2] = ymin;
    this->ModelBounds[3] = ymax;
    this->ModelBounds[4] = zmin;
    this->ModelBounds[5] = zmax;

    this->Origin[0] = xmin;
    this->Origin[1] = ymin;
    this->Origin[2] = zmin;

    if ( (length = xmax - xmin) == 0.0 ) length = 1.0;
    this->AspectRatio[0] = 1.0;
    this->AspectRatio[1] = (ymax - ymin) / length;
    this->AspectRatio[2] = (zmax - zmin) / length;
    }
}

int vtkBooleanStructuredPoints::GetDataReleased()
{
  return this->DataReleased;
}

void vtkBooleanStructuredPoints::SetDataReleased(int flag)
{
  this->DataReleased = flag;
}

void vtkBooleanStructuredPoints::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkStructuredPoints::PrintSelf(os,indent);
  vtkFilter::_PrintSelf(os,indent);

  os << indent << "Input DataSets:\n";
  this->InputList.PrintSelf(os,indent.GetNextIndent());
}

<|endoftext|>


<commit_before>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/core/common_runtime/immutable_executor_state.h"

#include "absl/memory/memory.h"
#include "tensorflow/core/framework/function.h"
#include "tensorflow/core/framework/metrics.h"
#include "tensorflow/core/framework/node_def_util.h"
#include "tensorflow/core/graph/edgeset.h"
#include "tensorflow/core/graph/graph.h"
#include "tensorflow/core/graph/graph_node_util.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/logging.h"

namespace tensorflow {

namespace {
bool IsInitializationOp(const Node* node) {
  return node->op_def().allows_uninitialized_input();
}
}  // namespace

ImmutableExecutorState::~ImmutableExecutorState() {
  for (int32_t i = 0; i < gview_.num_nodes(); i++) {
    NodeItem* item = gview_.node(i);
    if (item != nullptr) {
      params_.delete_kernel(item->kernel);
    }
  }
}

namespace {
void GetMaxPendingCounts(const Node* n, size_t* max_pending,
                         size_t* max_dead_count) {
  const size_t num_in_edges = n->in_edges().size();
  size_t initial_count;
  if (IsMerge(n)) {
    // merge waits all control inputs so we initialize the pending
    // count to be the number of control edges.
    int32_t num_control_edges = 0;
    for (const Edge* edge : n->in_edges()) {
      if (edge->IsControlEdge()) {
        num_control_edges++;
      }
    }
    // Use bit 0 to indicate if we are waiting for a ready live data input.
    initial_count = 1 + (num_control_edges << 1);
  } else {
    initial_count = num_in_edges;
  }

  *max_pending = initial_count;
  *max_dead_count = num_in_edges;
}
}  // namespace

ImmutableExecutorState::FrameInfo* ImmutableExecutorState::EnsureFrameInfo(
    const string& fname) {
  auto iter = frame_info_.find(fname);
  if (iter != frame_info_.end()) {
    return iter->second.get();
  } else {
    auto frame_info = absl::make_unique<FrameInfo>(fname);
    absl::string_view fname_view = frame_info->name;
    auto emplace_result =
        frame_info_.emplace(fname_view, std::move(frame_info));
    return emplace_result.first->second.get();
  }
}

Status ImmutableExecutorState::Initialize(const Graph& graph) {
  TF_RETURN_IF_ERROR(gview_.Initialize(&graph));

  // Build the information about frames in this subgraph.
  ControlFlowInfo cf_info;
  TF_RETURN_IF_ERROR(BuildControlFlowInfo(&graph, &cf_info));

  for (auto& it : cf_info.unique_frame_names) {
    EnsureFrameInfo(it)->nodes =
        absl::make_unique<std::vector<const NodeItem*>>();
  }
  root_frame_info_ = frame_info_[""].get();

  pending_ids_.resize(gview_.num_nodes());

  // Preprocess every node in the graph to create an instance of op
  // kernel for each node.
  requires_control_flow_ = false;
  for (const Node* n : graph.nodes()) {
    if (IsSink(n)) continue;
    if (IsSwitch(n) || IsMerge(n) || IsEnter(n) || IsExit(n)) {
      requires_control_flow_ = true;
    } else if (IsRecv(n)) {
      // A Recv node from a different device may produce dead tensors from
      // non-local control-flow nodes.
      //
      // TODO(mrry): Track whether control flow was present in the
      // pre-partitioned graph, and enable the caller (e.g.
      // `DirectSession`) to relax this constraint.
      string send_device;
      string recv_device;
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "send_device", &send_device));
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "recv_device", &recv_device));
      if (send_device != recv_device) {
        requires_control_flow_ = true;
      }
    }

    const int id = n->id();
    const string& frame_name = cf_info.frame_names[id];
    FrameInfo* frame_info = EnsureFrameInfo(frame_name);

    NodeItem* item = gview_.node(id);
    item->node_id = id;

    item->input_start = frame_info->total_inputs;
    frame_info->total_inputs += n->num_inputs();

    Status s = params_.create_kernel(n->properties(), &item->kernel);
    if (!s.ok()) {
      item->kernel = nullptr;
      s = AttachDef(s, *n);
      return s;
    }
    CHECK(item->kernel);
    item->kernel_is_async = (item->kernel->AsAsync() != nullptr);
    item->is_merge = IsMerge(n);
    item->is_any_consumer_merge_or_control_trigger = false;
    for (const Node* consumer : n->out_nodes()) {
      if (IsMerge(consumer) || IsControlTrigger(consumer)) {
        item->is_any_consumer_merge_or_control_trigger = true;
        break;
      }
    }
    const Tensor* const_tensor = item->kernel->const_tensor();
    if (const_tensor) {
      // Hold onto a shallow copy of the constant tensor in `*this` so that the
      // reference count does not drop to 1. This prevents the constant tensor
      // from being forwarded, and its buffer reused.
      const_tensors_.emplace_back(*const_tensor);
    }
    item->const_tensor = const_tensor;
    item->is_noop = (item->kernel->type_string_view() == "NoOp");
    item->is_enter = IsEnter(n);
    if (item->is_enter) {
      bool is_constant_enter;
      TF_RETURN_IF_ERROR(
          GetNodeAttr(n->attrs(), "is_constant", &is_constant_enter));
      item->is_constant_enter = is_constant_enter;

      string frame_name;
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "frame_name", &frame_name));
      FrameInfo* frame_info = frame_info_[frame_name].get();

      int parallel_iterations;
      TF_RETURN_IF_ERROR(
          GetNodeAttr(n->attrs(), "parallel_iterations", &parallel_iterations));

      if (frame_info->parallel_iterations == -1) {
        frame_info->parallel_iterations = parallel_iterations;
      } else if (frame_info->parallel_iterations != parallel_iterations) {
        LOG(WARNING) << "Loop frame \"" << frame_name
                     << "\" had two different values for parallel_iterations: "
                     << frame_info->parallel_iterations << " vs. "
                     << parallel_iterations << ".";
      }

      if (enter_frame_info_.size() <= id) {
        enter_frame_info_.resize(id + 1);
      }
      enter_frame_info_[id] = frame_info;
    } else {
      item->is_constant_enter = false;
    }
    item->is_exit = IsExit(n);
    item->is_control_trigger = IsControlTrigger(n);
    item->is_source = IsSource(n);
    item->is_enter_exit_or_next_iter =
        (IsEnter(n) || IsExit(n) || IsNextIteration(n));
    item->is_transfer_node = IsTransferNode(n);
    item->is_initialization_op = IsInitializationOp(n);
    item->is_recv_or_switch = IsRecv(n) || IsSwitch(n);
    item->is_next_iteration = IsNextIteration(n);
    item->is_distributed_communication = IsDistributedCommunication(n);

    // Compute the maximum values we'll store for this node in the
    // pending counts data structure, and allocate a handle in
    // that frame's pending counts data structure that has enough
    // space to store these maximal count values.
    size_t max_pending, max_dead;
    GetMaxPendingCounts(n, &max_pending, &max_dead);
    pending_ids_[id] =
        frame_info->pending_counts_layout.CreateHandle(max_pending, max_dead);

    // See if this node is a root node, and if so, add item to root_nodes_.
    if (n->in_edges().empty()) {
      root_nodes_.push_back(item);
    }

    // Initialize static information about the frames in the graph.
    frame_info->nodes->push_back(item);
    if (item->is_enter) {
      string enter_name;
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "frame_name", &enter_name));
      EnsureFrameInfo(enter_name)->input_count++;
    }

    // Record information about whether each output of the op is used.
    std::unique_ptr<bool[]> outputs_required(new bool[n->num_outputs()]);
    std::fill(&outputs_required[0], &outputs_required[n->num_outputs()], false);
    int32_t unused_outputs = n->num_outputs();
    for (const Edge* e : n->out_edges()) {
      if (IsSink(e->dst())) continue;
      if (e->src_output() >= 0) {
        if (!outputs_required[e->src_output()]) {
          --unused_outputs;
          outputs_required[e->src_output()] = true;
        }
      }
    }
    if (unused_outputs > 0) {
      for (int i = 0; i < n->num_outputs(); ++i) {
        if (!outputs_required[i]) {
          metrics::RecordUnusedOutput(n->type_string());
        }
      }
      item->outputs_required = std::move(outputs_required);
    }
  }

  // Rewrite each `EdgeInfo::input_slot` member to refer directly to the input
  // location.
  for (const Node* n : graph.nodes()) {
    if (IsSink(n)) continue;
    const int id = n->id();
    NodeItem* item = gview_.node(id);

    for (EdgeInfo& e : item->mutable_output_edges()) {
      const int dst_id = e.dst_id;
      NodeItem* dst_item = gview_.node(dst_id);
      e.input_slot += dst_item->input_start;
    }
  }

  // Initialize PendingCounts only after pending_ids_[node.id] is initialized
  // for all nodes.
  InitializePending(&graph, cf_info);
  return gview_.SetAllocAttrs(&graph, params_.device);
}

namespace {
// If a Node has been marked to use a ScopedAllocator x for output i, then
// sc_attr will contain the subsequence (i, x) at an even offset.  This function
// extracts and transfers that ScopedAllocator id to alloc_attr.  For now, we
// only allow one ScopedAllocator use per Node.
bool ExtractScopedAllocatorAttr(const std::vector<int>& sc_attr,
                                int output_index,
                                AllocatorAttributes* alloc_attr) {
  DCHECK_LE(2, sc_attr.size());
  for (int i = 0; i < sc_attr.size(); i += 2) {
    if (sc_attr[i] == output_index) {
      CHECK_EQ(alloc_attr->scope_id, 0);
      alloc_attr->scope_id = sc_attr[i + 1];
      return true;
    }
  }
  return false;
}
}  // namespace

Status ImmutableExecutorState::BuildControlFlowInfo(const Graph* g,
                                                    ControlFlowInfo* cf_info) {
  const int num_nodes = g->num_node_ids();
  cf_info->frame_names.resize(num_nodes);
  std::vector<Node*> parent_nodes;
  parent_nodes.resize(num_nodes);
  std::vector<bool> visited;
  visited.resize(num_nodes);

  string frame_name;
  std::deque<Node*> ready;

  // Initialize with the root nodes.
  for (Node* n : g->nodes()) {
    if (n->in_edges().empty()) {
      visited[n->id()] = true;
      cf_info->unique_frame_names.insert(frame_name);
      ready.push_back(n);
    }
  }

  while (!ready.empty()) {
    Node* curr_node = ready.front();
    int curr_id = curr_node->id();
    ready.pop_front();

    Node* parent = nullptr;
    if (IsEnter(curr_node)) {
      // Enter a child frame.
      TF_RETURN_IF_ERROR(
          GetNodeAttr(curr_node->attrs(), "frame_name", &frame_name));
      parent = curr_node;
    } else if (IsExit(curr_node)) {
      // Exit to the parent frame.
      parent = parent_nodes[curr_id];
      if (!parent) {
        return errors::InvalidArgument(
            "Invalid Exit op: Cannot find a corresponding Enter op.");
      }
      frame_name = cf_info->frame_names[parent->id()];
      parent = parent_nodes[parent->id()];
    } else {
      parent = parent_nodes[curr_id];
      frame_name = cf_info->frame_names[curr_id];
    }

    for (const Edge* out_edge : curr_node->out_edges()) {
      Node* out = out_edge->dst();
      if (IsSink(out)) continue;
      const int out_id = out->id();

      // Add to ready queue if not visited.
      bool is_visited = visited[out_id];
      if (!is_visited) {
        ready.push_back(out);
        visited[out_id] = true;

        // Process the node 'out'.
        cf_info->frame_names[out_id] = frame_name;
        parent_nodes[out_id] = parent;
        cf_info->unique_frame_names.insert(frame_name);
      }
    }
  }

  return Status::OK();
}

void ImmutableExecutorState::InitializePending(const Graph* graph,
                                               const ControlFlowInfo& cf_info) {
  for (auto& it : cf_info.unique_frame_names) {
    FrameInfo* finfo = EnsureFrameInfo(it);
    DCHECK_EQ(finfo->pending_counts.get(), nullptr);
    finfo->pending_counts =
        absl::make_unique<PendingCounts>(finfo->pending_counts_layout);
  }

  if (!requires_control_flow_) {
    atomic_pending_counts_.reset(new std::atomic<int32>[gview_.num_nodes()]);
    std::fill(atomic_pending_counts_.get(),
              atomic_pending_counts_.get() + gview_.num_nodes(), 0);
  }

  for (const Node* n : graph->nodes()) {
    if (IsSink(n)) continue;
    const int id = n->id();
    const string& name = cf_info.frame_names[id];
    size_t max_pending, max_dead;
    GetMaxPendingCounts(n, &max_pending, &max_dead);
    auto& counts = EnsureFrameInfo(name)->pending_counts;
    counts->set_initial_count(pending_ids_[id], max_pending);
    if (!requires_control_flow_) {
      atomic_pending_counts_[id] = max_pending;
    }
  }
}
}  // namespace tensorflow
<commit_msg>Fix memory leak when a graph node is invalid.<commit_after>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/core/common_runtime/immutable_executor_state.h"

#include "absl/memory/memory.h"
#include "tensorflow/core/framework/function.h"
#include "tensorflow/core/framework/metrics.h"
#include "tensorflow/core/framework/node_def_util.h"
#include "tensorflow/core/graph/edgeset.h"
#include "tensorflow/core/graph/graph.h"
#include "tensorflow/core/graph/graph_node_util.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/logging.h"

namespace tensorflow {

namespace {
bool IsInitializationOp(const Node* node) {
  return node->op_def().allows_uninitialized_input();
}
}  // namespace

ImmutableExecutorState::~ImmutableExecutorState() {
  for (int32_t i = 0; i < gview_.num_nodes(); i++) {
    NodeItem* item = gview_.node(i);
    if (item != nullptr) {
      params_.delete_kernel(item->kernel);
    }
  }
}

namespace {
void GetMaxPendingCounts(const Node* n, size_t* max_pending,
                         size_t* max_dead_count) {
  const size_t num_in_edges = n->in_edges().size();
  size_t initial_count;
  if (IsMerge(n)) {
    // merge waits all control inputs so we initialize the pending
    // count to be the number of control edges.
    int32_t num_control_edges = 0;
    for (const Edge* edge : n->in_edges()) {
      if (edge->IsControlEdge()) {
        num_control_edges++;
      }
    }
    // Use bit 0 to indicate if we are waiting for a ready live data input.
    initial_count = 1 + (num_control_edges << 1);
  } else {
    initial_count = num_in_edges;
  }

  *max_pending = initial_count;
  *max_dead_count = num_in_edges;
}
}  // namespace

ImmutableExecutorState::FrameInfo* ImmutableExecutorState::EnsureFrameInfo(
    const string& fname) {
  auto iter = frame_info_.find(fname);
  if (iter != frame_info_.end()) {
    return iter->second.get();
  } else {
    auto frame_info = absl::make_unique<FrameInfo>(fname);
    absl::string_view fname_view = frame_info->name;
    auto emplace_result =
        frame_info_.emplace(fname_view, std::move(frame_info));
    return emplace_result.first->second.get();
  }
}

Status ImmutableExecutorState::Initialize(const Graph& graph) {
  TF_RETURN_IF_ERROR(gview_.Initialize(&graph));

  // Build the information about frames in this subgraph.
  ControlFlowInfo cf_info;
  TF_RETURN_IF_ERROR(BuildControlFlowInfo(&graph, &cf_info));

  for (auto& it : cf_info.unique_frame_names) {
    EnsureFrameInfo(it)->nodes =
        absl::make_unique<std::vector<const NodeItem*>>();
  }
  root_frame_info_ = frame_info_[""].get();

  pending_ids_.resize(gview_.num_nodes());

  // Preprocess every node in the graph to create an instance of op
  // kernel for each node.
  requires_control_flow_ = false;
  for (const Node* n : graph.nodes()) {
    if (IsSink(n)) continue;
    if (IsSwitch(n) || IsMerge(n) || IsEnter(n) || IsExit(n)) {
      requires_control_flow_ = true;
    } else if (IsRecv(n)) {
      // A Recv node from a different device may produce dead tensors from
      // non-local control-flow nodes.
      //
      // TODO(mrry): Track whether control flow was present in the
      // pre-partitioned graph, and enable the caller (e.g.
      // `DirectSession`) to relax this constraint.
      string send_device;
      string recv_device;
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "send_device", &send_device));
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "recv_device", &recv_device));
      if (send_device != recv_device) {
        requires_control_flow_ = true;
      }
    }

    const int id = n->id();
    const string& frame_name = cf_info.frame_names[id];
    FrameInfo* frame_info = EnsureFrameInfo(frame_name);

    NodeItem* item = gview_.node(id);
    item->node_id = id;

    item->input_start = frame_info->total_inputs;
    frame_info->total_inputs += n->num_inputs();

    Status s = params_.create_kernel(n->properties(), &item->kernel);
    if (!s.ok()) {
      params_.delete_kernel(item->kernel);
      item->kernel = nullptr;
      s = AttachDef(s, *n);
      return s;
    }
    CHECK(item->kernel);
    item->kernel_is_async = (item->kernel->AsAsync() != nullptr);
    item->is_merge = IsMerge(n);
    item->is_any_consumer_merge_or_control_trigger = false;
    for (const Node* consumer : n->out_nodes()) {
      if (IsMerge(consumer) || IsControlTrigger(consumer)) {
        item->is_any_consumer_merge_or_control_trigger = true;
        break;
      }
    }
    const Tensor* const_tensor = item->kernel->const_tensor();
    if (const_tensor) {
      // Hold onto a shallow copy of the constant tensor in `*this` so that the
      // reference count does not drop to 1. This prevents the constant tensor
      // from being forwarded, and its buffer reused.
      const_tensors_.emplace_back(*const_tensor);
    }
    item->const_tensor = const_tensor;
    item->is_noop = (item->kernel->type_string_view() == "NoOp");
    item->is_enter = IsEnter(n);
    if (item->is_enter) {
      bool is_constant_enter;
      TF_RETURN_IF_ERROR(
          GetNodeAttr(n->attrs(), "is_constant", &is_constant_enter));
      item->is_constant_enter = is_constant_enter;

      string frame_name;
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "frame_name", &frame_name));
      FrameInfo* frame_info = frame_info_[frame_name].get();

      int parallel_iterations;
      TF_RETURN_IF_ERROR(
          GetNodeAttr(n->attrs(), "parallel_iterations", &parallel_iterations));

      if (frame_info->parallel_iterations == -1) {
        frame_info->parallel_iterations = parallel_iterations;
      } else if (frame_info->parallel_iterations != parallel_iterations) {
        LOG(WARNING) << "Loop frame \"" << frame_name
                     << "\" had two different values for parallel_iterations: "
                     << frame_info->parallel_iterations << " vs. "
                     << parallel_iterations << ".";
      }

      if (enter_frame_info_.size() <= id) {
        enter_frame_info_.resize(id + 1);
      }
      enter_frame_info_[id] = frame_info;
    } else {
      item->is_constant_enter = false;
    }
    item->is_exit = IsExit(n);
    item->is_control_trigger = IsControlTrigger(n);
    item->is_source = IsSource(n);
    item->is_enter_exit_or_next_iter =
        (IsEnter(n) || IsExit(n) || IsNextIteration(n));
    item->is_transfer_node = IsTransferNode(n);
    item->is_initialization_op = IsInitializationOp(n);
    item->is_recv_or_switch = IsRecv(n) || IsSwitch(n);
    item->is_next_iteration = IsNextIteration(n);
    item->is_distributed_communication = IsDistributedCommunication(n);

    // Compute the maximum values we'll store for this node in the
    // pending counts data structure, and allocate a handle in
    // that frame's pending counts data structure that has enough
    // space to store these maximal count values.
    size_t max_pending, max_dead;
    GetMaxPendingCounts(n, &max_pending, &max_dead);
    pending_ids_[id] =
        frame_info->pending_counts_layout.CreateHandle(max_pending, max_dead);

    // See if this node is a root node, and if so, add item to root_nodes_.
    if (n->in_edges().empty()) {
      root_nodes_.push_back(item);
    }

    // Initialize static information about the frames in the graph.
    frame_info->nodes->push_back(item);
    if (item->is_enter) {
      string enter_name;
      TF_RETURN_IF_ERROR(GetNodeAttr(n->attrs(), "frame_name", &enter_name));
      EnsureFrameInfo(enter_name)->input_count++;
    }

    // Record information about whether each output of the op is used.
    std::unique_ptr<bool[]> outputs_required(new bool[n->num_outputs()]);
    std::fill(&outputs_required[0], &outputs_required[n->num_outputs()], false);
    int32_t unused_outputs = n->num_outputs();
    for (const Edge* e : n->out_edges()) {
      if (IsSink(e->dst())) continue;
      if (e->src_output() >= 0) {
        if (!outputs_required[e->src_output()]) {
          --unused_outputs;
          outputs_required[e->src_output()] = true;
        }
      }
    }
    if (unused_outputs > 0) {
      for (int i = 0; i < n->num_outputs(); ++i) {
        if (!outputs_required[i]) {
          metrics::RecordUnusedOutput(n->type_string());
        }
      }
      item->outputs_required = std::move(outputs_required);
    }
  }

  // Rewrite each `EdgeInfo::input_slot` member to refer directly to the input
  // location.
  for (const Node* n : graph.nodes()) {
    if (IsSink(n)) continue;
    const int id = n->id();
    NodeItem* item = gview_.node(id);

    for (EdgeInfo& e : item->mutable_output_edges()) {
      const int dst_id = e.dst_id;
      NodeItem* dst_item = gview_.node(dst_id);
      e.input_slot += dst_item->input_start;
    }
  }

  // Initialize PendingCounts only after pending_ids_[node.id] is initialized
  // for all nodes.
  InitializePending(&graph, cf_info);
  return gview_.SetAllocAttrs(&graph, params_.device);
}

namespace {
// If a Node has been marked to use a ScopedAllocator x for output i, then
// sc_attr will contain the subsequence (i, x) at an even offset.  This function
// extracts and transfers that ScopedAllocator id to alloc_attr.  For now, we
// only allow one ScopedAllocator use per Node.
bool ExtractScopedAllocatorAttr(const std::vector<int>& sc_attr,
                                int output_index,
                                AllocatorAttributes* alloc_attr) {
  DCHECK_LE(2, sc_attr.size());
  for (int i = 0; i < sc_attr.size(); i += 2) {
    if (sc_attr[i] == output_index) {
      CHECK_EQ(alloc_attr->scope_id, 0);
      alloc_attr->scope_id = sc_attr[i + 1];
      return true;
    }
  }
  return false;
}
}  // namespace

Status ImmutableExecutorState::BuildControlFlowInfo(const Graph* g,
                                                    ControlFlowInfo* cf_info) {
  const int num_nodes = g->num_node_ids();
  cf_info->frame_names.resize(num_nodes);
  std::vector<Node*> parent_nodes;
  parent_nodes.resize(num_nodes);
  std::vector<bool> visited;
  visited.resize(num_nodes);

  string frame_name;
  std::deque<Node*> ready;

  // Initialize with the root nodes.
  for (Node* n : g->nodes()) {
    if (n->in_edges().empty()) {
      visited[n->id()] = true;
      cf_info->unique_frame_names.insert(frame_name);
      ready.push_back(n);
    }
  }

  while (!ready.empty()) {
    Node* curr_node = ready.front();
    int curr_id = curr_node->id();
    ready.pop_front();

    Node* parent = nullptr;
    if (IsEnter(curr_node)) {
      // Enter a child frame.
      TF_RETURN_IF_ERROR(
          GetNodeAttr(curr_node->attrs(), "frame_name", &frame_name));
      parent = curr_node;
    } else if (IsExit(curr_node)) {
      // Exit to the parent frame.
      parent = parent_nodes[curr_id];
      if (!parent) {
        return errors::InvalidArgument(
            "Invalid Exit op: Cannot find a corresponding Enter op.");
      }
      frame_name = cf_info->frame_names[parent->id()];
      parent = parent_nodes[parent->id()];
    } else {
      parent = parent_nodes[curr_id];
      frame_name = cf_info->frame_names[curr_id];
    }

    for (const Edge* out_edge : curr_node->out_edges()) {
      Node* out = out_edge->dst();
      if (IsSink(out)) continue;
      const int out_id = out->id();

      // Add to ready queue if not visited.
      bool is_visited = visited[out_id];
      if (!is_visited) {
        ready.push_back(out);
        visited[out_id] = true;

        // Process the node 'out'.
        cf_info->frame_names[out_id] = frame_name;
        parent_nodes[out_id] = parent;
        cf_info->unique_frame_names.insert(frame_name);
      }
    }
  }

  return Status::OK();
}

void ImmutableExecutorState::InitializePending(const Graph* graph,
                                               const ControlFlowInfo& cf_info) {
  for (auto& it : cf_info.unique_frame_names) {
    FrameInfo* finfo = EnsureFrameInfo(it);
    DCHECK_EQ(finfo->pending_counts.get(), nullptr);
    finfo->pending_counts =
        absl::make_unique<PendingCounts>(finfo->pending_counts_layout);
  }

  if (!requires_control_flow_) {
    atomic_pending_counts_.reset(new std::atomic<int32>[gview_.num_nodes()]);
    std::fill(atomic_pending_counts_.get(),
              atomic_pending_counts_.get() + gview_.num_nodes(), 0);
  }

  for (const Node* n : graph->nodes()) {
    if (IsSink(n)) continue;
    const int id = n->id();
    const string& name = cf_info.frame_names[id];
    size_t max_pending, max_dead;
    GetMaxPendingCounts(n, &max_pending, &max_dead);
    auto& counts = EnsureFrameInfo(name)->pending_counts;
    counts->set_initial_count(pending_ids_[id], max_pending);
    if (!requires_control_flow_) {
      atomic_pending_counts_[id] = max_pending;
    }
  }
}
}  // namespace tensorflow
<|endoftext|>