Changeset 197474 in webkit


Ignore:
Timestamp:
Mar 2, 2016 4:23:47 PM (8 years ago)
Author:
beidson@apple.com
Message:

Modern IDB: Close UniqueIDBDatabases once they become unused.
https://bugs.webkit.org/show_bug.cgi?id=154922

Reviewed by Alex Christensen.

Source/WebCore:

Tests: storage/indexeddb/modern/256-open-databases.html

storage/indexeddb/modern/exceed-open-file-limit.html

Without this change, attempts to open a 256th database in the DatabaseProcess will fail on Mac.

Due to SQLite journal files, this limit could come up as early as 128 databases if they are all
in active use.

This is because launchd - by default - limits xpc services to having 256 open file handles by default.

While we should explore raising the limit, we should also close databases we no longer need.

  • Modules/indexeddb/server/IDBBackingStore.h:
  • Modules/indexeddb/server/IDBServer.cpp:

(WebCore::IDBServer::IDBServer::closeUniqueIDBDatabase):
(WebCore::IDBServer::IDBServer::deleteUniqueIDBDatabase): Deleted.

  • Modules/indexeddb/server/IDBServer.h:
  • Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp:

(WebCore::IDBServer::MemoryBackingStoreTransaction::MemoryBackingStoreTransaction):

  • Modules/indexeddb/server/MemoryIDBBackingStore.cpp:

(WebCore::IDBServer::MemoryIDBBackingStore::getOrEstablishDatabaseInfo):

  • Modules/indexeddb/server/MemoryIDBBackingStore.h:
  • Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:

(WebCore::IDBServer::SQLiteIDBBackingStore::getOrEstablishDatabaseInfo):

  • Modules/indexeddb/server/SQLiteIDBBackingStore.h:
  • Modules/indexeddb/server/UniqueIDBDatabase.cpp:

(WebCore::IDBServer::UniqueIDBDatabase::UniqueIDBDatabase):
(WebCore::IDBServer::UniqueIDBDatabase::~UniqueIDBDatabase):
(WebCore::IDBServer::UniqueIDBDatabase::performCurrentOpenOperation): Handle the case where opening

the backing store failed by firing an error event instead of pretending everything is okay.

(WebCore::IDBServer::UniqueIDBDatabase::deleteBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::didDeleteBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::openBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::didOpenBackingStore):
(WebCore::IDBServer::UniqueIDBDatabase::isCurrentlyInUse):
(WebCore::IDBServer::UniqueIDBDatabase::operationAndTransactionTimerFired): If the database is not

currently in use, close it.

(WebCore::IDBServer::UniqueIDBDatabase::inProgressTransactionCompleted):

  • Modules/indexeddb/server/UniqueIDBDatabase.h:

(WebCore::IDBServer::UniqueIDBDatabase::deletePending): Deleted.

  • Modules/indexeddb/shared/IDBObjectStoreInfo.cpp:

(WebCore::IDBObjectStoreInfo::isolatedCopy): Actually get this right.

LayoutTests:

  • platform/mac-wk1/TestExpectations:
  • storage/indexeddb/modern/256-open-databases-expected.txt: Added.
  • storage/indexeddb/modern/256-open-databases.html: Added.
  • storage/indexeddb/modern/exceed-open-file-limit-expected.txt: Added.
  • storage/indexeddb/modern/exceed-open-file-limit.html: Added.
  • storage/indexeddb/modern/resources/256-open-databases.js: Added.
  • storage/indexeddb/modern/resources/exceed-open-file-limit.js: Added.
