Hi all!

We are consistently triggering a probable bug in OpenSG where it complains
about "link inconsistent" after shallow clone and destroying all refs to
the original.

This happens on the following sequence of events (which the attached code
demonstrates)

1. create node with group core
2. apply changelist in another aspect's thread
3. clone node with cloneTree (share core)
4. clear refereces to original
5. apply change list in another aspect's thread again

I've attached the log output (with dumped changelists), if it helps.

The attached code provides a small repro case (testSyncChangeLists) which I
believe is the minimal code to trigger the bug, and does run on it's own
(no dependency except the changelist dump).

Before I dig deeper to the cause in OpenSG, are we doing something wrong
with how we sync changelists/clone objects, or is this a genuine bug?

Cheers,
-- 
Marcus Lindblom Sonestedt
*Systemarkitekt*
*BIT ADDICT *- Passion för utveckling
+46 (0)706 43 63 28
marcus.lindblom.sonest...@bitaddict.se
www.bitaddict.se
ColorBuffer : 0x0000000000000001
DepthBuffer : 0x0000000000000002
GBuffer     : 0x0000000000000004
ShadowFactor: 0x0000000000000008

-- Creating scenegraph
-- Applying main changelist
Change list has change/create entries: 3 2
  id: 289 ptr: 00000000039DDC80 type: Node name: (null) change: Change  fields: 
[<ALL>]
  id: 290 ptr: 00000000042D9240 type: Group name: (null) change: Change  
fields: [<ALL>]
  id: 289 ptr: 00000000039DDC80 type: Node name: (null) change: AddReference
-- Cloning scene graph and applying
-- Applying main changelist again
Change list has change/create entries: 5 1
  id: 291 ptr: 00000000041A5FD0 type: Node name: (null) change: Change  fields: 
[<ALL>]
  id: 290 ptr: 00000000042D9320 type: Group name: (null) change: Change
  id: 291 ptr: 00000000041A5FD0 type: Node name: (null) change: AddReference
  id: 289 ptr: 00000000039DDDB0 type: Node name: (null) change: Change
  id: 289 ptr: 00000000039DDDB0 type: Node name: (null) change: SubReference
OpenSG aspect 1: WARNING: Child ([00000000042D9320] id [290] type [Group] 
parentFieldId [3]) - Parent ([00000000039DDDB0
] id [289] type [Node]): link inconsistent!

BUG TRIGGERED
FieldConnectorFactoryBase::terminate
OpenSG aspect 0: WARNING: FieldContainerFactoryBase::terminate: Entry [290] is 
not NULL ([00000000042BF920]).

#include <vizOpenSGDebug.h>

#include <OpenSG/OSGBackground.h>
#include <OpenSG/OSGCamera.h>
#include <OpenSG/OSGChangeList.h>
#include <OpenSG/OSGNameAttachment.h>
#include <OpenSG/OSGNode.h>
#include <OpenSG/OSGViewport.h>

namespace viz
{
  namespace
  {
    class FCHack : public OSG::FieldContainer {
    public:
      using OSG::FieldContainer::getAspectPtr;
      using OSG::FieldContainer::getAspectStore;
    };
  }


  int countChildren(const OSG::Node* node)
  {
    if (!node)
      return 0;

    int count = 1;
    for (const auto child : *node->getMFChildren()) {
      count += countChildren(child);
    }
    return count;
  }

  void dumpNumNodes(const OSG::Node* root)
  {
#if 0
    int aspect = OSG::Thread::getCurrentAspect();
    printf("Nodes under root for current aspect %d: %d\n", aspect, countChildren(root));

    int otherAspect = (aspect == 0) ? 1 : 0;
    auto rootAsFC = static_cast<FieldContainer*>(root);
    auto rootOtherAspectFC = static_cast<FCHack*>(rootAsFC)->getAspectPtr(otherAspect);
    auto rootOtherAspect = static_cast<OSG::Node*>(rootOtherAspectFC);
    printf("Nodes under root for other aspect %d: %d\n", otherAspect, countChildren(rootOtherAspect));
#endif
  }


