LevelDB утверждает при удалении экземпляра LevelDB

Я получаю несколько раздражающих утверждений, когда пытаюсь удалить экземпляр leveldb, и я не уверен, почему это происходит!

Утверждение происходит в файле version_set.cc:

void VersionSet::AppendVersion(Version* v) {
  // Make "v" current
  assert(v->refs_ == 0); // <---??? how do I avoid this assertion?
  // the rest of the source code is available in the link to version_set.cc
}

Кроме того, он утверждает в другом месте того же файла:

Version::~Version() {
  assert(refs_ == 0); // <-- Again... how do I avoid this one too?
  // the rest of the source code is available in the link to version_set.cc
}

Вот более подробная информация об использовании в моей системе, у меня есть:

  • класс ExtStorage (расширенное хранилище), у которого есть экземпляр LevelDB::DB.
  • class EextStorageDotNet, который является оболочкой C ++ / CLI для ExtStorage.
  • class AltStorage, который содержит указатель на класс ExtStorage (переданный через конструктор):
  • class AltStorageDotNet, который является оболочкой C ++ / CLI для AltStorage.

Альтернативный класс хранилища выглядит так:

class AltStorage{
    ExtStorage* instance;
public:
    AltStorage(ExtStorage* extStorage):instance(extStorage){}

    ~AltStorage(){
        delete instance;
        instance = NULL;
    }
};

Класс ExtStorage выглядит так:

class ExtStorage{
    leveldb::DB* mydb;
public:
    ExtStorage(/*some parameters*/){
         mydb = new leveldb::DB(/*parameters*/);
    }

    // Destructor
    ~ExtStorage() {
        Close();
    }

    // deletes the leveldb::DB instance
    void Close() {
        if(mydb == NULL) {
            delete mydb; // <-- Asserts every time I get here when using with the AltStorageDotNet
            mydb= NULL;

            // Close the L1 and L2 caches
            // only once (
        }
    }
}

Класс AltStorageDotNet выглядит так:

public ref class AltStorageDotNet{
    AltStorage* altInstance;
    ExtStorageDotNet^ extInstance;
public:
    AltStorageDotNet() {
        ExtStorage extStorage = new ExtStorage(/*params*/);
        altInstance = new AltStorage(extStorage);
        extInstance = gcnew ExtStorageDotNet(extStorage);
    }

    ~AltStorageDotNet(){
        delete altInstance;
        altInstance = NULL;
        // no need to delete extInstance since it was created with gcnew 
    }

    !AltStorageDotNet(){
        delete altInstance;
        altInstance = NULL;
        // no need to delete extInstance since it was created with gcnew
    }

    inline ExtStorageDotNet^ GetExtInstance(){return extInstance;}
};

Обертки DotNet выглядят так:

public ref class ExtStorageDotNet{
private:
    ExtStorage* instance;
public:
    ExtStorageDotNet(ExtStorage* extStorage){
        instance = extStorage;
    }

    ~ExtStorageDotNet(){
        delete instance;
        instance = NULL;
    }

    !ExtStorageDotNet(){
        delete instance;
        instance = NULL;
    }

    void Close(){instance->Close();}
};

Всякий раз, когда я использую оболочку ExtStorageDotNet в своем приложении C #, все работает хорошо и нет никаких утверждений. Однако, когда я использую оболочку AltStorageDotNet и обращаюсь к оболочке ExtStorageDotNet, я получаю утверждения при закрытии базы данных. Все это часть набора тестов, в котором я инициализирую экземпляр для каждого тестового примера и закрываю его после каждого тестового примера; связанные файлы базы данных удаляются до начала нового тестового примера. Я не вижу причин, по которым это должно происходить, и утверждение не помогает в отслеживании проблемы.


