class TableStoreWriteLock extends AbstractTSQueueLock implements WriteLock {
    public static final String APPEND_LOCK_KEY = "chronicle.append.lock";
    private static final String LOCK_KEY = "chronicle.write.lock";
    private final long timeout;
    private Thread lockedByThread = null;
    private StackTrace lockedHere;

    public TableStoreWriteLock(final TableStore<?> tableStore, Supplier<TimingPauser> pauser, Long timeoutMs, final String lockKey) {
        super(lockKey, tableStore, pauser);
        timeout = timeoutMs;
    }

    public TableStoreWriteLock(final TableStore<?> tableStore, Supplier<TimingPauser> pauser, Long timeoutMs) {
        this(tableStore, pauser, timeoutMs, LOCK_KEY);
    }

    /**
     * Guaranteed to succeed in getting the lock (may involve timeout and recovery) or else throw.
     * <p>This is not re-entrant i.e. if you lock and try and lock again it will timeout and recover
     */
    @Override
    public void lock() {
        throwExceptionIfClosed();

        assert checkNotAlreadyLocked();

        long currentLockValue = 0;
        TimingPauser tlPauser = pauser.get();
        try {
            currentLockValue = lock.getVolatileValue();
            while (!lock.compareAndSwapValue(UNLOCKED, PID)) {
                currentLockValue = lockGetCurrentLockValue(tlPauser);
            }

            lockAssertPostConditions();

            // success
        } catch (TimeoutException e) {
            handleTimeoutEx(currentLockValue);
        } finally {
            tlPauser.reset();
        }
    }

    private long lockGetCurrentLockValue(TimingPauser tlPauser) throws TimeoutException {
        if (Thread.currentThread().isInterrupted())
            throw new InterruptedRuntimeException("Interrupted for the lock file:" + path);
        tlPauser.pause(timeout, TimeUnit.MILLISECONDS);
        return lock.getVolatileValue();
    }

    private void lockAssertPostConditions() {<FILL_FUNCTION_BODY>}

    private void handleTimeoutEx(long currentLockValue) {
        final String lockedBy = getLockedBy(currentLockValue);
        final String warningMsg = lockHandleTimeoutExCreateWarningMessage(lockedBy);
        if (forceUnlockOnTimeoutWhen == UnlockMode.NEVER)
            throw new UnrecoverableTimeoutException(new IllegalStateException(warningMsg + UNLOCK_MAIN_MSG));
        else if (forceUnlockOnTimeoutWhen == UnlockMode.LOCKING_PROCESS_DEAD) {
            if (forceUnlockIfProcessIsDead())
                lock();
            else
                throw new UnrecoverableTimeoutException(new IllegalStateException(warningMsg + UNLOCK_MAIN_MSG));
        } else {
            warn().on(getClass(), warningMsg + UNLOCKING_FORCIBLY_MSG);
            forceUnlock(currentLockValue);
            lock();
        }
    }

    @NotNull
    private String lockHandleTimeoutExCreateWarningMessage(String lockedBy) {
        return "Couldn't acquire write lock " +
                "after " + timeout + " ms " +
                "for the lock file:" + path + ". " +
                "Lock was held by " + lockedBy;
    }

    @NotNull
    protected String getLockedBy(long value) {
        return value == Long.MIN_VALUE ? "unknown" :
                value == PID ? "me"
                        : Long.toString((int) value);
    }

    private boolean checkNotAlreadyLocked() {
        if (!locked())
            return true;
        if (lockedByThread == null)
            return true;
        if (lockedByThread == Thread.currentThread())
            throw new AssertionError("Lock is already acquired by current thread and is not reentrant - nested document context?", lockedHere);
        return true;
    }

    @Override
    public void unlock() {
        throwExceptionIfClosed();
        if (!lock.compareAndSwapValue(PID, UNLOCKED)) {
            long value = lock.getVolatileValue();
            if (value == UNLOCKED)
                warn().on(getClass(), "Write lock was already unlocked. For the " +
                        "lock file:" + path);
            else
                warn().on(getClass(), "Write lock was locked by someone else! For the " +
                        "lock file:" + path + " " +
                        "by PID: " + getLockedBy(value));
        }
        lockedByThread = null;
        lockedHere = null;
    }

    @Override
    public boolean locked() {
        throwExceptionIfClosed();
        return lock.getVolatileValue(UNLOCKED) != UNLOCKED;
    }

    /**
     * Don't use this - for internal use only
     */
    public void forceUnlock() {
        throwExceptionIfClosed();

        if (locked())
            forceUnlock(lockedBy());
    }

    /**
     * Don't use this - for internal use only
     * Does not warn when force unlocked
     */
    @Deprecated(/* to be removed in x.26. No replacement provided - use forceUnlock */)
    public void forceUnlockQuietly() {
        lock.setValue(UNLOCKED);
    }
}

        //noinspection ConstantConditions,AssertWithSideEffects
        assert SKIP_ASSERTIONS ||
                ((lockedByThread = Thread.currentThread()) != null && (lockedHere = new StackTrace()) != null);
    
<methods>public void <init>(java.lang.String, TableStore<?>, Supplier<net.openhft.chronicle.threads.TimingPauser>) ,public boolean forceUnlockIfProcessIsDead() ,public boolean isLockedByCurrentProcess(java.util.function.LongConsumer) ,public long lockedBy() ,public java.lang.String toString() <variables>protected static final long PID,public static final long UNLOCKED,protected static final java.lang.String UNLOCKING_FORCIBLY_MSG,protected static final java.lang.String UNLOCK_MAIN_MSG,protected final non-sealed net.openhft.chronicle.queue.impl.table.UnlockMode forceUnlockOnTimeoutWhen,protected final non-sealed net.openhft.chronicle.core.values.LongValue lock,private final non-sealed java.lang.String lockKey,protected final non-sealed java.io.File path,protected final non-sealed ThreadLocal<net.openhft.chronicle.threads.TimingPauser> pauser,protected final non-sealed TableStore#RAW tableStore