Пример #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);
  }
Пример #2
0
 @Override
 public void readExtraCommitData(byte[] extra) throws IOException {
   try {
     mHeaderLatch.acquireShared();
     try {
       readPartial(mCommitNumber & 1, I_EXTRA_DATA, extra, 0, extra.length);
     } finally {
       mHeaderLatch.releaseShared();
     }
   } catch (Throwable e) {
     throw closeOnFailure(e);
   }
 }
Пример #3
0
 /** @see _SnapshotPageArray#beginSnapshot */
 Snapshot beginSnapshot(_LocalDatabase db) throws IOException {
   mHeaderLatch.acquireShared();
   try {
     long pageCount, redoPos;
     long header = p_alloc(MINIMUM_PAGE_SIZE);
     try {
       mPageArray.readPage(mCommitNumber & 1, header, 0, MINIMUM_PAGE_SIZE);
       pageCount = _PageManager.readTotalPageCount(header, I_MANAGER_HEADER);
       redoPos = _LocalDatabase.readRedoPosition(header, I_EXTRA_DATA);
     } finally {
       p_delete(header);
     }
     return mPageArray.beginSnapshot(db, pageCount, redoPos);
   } finally {
     mHeaderLatch.releaseShared();
   }
 }
Пример #4
0
  @Override
  public void commit(boolean resume, long header, final CommitCallback callback)
      throws IOException {
    // Acquire a shared lock to prevent concurrent commits after callback has released
    // exclusive lock.
    CommitLock.Shared shared = mCommitLock.acquireShared();

    try {
      mHeaderLatch.acquireShared();
      final int commitNumber = mCommitNumber + 1;
      mHeaderLatch.releaseShared();

      try {
        if (!resume) {
          mPageManager.commitStart(header, I_MANAGER_HEADER);
        }
        if (callback != null) {
          // Invoke the callback to ensure all dirty pages get written.
          callback.prepare(resume, header);
        }
      } catch (DatabaseException e) {
        if (e.isRecoverable()) {
          throw e;
        } else {
          throw closeOnFailure(e);
        }
      }

      try {
        commitHeader(header, commitNumber);
        mPageManager.commitEnd(header, I_MANAGER_HEADER);
      } catch (Throwable e) {
        throw closeOnFailure(e);
      }
    } finally {
      shared.release();
    }
  }
Пример #5
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);
    }
  }