person Kiril    schedule 17.08.2011    source источник
comment
// no need to delete extInstance since it was created with gcnew этот комментарий очень ошибочен.   -  person ildjarn    schedule 18.08.2011
comment
Не могу удалить extInstance, нет gcdelete ... Я могу установить для него значение null, но это все. gcnew предназначен для справочных объектов .NET; объекты, созданные с помощью gcnew, автоматически собираются сборщиком мусора; важно использовать gcnew с типами CLR (stackoverflow.com/questions / 202459 / what-is-gcnew / 202464 # 202464)   -  person Kiril    schedule 18.08.2011
comment
Нет, gcdelete нет, но delete имеет совершенно другую семантику для управляемых типов и неуправляемых типов. Вы по-прежнему должны использовать его, а в некоторых случаях нужно - это, вероятно, один из таких случаев.   -  person ildjarn    schedule 18.08.2011
comment
А как насчет автоматической сборки мусора?   -  person Kiril    schedule 18.08.2011
comment
Сборка мусора связана с управлением памятью; IDisposable касается детерминированного завершения - для ваших целей это полностью ортогональные концепции.   -  person ildjarn    schedule 18.08.2011


Ответы (2)


Я избавился от вложенных ссылок, но это не решило проблему. Как оказалось, проблема, вызывающая эту проблему, была не совсем в том месте, на которое я смотрел. Проблема возникает, когда пользователь получает итератор в базе данных и не может удалить итератор перед удалением базы данных. Это обсуждалось в группе Google, относящейся к уровню db.

// Caller should delete the iterator when it is no longer needed.
// The returned iterator should be deleted before this db is deleted.
virtual Iterator* NewIterator(const ReadOptions& options) = 0; 

Когда пользователь получает итератор, он должен удалить его перед удалением базы данных, иначе он получит утверждение, упомянутое выше.

person Kiril    schedule 03.10.2011

Я не знаю, связано ли это с вашими утверждениями или нет, но этот код гарантированно вызовет повреждение памяти. И ExtStorageDotNet, и AltStorageDotNet являются одноразовыми и финализируемыми, и оба (прямо или косвенно) delete являются экземпляром ExtStorage - неизменно один будет delete после того, как другой уже будет.

Кроме того, как я уже сказал в своем комментарии, комментарий в вашем коде, говорящий о том, что no need to delete extInstance since it was created with gcnew, не подходит - см. этот ответ для получения подробной информации. (Я предполагаю, что вы знаете C # и, следовательно, о IDisposable; если вы этого не сделаете, вам действительно нужно немного почитать.)

person ildjarn    schedule 17.08.2011
comment
Хорошо, я не понимаю, как я могу получить повреждение памяти, если delete эквивалентен nop на указателе NULL (это то, на что я установил указатель после его удаления). Кроме того, согласно ответу на этот вопрос, объекты, созданные с помощью gcnew, автоматически собирают мусор. . - person Kiril; 18.08.2011
comment
@Lirik: Вы установили ExtStorageDotNet::instance на NULL, но как вы ожидаете, что это повлияет на AltStorage::instance (и наоборот)? Кроме того, забудьте о сборке мусора в этом обсуждении, поскольку это совершенно не имеет отношения к IDisposable. ; -] - person ildjarn; 18.08.2011
comment
понял: не нужно удалять AltStorage::instance. - person Kiril; 18.08.2011
comment
@Lirik: Не совсем. : - / Вам нужно реструктурировать свой код так, чтобы только один объект владел данным неуправляемым объектом, и вам нужно delete управляемые объекты, которыми вы владеете, в ваших деструкторах (но не в ваших финализаторах). Опять же, прочтите, пожалуйста, о IDisposable; есть много статей о его правильной реализации в MSDN. - person ildjarn; 18.08.2011
comment
Хорошо, я не реализую IDisposable, так чем же мне поможет удаление AltStoragDotNet::extInstance? - person Kiril; 18.08.2011
comment
@Lirik: деструктор управляемого типа является реализацией IDisposable; синтаксис деструктора - это просто синтаксический сахар. Точно так же синтаксис !Classname() - это просто синтаксический сахар для переопределения _3 _ . Теперь, возможно, пришло время ознакомиться с некоторыми основами C ++ / CLI. ; -] - person ildjarn; 18.08.2011
comment
А !AltStorageDotNet финализатор? Так что я могу просто избавиться от финализатора, и это решит одну из проблем. - person Kiril; 18.08.2011
comment
@Lirik: Нет ... Когда у вас есть неуправляемая память / объекты, вам всегда нужен финализатор. Это должно быть описано в любой статье о правильной реализации IDisposable. - person ildjarn; 18.08.2011
comment
Так где же мне удалить, в финализаторе или в утилите / дистрибуторе? - person Kiril; 18.08.2011
comment
@Lirik: delete неуправляемые объекты в обоих; delete управляемые объекты только в деструкторе. Опять же, это должно быть описано в любой статье о правильной реализации IDisposable (подсказка, подсказка - прочтите немного). - person ildjarn; 18.08.2011