/**
   * Sets the block id, data and auxData for the block at (x, y, z), if the current data matches the
   * expected data.<br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param expectId the expected block id
   * @param expectData the expected block data
   * @param expectAuxData the expected block auxiliary data
   * @param newId the new block id
   * @param newData the new block data
   * @param newAuxData the new block auxiliary data
   * @return true if the block was set
   */
  @Override
  public boolean compareAndSetBlock(
      int x, int y, int z, short expectId, short expectData, short newId, short newData) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        short oldBlockId = blockIds.get(index);
        boolean oldReserved = auxStore.isReserved(oldBlockId);

        if (!oldReserved) {
          if (blockIds.get(index) != expectId || expectData != 0) {
            return false;
          }
        } else {
          int seq = auxStore.getSequence(oldBlockId);
          short oldId = auxStore.getId(oldBlockId);
          short oldData = auxStore.getData(oldBlockId);
          if (!testSequence(x, y, z, seq)) {
            continue;
          }
          if (oldId != expectId || oldData != expectData) {
            return false;
          }
        }

        if (newData == 0 && !auxStore.isReserved(newId)) {
          if (!blockIds.compareAndSet(index, oldBlockId, newId)) {
            continue;
          }
        } else {
          int newIndex = auxStore.add(newId, newData);
          if (!blockIds.compareAndSet(index, oldBlockId, (short) newIndex)) {
            auxStore.remove(newIndex);
            continue;
          }
        }

        if (oldReserved) {
          auxStore.remove(oldBlockId);
        }

        markDirty(x, y, z);
        return true;
      }
    } finally {
      atomicNotify(index);
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
  /**
   * Sets the block id, data and auxData for the block at (x, y, z).<br>
   * <br>
   * If the data is 0 and the auxData is null, then the block will be stored as a single short.<br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param id the block id
   * @param data the block data
   * @param auxData the block auxiliary data
   */
  public final void setBlock(int x, int y, int z, short id, short data, T auxData) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait();
        }
        checkCompressing();

        short oldBlockId = blockIds.get(index);
        boolean oldReserved = auxStore.isReserved(oldBlockId);
        if (data == 0 && auxData == null && !auxStore.isReserved(id)) {
          if (!blockIds.compareAndSet(index, oldBlockId, id)) {
            continue;
          }
          if (oldReserved) {
            if (!auxStore.remove(oldBlockId)) {
              throw new IllegalStateException(
                  "setBlock() tried to remove old record, but it had already been removed");
            }
          }
          return;
        } else {
          int newIndex = auxStore.add(id, data, auxData);
          if (!blockIds.compareAndSet(index, oldBlockId, (short) newIndex)) {
            if (auxStore.remove(newIndex)) {
              throw new IllegalStateException(
                  "setBlock() tried to remove old record, but it had already been removed");
            }
            continue;
          }
          if (oldReserved) {
            if (!auxStore.remove(oldBlockId)) {
              throw new IllegalStateException(
                  "setBlock() tried to remove old record, but it had already been removed");
            }
          }
          return;
        }
      }
    } finally {
      markDirty(x, y, z);
      atomicNotify();
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
 /**
  * Copies the block data in the store into an array.<br>
  * <br>
  * If the store is updated while this snapshot is being taken, data tearing could occur.<br>
  * <br>
  * If the array is the wrong length or null, a new array is created.
  *
  * @param the array to place the data
  * @return the array
  */
 public short[] getDataArray(short[] array) {
   int length = blockIds.length();
   if (array == null || array.length != length) {
     array = new short[length];
   }
   for (int i = 0; i < length; i++) {
     short blockId = blockIds.get(i);
     if (auxStore.isReserved(blockId)) {
       array[i] = auxStore.getData(blockId);
     } else {
       array[i] = 0;
     }
   }
   return array;
 }
  /**
   * Atomically gets the full set of data associated with the block.<br>
   * <br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @return the full state of the block
   */
  @Override
  public int getFullData(int x, int y, int z) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        int seq = getSequence(x, y, z);
        short blockId = blockIds.get(index);
        if (auxStore.isReserved(blockId)) {
          int state = auxStore.getInt(blockId);
          if (testSequence(x, y, z, seq)) {
            return state;
          }
        } else {
          int state = BlockFullState.getPacked(blockId, (short) 0);
          return state;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
  /**
   * Gets the block id for a block at a particular location.<br>
   * <br>
   * Block ids range from 0 to 65535.
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @return the block id
   */
  @Override
  public int getBlockId(int x, int y, int z) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        int seq = getSequence(x, y, z);
        short blockId = blockIds.get(index);
        if (auxStore.isReserved(blockId)) {
          blockId = auxStore.getId(blockId);
          if (testSequence(x, y, z, seq)) {
            return blockId & 0x0000FFFF;
          }
        } else {
          return blockId & 0x0000FFFF;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
  /**
   * Gets the sequence number associated with a block location.<br>
   * <br>
   * If soft is true, this method counts as a volatile read. Otherwise, it is both a volatile read
   * and a volatile write.<br>
   * <br>
   * Soft reads should only be used for the first of the 2 step process for confirming that data
   * hasn't changed.
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @return the sequence number, or DatatableSequenceNumber.ATOMIC for a single short record
   */
  @Override
  public int getSequence(int x, int y, int z) {
    checkCompressing();
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        int blockId = blockIds.get(index);
        if (!auxStore.isReserved(blockId)) {
          return AtomicSequenceNumber.ATOMIC;
        }

        int sequence = auxStore.getSequence(blockId);
        if (sequence != AtomicSequenceNumber.UNSTABLE) {
          return sequence;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
 /**
  * Copies the block ids in the store into an array.<br>
  * <br>
  * If the store is updated while this snapshot is being taken, data tearing could occur.<br>
  * <br>
  * If the array is the wrong length or null, a new array is created.
  *
  * @param the array to place the data
  * @return the array
  */
 public short[] getBlockIdArray(short[] array) {
   int length = blockIds.length();
   if (array == null || array.length != length) {
     array = new short[length];
   }
   for (int i = 0; i < length; i++) {
     short blockId = blockIds.get(i);
     if (auxStore.isReserved(blockId)) {
       blockId = auxStore.getId(blockId);
     } else {
       blockId &= 0x0000FFFF;
     }
     array[i] = blockId;
   }
   return array;
 }
 /**
  * Compresses the auxiliary store.<br>
  * <br>
  * This method should only be called when the store is guaranteed not to be accessed from any
  * other thread.<br>
  */
 @Override
 public void compress() {
   if (!compressing.compareAndSet(false, true)) {
     throw new IllegalStateException("Compression started while compression was in progress");
   }
   int length = side * side * side;
   AtomicIntArrayStore newAuxStore = new AtomicIntArrayStore(length);
   for (int i = 0; i < length; i++) {
     short blockId = blockIds.get(i);
     if (auxStore.isReserved(blockId)) {
       short storedId = auxStore.getId(blockId);
       short storedData = auxStore.getData(blockId);
       int newIndex = newAuxStore.add(storedId, storedData);
       if (!blockIds.compareAndSet(i, blockId, (short) newIndex)) {
         throw new IllegalStateException("Unstable block id data during compression step");
       }
     }
   }
   auxStore = newAuxStore;
   compressing.set(false);
 }
  private int getAndSetBlockRaw(int x, int y, int z, short id, short data) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait(index);
        }
        checkCompressing();

        short oldBlockId = blockIds.get(index);
        boolean oldReserved = auxStore.isReserved(oldBlockId);
        if (data == 0 && !auxStore.isReserved(id)) {
          if (!blockIds.compareAndSet(index, oldBlockId, id)) {
            continue;
          }
        } else {
          int newIndex = auxStore.add(id, data);
          if (!blockIds.compareAndSet(index, oldBlockId, (short) newIndex)) {
            auxStore.remove(newIndex);
            continue;
          }
        }

        if (oldReserved) {
          return auxStore.remove(oldBlockId);
        }

        return BlockFullState.getPacked(oldBlockId, (short) 0);
      }
    } finally {
      markDirty(x, y, z);
      atomicNotify(index);
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
示例#10
0
 public AtomicBlockStore(int shift, int dirtySize, short[] initial) {
   this.side = 1 << shift;
   this.shift = shift;
   this.doubleShift = shift << 1;
   int size = side * side * side;
   blockIds = new AtomicShortArray(size);
   auxStore = new AtomicIntReferenceArrayStore<T>(size);
   dirtyX = new byte[dirtySize];
   dirtyY = new byte[dirtySize];
   dirtyZ = new byte[dirtySize];
   if (initial != null) {
     for (int i = 0; i < Math.min(initial.length, size); i++) {
       blockIds.set(i, initial[i]);
     }
   }
 }
 /**
  * Waits until a notify
  *
  * @return true if interrupted during the wait
  */
 private final boolean atomicWait(int index) {
   AtomicInteger i = auxStore.getWaiting(index);
   i.incrementAndGet();
   try {
     short blockId = blockIds.get(index);
     boolean reserved = auxStore.isReserved(blockId);
     synchronized (i) {
       if (!reserved || !auxStore.testUnstable(blockId)) {
         return false;
       }
       try {
         i.wait();
       } catch (InterruptedException e) {
         return true;
       }
     }
   } finally {
     i.decrementAndGet();
   }
   return false;
 }
  /**
   * Tests if a the sequence number associated with a particular block location has not changed.
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param expected the expected sequence number
   * @return true if the sequence number has not changed and expected is not
   *     DatatableSequenceNumber.ATOMIC
   */
  @Override
  public boolean testSequence(int x, int y, int z, int expected) {

    if (expected == AtomicSequenceNumber.ATOMIC) {
      return false;
    }

    checkCompressing();
    int index = getIndex(x, y, z);
    boolean interrupted = false;
    try {
      checkCompressing();

      int blockId = blockIds.get(index);
      return auxStore.isReserved(blockId) && auxStore.testSequence(blockId, expected);
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
示例#13
0
  /**
   * Atomically gets the full set of data associated with the block.<br>
   * <br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param fullState a BlockFullState object to store the return value, or null to generate a new
   *     one
   * @return the full state of the block
   */
  public final BlockFullState<T> getFullData(int x, int y, int z, BlockFullState<T> fullData) {
    if (fullData == null) {
      fullData = new BlockFullState<T>();
    }
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait();
        }
        checkCompressing();

        int seq = getSequence(x, y, z);
        short blockId = blockIds.get(index);
        if (auxStore.isReserved(blockId)) {
          fullData.setId(auxStore.getId(blockId));
          fullData.setData(auxStore.getData(blockId));
          fullData.setAuxData(auxStore.getAuxData(blockId));
          if (testSequence(x, y, z, seq)) {
            return fullData;
          }
        } else {
          fullData.setId(blockId);
          fullData.setData((short) 0);
          fullData.setAuxData(null);
          return fullData;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
示例#14
0
  /**
   * Tests if a the sequence number associated with a particular block location has not changed.
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param expected the expected sequence number
   * @return true if the sequence number has not changed and expected is not
   *     DatatableSequenceNumber.ATOMIC
   */
  public final boolean testSequence(int x, int y, int z, int expected) {

    if (expected == DatatableSequenceNumber.ATOMIC) {
      return false;
    }

    checkCompressing();
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      if (spins++ > SPINS) {
        interrupted |= atomicWait();
      }
      checkCompressing();

      int blockId = blockIds.get(index);
      return auxStore.isReserved(blockId) && auxStore.testSequence(blockId, expected);
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }
示例#15
0
  /**
   * Sets the block id, data and auxData for the block at (x, y, z), if the current data matches the
   * expected data.<br>
   *
   * @param x the x coordinate
   * @param y the y coordinate
   * @param z the z coordinate
   * @param expectId the expected block id
   * @param expectData the expected block data
   * @param expectAuxData the expected block auxiliary data
   * @param newId the new block id
   * @param newData the new block data
   * @param newAuxData the new block auxiliary data
   * @return true if the block was set
   */
  public final boolean compareAndSetBlock(
      int x,
      int y,
      int z,
      short expectId,
      short expectData,
      T expectAuxData,
      short newId,
      short newData,
      T newAuxData) {
    int index = getIndex(x, y, z);
    int spins = 0;
    boolean interrupted = false;
    try {
      while (true) {
        if (spins++ > SPINS) {
          interrupted |= atomicWait();
        }
        checkCompressing();

        short oldBlockId = blockIds.get(index);
        boolean oldReserved = auxStore.isReserved(oldBlockId);

        if (!oldReserved) {
          if (blockIds.get(index) != expectId || expectData != 0 || expectAuxData != null) {
            return false;
          }
        } else {
          int seq = auxStore.getSequence(oldBlockId);
          short oldId = auxStore.getId(oldBlockId);
          short oldData = auxStore.getData(oldBlockId);
          T oldAuxData = auxStore.getAuxData(oldBlockId);
          if (!testSequence(x, y, z, seq)) {
            continue;
          }
          if (oldId != expectId || oldData != expectData || oldAuxData != expectAuxData) {
            return false;
          }
        }

        if (newData == 0 && newAuxData == null && !auxStore.isReserved(newId)) {
          if (!blockIds.compareAndSet(index, oldBlockId, newId)) {
            continue;
          }
          if (oldReserved) {
            if (!auxStore.remove(oldBlockId)) {
              throw new IllegalStateException(
                  "setBlock() tried to remove old record, but it had already been removed");
            }
          }
          markDirty(x, y, z);
          return true;
        } else {
          int newIndex = auxStore.add(newId, newData, newAuxData);
          if (!blockIds.compareAndSet(index, oldBlockId, (short) newIndex)) {
            if (!auxStore.remove(newIndex)) {
              throw new IllegalStateException(
                  "setBlock() tried to remove old record, but it had already been removed");
            }
            continue;
          }
          if (oldReserved) {
            if (!auxStore.remove(oldBlockId)) {
              throw new IllegalStateException(
                  "setBlock() tried to remove old record, but it had already been removed");
            }
          }
          markDirty(x, y, z);
          return true;
        }
      }
    } finally {
      atomicNotify();
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }