ThreadSafeEmbeddedSlotMap.java

package org.mozilla.javascript;

import java.util.concurrent.locks.StampedLock;

@SuppressWarnings("AndroidJdkLibsChecker")
class ThreadSafeEmbeddedSlotMap extends EmbeddedSlotMap implements LockAwareSlotMap {

    private final StampedLock lock = new StampedLock();
    private volatile LockAwareSlotMap current = this;

    public ThreadSafeEmbeddedSlotMap() {
        super();
    }

    public ThreadSafeEmbeddedSlotMap(int capacity) {
        super(capacity);
    }

    @Override
    public int size() {
        long stamp = lock.tryOptimisticRead();
        int s = current.sizeWithLock();
        if (lock.validate(stamp)) {
            return s;
        }

        stamp = lock.readLock();
        try {
            return current.sizeWithLock();
        } finally {
            lock.unlockRead(stamp);
        }
    }

    @Override
    public int dirtySize() {
        assert lock.isReadLocked() || lock.isWriteLocked();
        return current.sizeWithLock();
    }

    @Override
    public boolean isEmpty() {
        long stamp = lock.tryOptimisticRead();
        boolean e = current.isEmptyWithLock();
        if (lock.validate(stamp)) {
            return e;
        }

        stamp = lock.readLock();
        try {
            return current.isEmptyWithLock();
        } finally {
            lock.unlockRead(stamp);
        }
    }

    @Override
    public Slot modify(SlotMapOwner owner, Object key, int index, int attributes) {
        final long stamp = lock.writeLock();
        try {
            return current.modifyWithLock(owner, key, index, attributes);
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    @Override
    public <S extends Slot> S compute(
            SlotMapOwner owner, Object key, int index, SlotComputer<S> c) {
        final long stamp = lock.writeLock();
        try {
            return current.computeWithLock(owner, key, index, c);
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    @Override
    public Slot query(Object key, int index) {
        long stamp = lock.tryOptimisticRead();
        Slot s = current.queryWithLock(key, index);
        if (lock.validate(stamp)) {
            return s;
        }

        stamp = lock.readLock();
        try {
            return current.queryWithLock(key, index);
        } finally {
            lock.unlockRead(stamp);
        }
    }

    @Override
    public void add(SlotMapOwner owner, Slot newSlot) {
        final long stamp = lock.writeLock();
        try {
            current.addWithLock(owner, newSlot);
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    @Override
    public void addWithLock(SlotMapOwner owner, Slot newSlot) {
        super.add(owner, newSlot);
    }

    @Override
    public <S extends Slot> S computeWithLock(
            SlotMapOwner owner, Object key, int index, SlotComputer<S> compute) {
        return super.compute(owner, key, index, compute);
    }

    @Override
    public boolean isEmptyWithLock() {
        return super.isEmpty();
    }

    @Override
    public Slot modifyWithLock(SlotMapOwner owner, Object key, int index, int attributes) {
        return super.modify(owner, key, index, attributes);
    }

    @Override
    public Slot queryWithLock(Object key, int index) {
        return super.query(key, index);
    }

    @Override
    public int sizeWithLock() {
        return super.size();
    }

    @Override
    public long readLock() {
        return lock.readLock();
    }

    @Override
    public void unlockRead(long stamp) {
        lock.unlock(stamp);
    }

    @Override
    protected void promoteMap(SlotMapOwner owner, Slot newSlot) {
        // We can use `setMap` here as this promotion can only be done
        // by the lock holder and the operation will be being done on
        // the "current" map.
        var newMap = new ThreadSafeHashSlotMap(lock, this, newSlot);
        owner.setMap(newMap);
        current = newMap;
    }
}