/** @param header array length is full page */ private void commitHeader(final long header, final int commitNumber) throws IOException { final PageArray array = mPageArray; p_longPutLE(header, I_MAGIC_NUMBER, MAGIC_NUMBER); p_intPutLE(header, I_PAGE_SIZE, array.pageSize()); p_intPutLE(header, I_COMMIT_NUMBER, commitNumber); // Durably write the new page store header before returning // from this method, to ensure that the manager doesn't start // returning uncommitted pages. This would prevent rollback // from working because the old pages would get overwritten. setHeaderChecksum(header); // Write multiple header copies in the page, in case special recovery is required. int dupCount = pageSize() / MINIMUM_PAGE_SIZE; for (int i = 1; i < dupCount; i++) { p_copy(header, 0, header, i * MINIMUM_PAGE_SIZE, MINIMUM_PAGE_SIZE); } // Ensure all writes are flushed before flushing the header. There's // otherwise no ordering guarantees. Metadata should also be flushed // first, because the header won't affect it. array.sync(true); mHeaderLatch.acquireExclusive(); try { array.writePage(commitNumber & 1, header); mCommitNumber = commitNumber; } finally { mHeaderLatch.releaseExclusive(); } // Final sync to ensure the header is durable. array.syncPage(commitNumber & 1); }
private _DurablePageDb( final PageArray rawArray, final PageCache cache, final Crypto crypto, final boolean destroy) throws IOException, WrongPageSize { mCrypto = crypto; PageArray array = crypto == null ? rawArray : new CryptoPageArray(rawArray, crypto); mPageArray = new _SnapshotPageArray(array, rawArray, cache); mHeaderLatch = new Latch(); try { int pageSize = mPageArray.pageSize(); checkPageSize(pageSize); if (destroy || mPageArray.isEmpty()) { // Newly created file. mPageManager = new _PageManager(mPageArray); mCommitNumber = -1; // Commit twice to ensure both headers have valid data. long header = p_calloc(pageSize); try { mCommitLock.acquireExclusive(); try { commit(false, header, null); commit(false, header, null); } finally { mCommitLock.releaseExclusive(); } } finally { p_delete(header); } mPageArray.setPageCount(2); } else { // Opened an existing file. // Previous header commit operation might have been interrupted before final // header sync completed. Pages cannot be safely recycled without this. mPageArray.sync(false); long header0 = p_null(); long header1 = p_null(); try { final long header; final int commitNumber; findHeader: { int pageSize0; int commitNumber0, commitNumber1; CorruptDatabaseException ex0; try { header0 = readHeader(0); commitNumber0 = p_intGetLE(header0, I_COMMIT_NUMBER); pageSize0 = p_intGetLE(header0, I_PAGE_SIZE); ex0 = null; } catch (CorruptDatabaseException e) { header0 = p_null(); commitNumber0 = -1; pageSize0 = pageSize; ex0 = e; } if (pageSize0 != pageSize) { throw new WrongPageSize(pageSize, pageSize0); } try { header1 = readHeader(1); commitNumber1 = p_intGetLE(header1, I_COMMIT_NUMBER); } catch (CorruptDatabaseException e) { if (ex0 != null) { // File is completely unusable. throw ex0; } header = header0; commitNumber = commitNumber0; break findHeader; } int pageSize1 = p_intGetLE(header1, I_PAGE_SIZE); if (pageSize0 != pageSize1) { throw new CorruptDatabaseException( "Mismatched page sizes: " + pageSize0 + " != " + pageSize1); } if (header0 == p_null()) { header = header1; commitNumber = commitNumber1; } else { // Modulo comparison. int diff = commitNumber1 - commitNumber0; if (diff > 0) { header = header1; commitNumber = commitNumber1; } else if (diff < 0) { header = header0; commitNumber = commitNumber0; } else { throw new CorruptDatabaseException( "Both headers have same commit number: " + commitNumber0); } } } mHeaderLatch.acquireExclusive(); mCommitNumber = commitNumber; mHeaderLatch.releaseExclusive(); mPageManager = new _PageManager(mPageArray, header, I_MANAGER_HEADER); } finally { p_delete(header0); p_delete(header1); } } } catch (WrongPageSize e) { delete(); closeQuietly(null, this); throw e; } catch (Throwable e) { delete(); throw closeOnFailure(e); } }