blob: 79efa76b5ec45bd4a2086625f0ceaad581573aee [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.ambari.server.logging;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ambari.server.configuration.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Ticker;
import com.google.inject.Inject;
import com.google.inject.Singleton;
/**
* Factory to create locks depending on configuration. If lock profiling is enabled,
* it creates instrumented locks that collect statistics and log requests. If profiling is
* disabled, it creates regular reentrant locks.
*
* @see Configuration#isServerLocksProfilingEnabled()
*/
@Singleton
public class LockFactory {
private static final Logger LOG = LoggerFactory.getLogger(LockFactory.class);
private final boolean profiling;
private final Set<ProfiledLock> profiledLocks;
@Inject
public LockFactory(Configuration config) {
profiling = config.isServerLocksProfilingEnabled();
profiledLocks = profiling ? new CopyOnWriteArraySet<>() : null;
LOG.info("Lock profiling is {}", profiling ? "enabled" : "disabled");
}
/**
* @return a new Lock instance (implementation depends on configuration setting) without any special label to identify it in log messages
*/
public Lock newLock() {
return newLock(getDefaultPrefix());
}
/**
* @return a new Lock instance (implementation depends on configuration setting) with <code>label</code> to identify it in log messages
*/
public Lock newLock(String label) {
ReentrantLock baseLock = new ReentrantLock();
if (profiling) {
ProfiledReentrantLock profiledLock = new ProfiledReentrantLock(baseLock, Ticker.systemTicker(), label);
profiledLocks.add(profiledLock);
return profiledLock;
}
return baseLock;
}
/**
* @return a new ReadWriteLock instance (implementation depends on configuration setting) without any special label to identify it in log messages
*/
public ReadWriteLock newReadWriteLock() {
return newReadWriteLock(getDefaultPrefix());
}
/**
* @return a new ReadWriteLock instance (implementation depends on configuration setting) with <code>label</code> to identify it in log messages
*/
public ReadWriteLock newReadWriteLock(String label) {
ReentrantReadWriteLock baseLock = new ReentrantReadWriteLock();
if (profiling) {
ProfiledReentrantReadWriteLock profiledLock = new ProfiledReentrantReadWriteLock(baseLock, Ticker.systemTicker(), label);
profiledLocks.add(profiledLock.readLock());
profiledLocks.add(profiledLock.writeLock());
return profiledLock;
}
return baseLock;
}
/**
* If lock profiling is enabled, append summary statistics about lock usage to <code>sb</code>
* @param sb the buffer to append the statistics to
*/
public void debugDump(StringBuilder sb) {
if (profiling) {
sb.append("\n\t\tLocks: [");
for (ProfiledLock lock : profiledLocks) {
sb.append("\n\t\t\t").append(lock.getLabel())
.append(lock)
.append(" waited: ").append(lock.getTimeSpentWaitingForLock())
.append(" held: ").append(lock.getTimeSpentLocked())
.append(" times locked: ").append(lock.getLockCount());
}
if (!profiledLocks.isEmpty()) {
sb.append("\n");
}
sb.append("]");
}
}
private static String getDefaultPrefix() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// 0: getStackTrace()
// 1: getDefaultPrefix()
// 2: newLock()
// 3: caller of newLock()
return stackTrace.length > 3
? stackTrace[3].getFileName() + ":" + stackTrace[3].getLineNumber()
: "";
}
}