gt:                     assert newTotalWriteLockCount > 0 : "write underflow";
context: /*
   *      Copyright (C) 2015  higherfrequencytrading.com
   *
   *      This program is free software: you can redistribute it and/or modify
   *      it under the terms of the GNU Lesser General Public License as published by
   *      the Free Software Foundation, either version 3 of the License.
   *
   *      This program is distributed in the hope that it will be useful,
   *      but WITHOUT ANY WARRANTY; without even the implied warranty of
   *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *      GNU Lesser General Public License for more details.
   *
   *      You should have received a copy of the GNU Lesser General Public License
   *      along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  package net.openhft.chronicle.hash.impl.stage.entry;
  
  import net.openhft.chronicle.hash.Data;
  import net.openhft.chronicle.hash.SegmentLock;
  import net.openhft.chronicle.hash.impl.*;
  import net.openhft.chronicle.hash.impl.stage.hash.Chaining;
  import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation;
  import net.openhft.chronicle.hash.impl.stage.query.KeySearch;
  import net.openhft.chronicle.hash.locks.InterProcessLock;
  import net.openhft.lang.collection.DirectBitSet;
  import net.openhft.lang.collection.SingleThreadedDirectBitSet;
  import net.openhft.lang.io.MultiStoreBytes;
  import net.openhft.sg.Stage;
  import net.openhft.sg.StageRef;
  import net.openhft.sg.Staged;
  import org.jetbrains.annotations.NotNull;
  
  import java.util.Objects;
  
  import static net.openhft.chronicle.hash.impl.LocalLockState.UNLOCKED;
  
  @Staged
  public abstract class SegmentStages implements SegmentLock {
  
      @StageRef Chaining chaining;
      @StageRef public VanillaChronicleHashHolder<?, ?, ?> hh;
      @StageRef public CheckOnEachPublicOperation checkOnEachPublicOperation;
  
      @Stage("TheSegmentIndex") public int segmentIndex = -1;
      
      public void initTheSegmentIndex(int segmentIndex) {
          this.segmentIndex = segmentIndex;
      }
  
      // "SegHeader" because stage-generator doesn't support stage names - one is a prefix of another
      // there is a stage named "Segment"
      @Stage("SegHeader") long segmentHeaderAddress;
      @Stage("SegHeader") SegmentHeader segmentHeader = null;
  
      abstract boolean segHeaderInit();
  
      private void initSegHeader() {
          segmentHeaderAddress = hh.h().ms.address() + hh.h().segmentHeaderOffset(segmentIndex);
          segmentHeader = BigSegmentHeader.INSTANCE;
      }
  
      public long entries() {
          return segmentHeader.size(segmentHeaderAddress);
      }
  
      public void entries(long size) {
          segmentHeader.size(segmentHeaderAddress, size);
      }
  
      long nextPosToSearchFrom() {
          return segmentHeader.nextPosToSearchFrom(segmentHeaderAddress);
      }
  
      public void nextPosToSearchFrom(long nextPosToSearchFrom) {
          segmentHeader.nextPosToSearchFrom(segmentHeaderAddress, nextPosToSearchFrom);
      }
  
      public long deleted() {
          return segmentHeader.deleted(segmentHeaderAddress);
      }
  
      public void deleted(long deleted) {
          segmentHeader.deleted(segmentHeaderAddress, deleted);
      }
  
      public long size() {
          return entries() - deleted();
      }
  
  
      @Stage("Locks") public SegmentStages rootContextOnThisSegment = null;
      /**
       * See the ChMap Ops spec, considerations of nested same-thread concurrent contexts.
       * Once context enters the segment, and observes concurrent same-thread context,
       * it sets concurrentSameThreadContexts = true for itself and that concurrent context.
       * This flag is not dropped on exit of one of these contexts, because between calls of
       * this context, nested one could be initialized, does some changes that break our thread-local
       * assumptions _and exit_, that is why on exit concurrent context should remain "dirty".
       */
      @Stage("Locks") public boolean concurrentSameThreadContexts;
  
      @Stage("Locks") public int latestSameThreadSegmentModCount;
      @Stage("Locks") public int contextModCount;
  
      @Stage("Locks")
      public void incrementModCount() {
          contextModCount = ++rootContextOnThisSegment.latestSameThreadSegmentModCount;
      }
  
      // chain
      @Stage("Locks") SegmentStages nextNode;
  
      @Stage("Locks") LocalLockState localLockState;
      @Stage("Locks") int totalReadLockCount;
      @Stage("Locks") int totalUpdateLockCount;
      @Stage("Locks") int totalWriteLockCount;
  
      @Stage("Locks")
      public boolean readZero() {
          return rootContextOnThisSegment.totalReadLockCount == 0;
      }
  
      @Stage("Locks")
      public boolean updateZero() {
          return rootContextOnThisSegment.totalUpdateLockCount == 0;
      }
  
      @Stage("Locks")
      public boolean writeZero() {
          return rootContextOnThisSegment.totalWriteLockCount == 0;
      }
  
      @Stage("Locks")
      public int decrementRead() {
          return rootContextOnThisSegment.totalReadLockCount -= 1;
      }
  
      @Stage("Locks")
      public int decrementUpdate() {
          return rootContextOnThisSegment.totalUpdateLockCount -= 1;
      }
  
      @Stage("Locks")
      public int decrementWrite() {
          return rootContextOnThisSegment.totalWriteLockCount -= 1;
      }
  
      @Stage("Locks")
      public void incrementRead() {
          rootContextOnThisSegment.totalReadLockCount += 1;
      }
  
      @Stage("Locks")
      public void incrementUpdate() {
          rootContextOnThisSegment.totalUpdateLockCount += 1;
      }
  
      @Stage("Locks")
      public void incrementWrite() {
          rootContextOnThisSegment.totalWriteLockCount += 1;
      }
  
      public abstract boolean locksInit();
  
      void initLocks() {
          localLockState = UNLOCKED;
          int indexOfThisContext = chaining.indexInContextChain;
          for (int i = indexOfThisContext - 1; i >= 0; i--) {
              if (tryFindInitLocksOfThisSegment(this, i))
                  return;
          }
          for (int i = indexOfThisContext + 1, size = chaining.contextChain.size(); i < size; i++) {
              if (tryFindInitLocksOfThisSegment(this, i))
                  return;
          }
          rootContextOnThisSegment = this;
          concurrentSameThreadContexts = false;
  
          latestSameThreadSegmentModCount = 0;
          contextModCount = 0;
  
          totalReadLockCount = 0;
          totalUpdateLockCount = 0;
          totalWriteLockCount = 0;
      }
  
      @Stage("Locks")
      boolean tryFindInitLocksOfThisSegment(Object thisContext, int index) {
          SegmentStages c = chaining.contextAtIndexInChain(index);
          if (c.segHeaderInit() &&
                  c.segmentHeaderAddress == segmentHeaderAddress &&
                  c.locksInit()) {
              SegmentStages root = c.rootContextOnThisSegment;
              this.rootContextOnThisSegment = root;
              root.concurrentSameThreadContexts = true;
              this.concurrentSameThreadContexts = true;
              this.contextModCount = root.latestSameThreadSegmentModCount;
              linkToSegmentContextsChain();
              return true;
          } else {
              return false;
          }
      }
  
      void closeLocks() {
          if (rootContextOnThisSegment == this) {
              closeRootLocks();
          } else {
              closeNestedLocks();
          }
          localLockState = null;
          rootContextOnThisSegment = null;
      }
  
      @Stage("Locks")
      private void closeNestedLocks() {
          unlinkFromSegmentContextsChain();
          readUnlockAndDecrementCount();
      }
  
      @Stage("Locks")
      public void readUnlockAndDecrementCount() {
          switch (localLockState) {
              case UNLOCKED:
                  return;
              case READ_LOCKED:
                  int newTotalReadLockCount = decrementRead();
                  if (newTotalReadLockCount == 0) {
                      if (updateZero() && writeZero())
                          segmentHeader.readUnlock(segmentHeaderAddress);
                  } else {
                      assert newTotalReadLockCount > 0 : "read underflow";
                  }
                  return;
              case UPDATE_LOCKED:
                  int newTotalUpdateLockCount = decrementUpdate();
                  if (newTotalUpdateLockCount == 0) {
                      if (writeZero()) {
                          if (readZero()) {
                              segmentHeader.updateUnlock(segmentHeaderAddress);
                          } else {
                              segmentHeader.downgradeUpdateToReadLock(segmentHeaderAddress);
                          }
                      }
                  } else {
                      assert newTotalUpdateLockCount > 0 : "update underflow";
                  }
                  return;
              case WRITE_LOCKED:
                  int newTotalWriteLockCount = decrementWrite();
                  if (newTotalWriteLockCount == 0) {
                      if (!updateZero()) {
                          segmentHeader.downgradeWriteToUpdateLock(segmentHeaderAddress);
                      } else {
                          if (!readZero()) {
                              segmentHeader.downgradeWriteToReadLock(segmentHeaderAddress);
                          } else {
                              segmentHeader.writeUnlock(segmentHeaderAddress);
                          }
                      }
                  } else {
  