package org.apache.aries.subsystem.core.internal; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.osgi.service.subsystem.Subsystem; import org.osgi.service.subsystem.SubsystemException; public class LockingStrategy { private final int TRY_LOCK_TIME; private final TimeUnit TRY_LOCK_TIME_UNIT = TimeUnit.SECONDS; public LockingStrategy(String tryLockTime) { int value = 600; // ten mins by default if (tryLockTime != null) { try { value = Integer.parseInt(tryLockTime); } catch (NumberFormatException e) { // ignore, the default will be used } } TRY_LOCK_TIME = value; } /* * A mutual exclusion lock used when acquiring the state change locks of * a collection of subsystems in order to prevent cycle deadlocks. */ private final ReentrantLock lock = new ReentrantLock(); /* * Used when the state change lock of a subsystem cannot be acquired. All * other state change locks are released while waiting. The condition is met * whenever the state change lock of one or more subsystems is released. */ private final Condition condition = lock.newCondition(); /* * Allow only one of the following operations to be executing at the same * time. * * (1) Install * (2) Install Dependencies * (3) Uninstall * * Allow any number of the following operations to be executing at the same * time. * * (1) Resolve * (2) Start * (3) Stop */ private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); private final ThreadLocal<Map<Subsystem.State, Set<BasicSubsystem>>> local = new ThreadLocal<Map<Subsystem.State, Set<BasicSubsystem>>>() { @Override protected Map<Subsystem.State, Set<BasicSubsystem>> initialValue() { return new HashMap<Subsystem.State, Set<BasicSubsystem>>(); } }; public void lock() { try { if (!lock.tryLock(TRY_LOCK_TIME, TRY_LOCK_TIME_UNIT)) { throw new SubsystemException("Unable to acquire the global mutual exclusion lock in time."); } } catch (InterruptedException e) { throw new SubsystemException(e); } } public void unlock() { lock.unlock(); } public void lock(Collection<BasicSubsystem> subsystems) { Collection<BasicSubsystem> locked = new ArrayList<BasicSubsystem>(subsystems.size()); try { while (locked.size() < subsystems.size()) { for (BasicSubsystem subsystem : subsystems) { if (!subsystem.stateChangeLock().tryLock()) { unlock(locked); locked.clear(); if (!condition.await(TRY_LOCK_TIME, TimeUnit.SECONDS)) { throw new SubsystemException("Unable to acquire the state change lock in time: " + subsystem); } break; } locked.add(subsystem); } } } catch (InterruptedException e) { unlock(locked); throw new SubsystemException(e); } } public void unlock(Collection<BasicSubsystem> subsystems) { for (BasicSubsystem subsystem : subsystems) { subsystem.stateChangeLock().unlock(); } signalAll(); } private void signalAll() { lock(); try { condition.signalAll(); } finally { unlock(); } } public boolean set(Subsystem.State state, BasicSubsystem subsystem) { Map<Subsystem.State, Set<BasicSubsystem>> map = local.get(); Set<BasicSubsystem> subsystems = map.get(state); if (subsystems == null) { subsystems = new HashSet<BasicSubsystem>(); map.put(state, subsystems); local.set(map); } if (subsystems.contains(subsystem)) { return false; } subsystems.add(subsystem); return true; } public void unset(Subsystem.State state, BasicSubsystem subsystem) { Map<Subsystem.State, Set<BasicSubsystem>> map = local.get(); Set<BasicSubsystem> subsystems = map.get(state); if (subsystems != null) { subsystems.remove(subsystem); } } public void readLock() { try { if (!rwlock.readLock().tryLock(TRY_LOCK_TIME, TRY_LOCK_TIME_UNIT)) { throw new SubsystemException("Unable to acquire the global read lock in time."); } } catch (InterruptedException e) { throw new SubsystemException(e); } } public void readUnlock() { rwlock.readLock().unlock(); } public void writeLock() { try { if (!rwlock.writeLock().tryLock(TRY_LOCK_TIME, TRY_LOCK_TIME_UNIT)) { throw new SubsystemException("Unable to acquire the global write lock in time."); } } catch (InterruptedException e) { throw new SubsystemException(e); } } public void writeUnlock() { rwlock.writeLock().unlock(); } }