Example #1
0
  /** @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);
  }
Example #2
0
  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);
    }
  }