Location:
trunk
Files:
6 added
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r197471 r197474  
     12016-03-02  Brady Eidson  <beidson@apple.com>
     2
     3        Modern IDB: Close UniqueIDBDatabases once they become unused.
     4        https://bugs.webkit.org/show_bug.cgi?id=154922
     5
     6        Reviewed by Alex Christensen.
     7
     8        * platform/mac-wk1/TestExpectations:
     9        * storage/indexeddb/modern/256-open-databases-expected.txt: Added.
     10        * storage/indexeddb/modern/256-open-databases.html: Added.
     11        * storage/indexeddb/modern/exceed-open-file-limit-expected.txt: Added.
     12        * storage/indexeddb/modern/exceed-open-file-limit.html: Added.
     13        * storage/indexeddb/modern/resources/256-open-databases.js: Added.
     14        * storage/indexeddb/modern/resources/exceed-open-file-limit.js: Added.
     15
    1162016-03-02  Ryan Haddad  <ryanhaddad@apple.com>
    217
  • trunk/LayoutTests/platform/mac-wk1/TestExpectations

    r197378 r197474  
    147147webkit.org/b/152178 [ Yosemite+ ] fast/replaced/replaced-breaking.html [ Failure ]
    148148
     149# DRT can open way more files than the DatabaseProcess with WebKitTestRunner, and the number is reasonable.
     150# So we shouldn't bother with this test in WebKit1.
     151storage/indexeddb/modern/exceed-open-file-limit.html
     152
    149153### END OF (2) Failures without bug reports
    150154########################################
  • trunk/Source/WebCore/ChangeLog

    r197472 r197474  
     12016-03-02  Brady Eidson  <beidson@apple.com>
     2
     3        Modern IDB: Close UniqueIDBDatabases once they become unused.
     4        https://bugs.webkit.org/show_bug.cgi?id=154922
     5
     6        Reviewed by Alex Christensen.
     7
     8        Tests: storage/indexeddb/modern/256-open-databases.html
     9               storage/indexeddb/modern/exceed-open-file-limit.html
     10
     11        Without this change, attempts to open a 256th database in the DatabaseProcess will fail on Mac.
     12       
     13        Due to SQLite journal files, this limit could come up as early as 128 databases if they are all
     14        in active use.
     15       
     16        This is because launchd - by default - limits xpc services to having 256 open file handles by default.
     17       
     18        While we should explore raising the limit, we should also close databases we no longer need.
     19       
     20        * Modules/indexeddb/server/IDBBackingStore.h:
     21
     22        * Modules/indexeddb/server/IDBServer.cpp:
     23        (WebCore::IDBServer::IDBServer::closeUniqueIDBDatabase):
     24        (WebCore::IDBServer::IDBServer::deleteUniqueIDBDatabase): Deleted.
     25        * Modules/indexeddb/server/IDBServer.h:
     26
     27        * Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp:
     28        (WebCore::IDBServer::MemoryBackingStoreTransaction::MemoryBackingStoreTransaction):
     29
     30        * Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
     31        (WebCore::IDBServer::MemoryIDBBackingStore::getOrEstablishDatabaseInfo):
     32        * Modules/indexeddb/server/MemoryIDBBackingStore.h:
     33
     34        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
     35        (WebCore::IDBServer::SQLiteIDBBackingStore::getOrEstablishDatabaseInfo):
     36        * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
     37
     38        * Modules/indexeddb/server/UniqueIDBDatabase.cpp:
     39        (WebCore::IDBServer::UniqueIDBDatabase::UniqueIDBDatabase):
     40        (WebCore::IDBServer::UniqueIDBDatabase::~UniqueIDBDatabase):
     41        (WebCore::IDBServer::UniqueIDBDatabase::performCurrentOpenOperation): Handle the case where opening
     42          the backing store failed by firing an error event instead of pretending everything is okay.
     43        (WebCore::IDBServer::UniqueIDBDatabase::deleteBackingStore):
     44        (WebCore::IDBServer::UniqueIDBDatabase::didDeleteBackingStore):
     45        (WebCore::IDBServer::UniqueIDBDatabase::openBackingStore):
     46        (WebCore::IDBServer::UniqueIDBDatabase::didOpenBackingStore):
     47        (WebCore::IDBServer::UniqueIDBDatabase::isCurrentlyInUse):
     48        (WebCore::IDBServer::UniqueIDBDatabase::operationAndTransactionTimerFired): If the database is not
     49          currently in use, close it.
     50        (WebCore::IDBServer::UniqueIDBDatabase::inProgressTransactionCompleted):
     51        * Modules/indexeddb/server/UniqueIDBDatabase.h:
     52        (WebCore::IDBServer::UniqueIDBDatabase::deletePending): Deleted.
     53
     54        * Modules/indexeddb/shared/IDBObjectStoreInfo.cpp:
     55        (WebCore::IDBObjectStoreInfo::isolatedCopy): Actually get this right.
     56
    1572016-03-02  Gavin Barraclough  <barraclough@apple.com>
    258
  • trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h

    r196191 r197474  
    5555    virtual ~IDBBackingStore() { }
    5656
    57     virtual const IDBDatabaseInfo& getOrEstablishDatabaseInfo() = 0;
     57    virtual IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) = 0;
    5858
    5959    virtual IDBError beginTransaction(const IDBTransactionInfo&) = 0;
     
    8080    virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) = 0;
    8181    virtual void deleteBackingStore() = 0;
     82
    8283    virtual bool supportsSimultaneousTransactions() = 0;
     84    virtual bool isEphemeral() = 0;
    8385};
    8486
  • trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp

    r197231 r197474  
    158158}
    159159
    160 void IDBServer::deleteUniqueIDBDatabase(UniqueIDBDatabase& database)
    161 {
    162     LOG(IndexedDB, "IDBServer::deleteUniqueIDBDatabase");
     160void IDBServer::closeUniqueIDBDatabase(UniqueIDBDatabase& database)
     161{
     162    LOG(IndexedDB, "IDBServer::closeUniqueIDBDatabase");
    163163
    164164    auto deletedDatabase = m_uniqueIDBDatabaseMap.take(database.identifier());
  • trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.h

    r196779 r197474  
    8888    void unregisterTransaction(UniqueIDBDatabaseTransaction&);
    8989
    90     void deleteUniqueIDBDatabase(UniqueIDBDatabase&);
     90    void closeUniqueIDBDatabase(UniqueIDBDatabase&);
    9191
    9292    std::unique_ptr<IDBBackingStore> createBackingStore(const IDBDatabaseIdentifier&);
  • trunk/Source/WebCore/Modules/indexeddb/server/MemoryBackingStoreTransaction.cpp

    r194859 r197474  
    4848    , m_info(info)
    4949{
    50     if (m_info.mode() == IndexedDB::TransactionMode::VersionChange)
    51         m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(m_backingStore.getOrEstablishDatabaseInfo());
     50    if (m_info.mode() == IndexedDB::TransactionMode::VersionChange) {
     51        IDBDatabaseInfo info;
     52        auto error = m_backingStore.getOrEstablishDatabaseInfo(info);
     53        if (error.isNull())
     54            m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(info);
     55    }
    5256}
    5357
  • trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp

    r196191 r197474  
    5656}
    5757
    58 const IDBDatabaseInfo& MemoryIDBBackingStore::getOrEstablishDatabaseInfo()
     58IDBError MemoryIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info)
    5959{
    6060    if (!m_databaseInfo)
    6161        m_databaseInfo = std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), 0);
    6262
    63     return *m_databaseInfo;
     63    info = *m_databaseInfo;
     64    return { };
    6465}
    6566
  • trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h

    r196191 r197474  
    4747    virtual ~MemoryIDBBackingStore() override final;
    4848
    49     virtual const IDBDatabaseInfo& getOrEstablishDatabaseInfo() override final;
     49    virtual IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) override final;
    5050    void setDatabaseInfo(const IDBDatabaseInfo&);
    5151
     
    7272    virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) override final;
    7373    virtual void deleteBackingStore() override final;
     74
    7475    virtual bool supportsSimultaneousTransactions() override final { return true; }
     76    virtual bool isEphemeral() override final { return true; }
    7577
    7678    void removeObjectStoreForVersionChangeAbort(MemoryObjectStore&);
  • trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp

    r197231 r197474  
    558558}
    559559
    560 const IDBDatabaseInfo& SQLiteIDBBackingStore::getOrEstablishDatabaseInfo()
     560IDBError SQLiteIDBBackingStore::getOrEstablishDatabaseInfo(IDBDatabaseInfo& info)
    561561{
    562562    LOG(IndexedDB, "SQLiteIDBBackingStore::getOrEstablishDatabaseInfo - database %s", m_identifier.databaseName().utf8().data());
    563563
    564     if (m_databaseInfo)
    565         return *m_databaseInfo;
    566 
    567     m_databaseInfo = std::make_unique<IDBDatabaseInfo>(m_identifier.databaseName(), 0);
     564    if (m_databaseInfo) {
     565        info = *m_databaseInfo;
     566        return { };
     567    }
    568568
    569569    makeAllDirectories(fullDatabaseDirectory());
     
    577577
    578578    if (!m_sqliteDB)
    579         return *m_databaseInfo;
     579        return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to open database file on disk") };
    580580
    581581    m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) {
     
    586586        LOG_ERROR("Error creating or migrating Records table in database");
    587587        m_sqliteDB = nullptr;
    588         return *m_databaseInfo;
     588        return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Records table in database") };
    589589    }
    590590
     
    592592        LOG_ERROR("Error creating or migrating Index Records table in database");
    593593        m_sqliteDB = nullptr;
    594         return *m_databaseInfo;
     594        return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records table in database") };
    595595    }
    596596
     
    599599        databaseInfo = createAndPopulateInitialDatabaseInfo();
    600600
    601     if (!databaseInfo)
     601    if (!databaseInfo) {
    602602        LOG_ERROR("Unable to establish IDB database at path '%s'", dbFilename.utf8().data());
    603     else
    604         m_databaseInfo = WTFMove(databaseInfo);
    605 
    606     return *m_databaseInfo;
     603        m_sqliteDB = nullptr;
     604        return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to establish IDB database file") };
     605    }
     606
     607    m_databaseInfo = WTFMove(databaseInfo);
     608    info = *m_databaseInfo;
     609    return { };
    607610}
    608611
  • trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h

    r196191 r197474  
    5252    virtual ~SQLiteIDBBackingStore() override final;
    5353
    54     virtual const IDBDatabaseInfo& getOrEstablishDatabaseInfo() override final;
     54    virtual IDBError getOrEstablishDatabaseInfo(IDBDatabaseInfo&) override final;
    5555
    5656    virtual IDBError beginTransaction(const IDBTransactionInfo&) override final;
     
    7676    virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) override final;
    7777    virtual void deleteBackingStore() override final;
     78
    7879    virtual bool supportsSimultaneousTransactions() override final { return false; }
     80    virtual bool isEphemeral() override final { return false; }
    7981
    8082    void unregisterCursor(SQLiteIDBCursor&);
  • trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp

    r197231 r197474  
    5151    , m_operationAndTransactionTimer(*this, &UniqueIDBDatabase::operationAndTransactionTimerFired)
    5252{
     53    LOG(IndexedDB, "UniqueIDBDatabase::UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data());
    5354}
    5455
    5556UniqueIDBDatabase::~UniqueIDBDatabase()
    5657{
    57     LOG(IndexedDB, "UniqueIDBDatabase::~UniqueIDBDatabase() (%p)", this);
     58    LOG(IndexedDB, "UniqueIDBDatabase::~UniqueIDBDatabase() (%p) %s", this, m_identifier.debugString().utf8().data());
    5859    ASSERT(!hasAnyPendingCallbacks());
    5960    ASSERT(m_inProgressTransactions.isEmpty());
     
    134135    if (requestedVersion < m_databaseInfo->version()) {
    135136        auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError(IDBDatabaseException::VersionError));
     137        m_currentOpenDBRequest->connection().didOpenDatabase(result);
     138        m_currentOpenDBRequest = nullptr;
     139
     140        return;
     141    }
     142
     143    if (!m_backingStoreOpenError.isNull()) {
     144        auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), m_backingStoreOpenError);
    136145        m_currentOpenDBRequest->connection().didOpenDatabase(result);
    137146        m_currentOpenDBRequest = nullptr;
     
    215224        m_backingStore = nullptr;
    216225        m_backingStoreSupportsSimultaneousTransactions = false;
     226        m_backingStoreIsEphemeral = false;
    217227    } else {
    218228        auto backingStore = m_server.createBackingStore(identifier);
    219         auto databaseInfo = backingStore->getOrEstablishDatabaseInfo();
     229
     230        IDBDatabaseInfo databaseInfo;
     231        auto error = backingStore->getOrEstablishDatabaseInfo(databaseInfo);
     232        if (!error.isNull())
     233            LOG_ERROR("Error getting database info from database %s that we are trying to delete", identifier.debugString().utf8().data());
     234
    220235        deletedVersion = databaseInfo.version();
    221236        backingStore->deleteBackingStore();
     
    249264    m_currentOpenDBRequest = nullptr;
    250265
    251     m_deletePending = false;
    252266    m_deleteBackingStoreInProgress = false;
    253267
    254268    if (m_closePendingDatabaseConnections.isEmpty()) {
    255269        if (m_pendingOpenDBRequests.isEmpty())
    256             m_server.deleteUniqueIDBDatabase(*this);
     270            m_server.closeUniqueIDBDatabase(*this);
    257271        else
    258272            invokeOperationAndTransactionTimer();
     
    465479    m_backingStore = m_server.createBackingStore(identifier);
    466480    m_backingStoreSupportsSimultaneousTransactions = m_backingStore->supportsSimultaneousTransactions();
    467     auto databaseInfo = m_backingStore->getOrEstablishDatabaseInfo();
    468 
    469     m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo));
    470 }
    471 
    472 void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info)
     481    m_backingStoreIsEphemeral = m_backingStore->isEphemeral();
     482
     483    IDBDatabaseInfo databaseInfo;
     484    auto error = m_backingStore->getOrEstablishDatabaseInfo(databaseInfo);
     485
     486    m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didOpenBackingStore, databaseInfo, error));
     487}
     488
     489void UniqueIDBDatabase::didOpenBackingStore(const IDBDatabaseInfo& info, const IDBError& error)
    473490{
    474491    ASSERT(isMainThread());
     
    476493   
    477494    m_databaseInfo = std::make_unique<IDBDatabaseInfo>(info);
     495    m_backingStoreOpenError = error;
    478496
    479497    ASSERT(m_isOpeningBackingStore);
     
    10981116}
    10991117
     1118bool UniqueIDBDatabase::isCurrentlyInUse() const
     1119{
     1120    return !m_openDatabaseConnections.isEmpty() || !m_closePendingDatabaseConnections.isEmpty() || !m_pendingOpenDBRequests.isEmpty() || m_currentOpenDBRequest || m_versionChangeDatabaseConnection || m_versionChangeTransaction || m_isOpeningBackingStore || m_deleteBackingStoreInProgress;
     1121}
     1122
    11001123void UniqueIDBDatabase::invokeOperationAndTransactionTimer()
    11011124{
     
    11101133
    11111134    RefPtr<UniqueIDBDatabase> protector(this);
     1135
     1136    // This UniqueIDBDatabase might be no longer in use by any web page.
     1137    // Assuming it is not ephemeral, the server should now close it to free up resources.
     1138    if (!m_backingStoreIsEphemeral && !isCurrentlyInUse()) {
     1139        ASSERT(m_pendingTransactions.isEmpty());
     1140        ASSERT(m_inProgressTransactions.isEmpty());
     1141        m_server.closeUniqueIDBDatabase(*this);
     1142        return;
     1143    }
    11121144
    11131145    // The current operation might require multiple attempts to handle, so try to
     
    12601292    // If this transaction completing was the last of those operations, we can finally delete this UniqueIDBDatabase.
    12611293    if (m_closePendingDatabaseConnections.isEmpty() && m_pendingOpenDBRequests.isEmpty() && !m_databaseInfo) {
    1262         m_server.deleteUniqueIDBDatabase(*this);
     1294        m_server.closeUniqueIDBDatabase(*this);
    12631295        return;
    12641296    }
  • trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h

    r197231 r197474  
    102102
    103103    void handleDelete(IDBConnectionToClient&, const IDBRequestData&);
    104     bool deletePending() const { return m_deletePending; }
    105104
    106105    static JSC::VM& databaseThreadVM();
     
    147146    // Main thread callbacks
    148147    void didDeleteBackingStore(uint64_t deletedVersion);
    149     void didOpenBackingStore(const IDBDatabaseInfo&);
     148    void didOpenBackingStore(const IDBDatabaseInfo&, const IDBError&);
    150149    void didPerformCreateObjectStore(uint64_t callbackIdentifier, const IDBError&, const IDBObjectStoreInfo&);
    151150    void didPerformDeleteObjectStore(uint64_t callbackIdentifier, const IDBError&, uint64_t objectStoreIdentifier);
     
    174173
    175174    bool hasAnyPendingCallbacks() const;
     175    bool isCurrentlyInUse() const;
    176176
    177177    void invokeOperationAndTransactionTimer();
     
    192192
    193193    bool m_isOpeningBackingStore { false };
     194    IDBError m_backingStoreOpenError;
    194195    std::unique_ptr<IDBBackingStore> m_backingStore;
    195196    std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
     
    197198
    198199    bool m_backingStoreSupportsSimultaneousTransactions { false };
     200    bool m_backingStoreIsEphemeral { false };
    199201
    200202    HashMap<uint64_t, ErrorCallback> m_errorCallbacks;
     
    213215    HashSet<uint64_t> m_objectStoreWriteTransactions;
    214216
    215     bool m_deletePending { false };
    216217    bool m_deleteBackingStoreInProgress { false };
    217218};
  • trunk/Source/WebCore/Modules/indexeddb/shared/IDBObjectStoreInfo.cpp

    r195527 r197474  
    8989    IDBObjectStoreInfo result = { m_identifier, m_name.isolatedCopy(), m_keyPath.isolatedCopy(), m_autoIncrement };
    9090
    91     for (auto& iterator : m_indexMap)
     91    for (auto& iterator : m_indexMap) {
    9292        result.m_indexMap.set(iterator.key, iterator.value.isolatedCopy());
     93        if (iterator.key > result.m_maxIndexID)
     94            result.m_maxIndexID = iterator.key;
     95    }
     96
     97    ASSERT(result.m_maxIndexID == m_maxIndexID);
    9398
    9499    return result;
Note: See TracChangeset for help on using the changeset viewer.