org.apache.jmeter.control.CriticalSectionController Maven / Gradle / Ivy
/*
* 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.jmeter.control;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.testelement.property.StringProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a Critical Section Controller; it will execute the set of statements
* (samplers/controllers, etc) under named lock.
*
* In a programming world - this is equivalent of :
*
*
* try {
* named_lock.lock();
* statements ....
* } finally {
* named_lock.unlock();
* }
*
*
* In JMeter you may have :
*
*
* Thread-Group (set to loop a number of times or indefinitely,
* ... Samplers ... (e.g. Counter )
* ... Other Controllers ....
* ... CriticalSectionController ( lock name like "foobar" )
* ... statements to perform when lock acquired
* ...
* ... Other Controllers /Samplers }
*
*
* @since 2.12
*/
public class CriticalSectionController extends GenericController implements
ThreadListener, TestStateListener {
private static final long serialVersionUID = 1L;
private static final Logger log = LoggerFactory.getLogger(CriticalSectionController.class);
private static final String LOCK_NAME = "CriticalSectionController.lockName"; //$NON-NLS-1$
private static final ConcurrentHashMap LOCK_MAP = new ConcurrentHashMap<>();
private transient volatile ReentrantLock currentLock;
/**
* constructor
*/
public CriticalSectionController() {
super();
}
/**
* constructor
* @param name The name of this controller
*/
public CriticalSectionController(String name) {
super();
this.setName(name);
}
/**
* Condition Accessor - this is gonna be any string value
* @param name The name of the lock for this controller
*/
public void setLockName(String name) {
setProperty(new StringProperty(LOCK_NAME, name));
}
/**
* If lock exists returns it, otherwise creates one, puts it in LOCK_MAP
* then returns it
*
* @return {@link ReentrantLock}
*/
private ReentrantLock getOrCreateLock() {
String lockName = getLockName();
ReentrantLock lock = LOCK_MAP.get(lockName);
ReentrantLock prev;
if (lock != null) {
return lock;
}
lock = new ReentrantLock();
prev = LOCK_MAP.putIfAbsent(lockName, lock);
return prev == null ? lock : prev;
}
/**
* @return String lock name
*/
public String getLockName() {
return getPropertyAsString(LOCK_NAME);
}
/**
* @see org.apache.jmeter.control.Controller#next()
*/
@Override
public Sampler next() {
if (StringUtils.isEmpty(getLockName())) {
if (log.isWarnEnabled()) {
log.warn("Empty lock name in Critical Section Controller: {}", getName());
}
return super.next();
}
if (isFirst()) {
// Take the lock for first child element
long startTime = System.currentTimeMillis();
if (this.currentLock == null) {
this.currentLock = getOrCreateLock();
}
this.currentLock.lock();
long endTime = System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Thread ('{}') acquired lock: '{}' in Critical Section Controller {} in: {} ms",
Thread.currentThread(), getLockName(), getName(), endTime - startTime);
}
}
return super.next();
}
/**
* Called after execution of last child of the controller We release lock
*
* @see org.apache.jmeter.control.GenericController#reInitialize()
*/
@Override
protected void reInitialize() {
if (this.currentLock != null) {
if (currentLock.isHeldByCurrentThread()) {
this.currentLock.unlock();
}
this.currentLock = null;
}
super.reInitialize();
}
@Override
public void threadStarted() {
this.currentLock = null;
}
@Override
public void threadFinished() {
if (this.currentLock != null
&& this.currentLock.isHeldByCurrentThread()) {
if (log.isWarnEnabled()) {
log.warn("Lock '{}' not released in: {}, releasing in threadFinished", getLockName(), getName());
}
this.currentLock.unlock();
}
this.currentLock = null;
}
@Override
public void testStarted() {
// NOOP
}
@Override
public void testStarted(String host) {
// NOOP
}
@Override
public void testEnded() {
LOCK_MAP.clear();
}
@Override
public void testEnded(String host) {
testEnded();
}
}