  void reportIfInChangeList(const OSG::ChangeList* cl, const OSG::Node* node,
    const char* changeListName)
  {

    auto fc = static_cast<const FCHack*>(static_cast<const OSG::FieldContainer*>(node))
      ->getAspectStore()->getPtr(0);

    assert(node);

    const auto nonRefChange = ~(OSG::ContainerChangeEntry::AddReference |
      OSG::ContainerChangeEntry::SubReference | OSG::ContainerChangeEntry::DepSubReference);

    auto id = fc->getId();
    auto matchesId = [=](const OSG::ContainerChangeEntry* e) {
      return e->uiContainerId == id && (e->uiEntryDesc & nonRefChange);
    };

    auto i = std::find_if(cl->begin(), cl->end(), matchesId);
    if (i != cl->end())
      printf("%d %s %s is changed in %s changelist\n", id, fc->getTypeName(), getName(node),
        changeListName);

    auto i2 = std::find_if(cl->beginCreated(), cl->endCreated(), matchesId);
    if (i2 != cl->endCreated())
      printf("%d %s %s is created in %s changelist\n", id, fc->getTypeName(), getName(node),
        changeListName);
  }

  void dumpChangeList(const OSG::ChangeList* changeList, int id)
  {
    printf("Change list has change/create entries: %d %d\n", changeList->getNumChanged(),
      changeList->getNumCreated());

    for (const auto e : *changeList) {
      if (id != -1 && id != e->uiContainerId)
        continue;

      auto fc = OSG::FieldContainerFactory::the()->getContainer(e->uiContainerId);
      if (!fc) {
        auto fcstore = OSG::FieldContainerFactory::the()->getFieldContainerStore();
        auto aspectstore = fcstore[e->uiContainerId];
        if (!aspectstore) {
          printf("change for id %d not in container store?!\n", e->uiContainerId);
          continue;
        }
        for (int i = 0; i < aspectstore->getNumAspects() && !fc; ++i) {
          fc = aspectstore->getPtr(i); // scan all aspects, get something
        }
      }

      auto ac = dynamic_cast<OSG::AttachmentContainer*>(fc);
      auto name = ac ? getName(ac) : "";

      std::string entryDesc;
      if (e->uiEntryDesc & OSG::ContainerChangeEntry::Create) entryDesc += "Create ";
      if (e->uiEntryDesc & OSG::ContainerChangeEntry::AddReference) entryDesc += "AddReference ";
      if (e->uiEntryDesc & OSG::ContainerChangeEntry::SubReference) entryDesc += "SubReference ";
      if (e->uiEntryDesc & OSG::ContainerChangeEntry::DepSubReference) entryDesc += "DepSubReference ";
      if (e->uiEntryDesc & OSG::ContainerChangeEntry::Change) entryDesc += "Change ";
      if (e->uiEntryDesc & OSG::ContainerChangeEntry::AddField) entryDesc += "Add/SubField "; // same id

      printf("  id: %i ptr: %p type: %s name: %s change: %s ", e->uiContainerId, fc,
        fc->getTypeName(), name, entryDesc.c_str());

      std::string fields;
      if (e->whichField == std::numeric_limits<OSG::BitVector>::max()) {
        fields = "<ALL>";
      }
      else {
        for (OSG::UInt64 i = 0; i < 64 && i < fc->getNumFields(); ++i) {
          if (e->whichField & (OSG::UInt64(1) << i)) {
            auto field = fc->getFieldDescription(i);
            if (field) {
              fields += field->getName() + ", ";
            }
            else {
              printf("\n  No field # %i on container %s?\n", i, typeid(*fc).name());
            }
          }
        }
      }

      if (!fields.empty()) {
        printf("fields: [%s]\n", fields.c_str());
      }
      else {
        printf("\n");
      }
    }
  }

  void dumpViewport(const OSG::Viewport* vp)
  {
    printf("this: %p %d %s\n", vp, vp->getId(), getName(vp));
    printf("cam:  %p %d %s\n", vp->getCamera(), vp->getCamera()->getId(), getName(vp->getCamera()));
    printf("root: %p %d %s (%d children)\n", vp->getRoot(), vp->getRoot()->getId(),
      getName(vp->getRoot()), countChildren(vp->getRoot()));
    printf("bg:   %p %d %s\n", vp->getBackground(), vp->getBackground()->getId(), getName(vp->getBackground()));
    printf("pos:  %f %f %f %f\n", vp->getLeft(), vp->getBottom(), vp->getRight(), vp->getTop());
    printf("mask: %x\n", vp->getTravMask());
  }
}
#define NO_CPPUNIT

