/*************************************************************************** * Copyright (c) 2013 VMware, Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ package com.vmware.vhadoop.vhm; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; import java.util.logging.Logger; import com.vmware.vhadoop.api.vhm.ClusterMap; import com.vmware.vhadoop.api.vhm.ClusterMapReader.ClusterMapAccess; public class MultipleReaderSingleWriterClusterMapAccess implements ClusterMapAccess { private final Set<Thread> _readerThreads = Collections.synchronizedSet(new HashSet<Thread>()); private final Object _clusterMapWriteLock = new Object(); private final ClusterMap _clusterMap; private static MultipleReaderSingleWriterClusterMapAccess _singleton; private static final Logger _log = Logger.getLogger(MultipleReaderSingleWriterClusterMapAccess.class.getName()); /* THREADING: Only accessed by single thread, so no need for synchronization */ static MultipleReaderSingleWriterClusterMapAccess getClusterMapAccess(ClusterMap clusterMap) { if (_singleton == null) { _singleton = new MultipleReaderSingleWriterClusterMapAccess(clusterMap); } return _singleton; } /* for testing only - ugh */ protected static void destroy() { _singleton = null; } private MultipleReaderSingleWriterClusterMapAccess(ClusterMap clusterMap) { _clusterMap = clusterMap; } @Override public ClusterMap lockClusterMap() { Thread currentThread = Thread.currentThread(); if (!_readerThreads.contains(currentThread)) { /* All readers will be blocked during a ClusterMap write and while ClusterMap waits for the reader count to go to zero */ synchronized(_clusterMapWriteLock) { _readerThreads.add(currentThread); } return _clusterMap; } else { _log.severe("VHM: attempt to double-lock the cluster map"); return null; } } @Override public boolean unlockClusterMap(ClusterMap clusterMap) { if (clusterMap == null) { _log.severe("VHM: unlock cluster map called with null cluster map argument - prior lock probably failed"); return false; } Thread currentThread = Thread.currentThread(); if (_readerThreads.contains(currentThread)) { _readerThreads.remove(currentThread); return true; } else { _log.severe("VHM: attempt to double-unlock cluster map"); return false; } } Object runCodeInWriteLock(Callable<Object> callable) throws Exception { synchronized(_clusterMapWriteLock) { /* Wait for the readers to stop reading. New readers will block on the write lock */ long readerTimeout = 1000; long pollSleep = 10; long killCntr = readerTimeout / pollSleep; while (_readerThreads.size() > 0) { try { Thread.sleep(pollSleep); if (--killCntr < 0) { _log.severe("VHM: reader lock left open, whacking to 0"); _readerThreads.clear(); break; } } catch (InterruptedException e) { _log.warning("VHM: unexpected interruption to sleep in writeLock"); } } return callable.call(); } } }