#ifndef NO_CPPUNIT
#include <cppunit/extensions/HelperMacros.h>
#endif

#pragma warning(disable: 4290) // exception specification ignored

#include <vizOpenSGDebug.h>

#include <OpenSG/OSGGroup.h>
#include <OpenSG/OSGNode.h>
#include <OpenSG/OSGBarrier.h>
#include <string>

using namespace OSG;

class vizAspectsLinkConsistencyTest
#ifndef NO_CPPUNIT
  : public CppUnit::TestFixture
{
  CPPUNIT_TEST_SUITE(vizAspectsLinkConsistencyTest);
  CPPUNIT_TEST(testSyncChangeList);
  CPPUNIT_TEST_SUITE_END();
#else
  {
#endif

public:
  void setUp();
  void tearDown();

  void testSyncChangeList();

  OSG::BarrierRefPtr mSyncBarrier;
  std::string mOsgLog;
};

#ifndef NO_CPPUNIT
CPPUNIT_TEST_SUITE_REGISTRATION( vizAspectsLinkConsistencyTest );
#endif
namespace
{
  void OSGLogBufCallback(const Char8 *data,
    Int32  size,
    void  *clientData)
  {
    auto test = reinterpret_cast<vizAspectsLinkConsistencyTest*>(clientData);

    std::string message(data, size);
    printf("OpenSG aspect %i: %s\n", OSG::Thread::getCurrentAspect(), message.c_str());
    test->mOsgLog += message;

    if (message.find("link inconsistent") != std::string::npos)
      printf("BUG TRIGGERED\n");
  }
}

void vizAspectsLinkConsistencyTest::setUp()
{
  mSyncBarrier = OSG::Barrier::get("vizAspectsLinkConsistencyTest", false);
  mSyncBarrier->setNumWaitFor(2);

  auto& log = OSG::osgLog();
  auto& logBuf = log.getLogBuf();
  logBuf.setCallback(&OSGLogBufCallback, this, true);
  log.setLogType(OSG::LOG_BUFFER);

}


void vizAspectsLinkConsistencyTest::tearDown()
{
  mSyncBarrier = nullptr;
}

namespace
{
  void runStdFunction(void *args)
  {
    auto func = reinterpret_cast<std::function<void()>*>(args);
    (*func)();
  }
}

void
vizAspectsLinkConsistencyTest::testSyncChangeList()
{
  auto mainThread = OSG::Thread::getCurrent();

  std::function<void()> threadFunc = [&]()
  {
    mSyncBarrier->enter(); // 1

    printf("-- Applying main changelist\n");
    viz::dumpChangeList(mainThread->getChangeList());
    mainThread->getChangeList()->applyAndClear(); // remove this and bug disappers

    mSyncBarrier->enter(); // 2
    mSyncBarrier->enter(); // 3

    printf("-- Applying main changelist again\n");
    viz::dumpChangeList(mainThread->getChangeList());
    mainThread->getChangeList()->applyAndClear(); // bug triggers here

    mSyncBarrier->enter(); // 4
  };

  // start thread above
  OSG::ThreadRefPtr thread = Thread::get("thread", true);
  thread->runFunction(runStdFunction, 1, &threadFunc);

  // run main thread
  {
    printf("\n-- Creating scenegraph\n");

    OSG::NodePtr scene = OSG::makeCoredNode<OSG::Group>();
    OSG::commitChanges();

    mSyncBarrier->enter(); // 1
    mSyncBarrier->enter(); // 2

    OSG::Thread::getCurrentChangeList()->clear();

    printf("-- Cloning scene graph and applying\n");

    OSG::NodePtr sceneClone = cloneTree(scene); // remove this and bug disappears
    scene = nullptr; // remove this and bug disappears
    OSG::commitChanges();

    mSyncBarrier->enter(); // 3
    mSyncBarrier->enter(); // 4
  }

  while(thread->exists());

#ifndef NO_CPPUNIT
  CPPUNIT_ASSERT(mOsgLog.find("link inconsistent") == std::string::npos);
#endif
}

#ifdef NO_CPPUNIT

void main(int argc, char* argv[])
{
  OSG::osgInit(argc, argv);

  vizAspectsLinkConsistencyTest test;

  test.setUp();
  test.testSyncChangeList();
  test.tearDown();
}

#endif
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Opensg-users mailing list
Opensg-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensg-users

Reply via email to