[jcifs] Possible deadlocking between jcifs.smb.Dfs and jcifs.smb.SmbTransport
Xavier Roche
roche+kml2 at exalead.com
Tue Mar 6 09:51:50 MST 2012
On 03/05/2012 05:02 PM, Michael B Allen wrote:
> Can you please create a small torture test program that triggers the
> deadlock and post it?
Sure -- here is a minimal test case (the code is a bit messy, but should
not have any thread-unsafe calls).
Please note that the deadlock may be difficule to reproduce depending on
the environment. We typically run it during few minutes, and get:
** Deadlock detected! **
The following thread:
Thread #3648 (Transport5) group="main" state="BLOCKED" priority="5"
daemon="true" alive="true" interrupted="false"
classLoader="sun.misc.Launcher$AppClassLoader"
jcifs.util.transport.Transport.loop(Transport.java:99)
jcifs.util.transport.Transport.run(Transport.java:267)
java.lang.Thread.run(Thread.java:662)
Thread #12 (Thread-4) group="main" state="BLOCKED" priority="5"
daemon="false" alive="true" interrupted="false"
classLoader="sun.misc.Launcher$AppClassLoader"
jcifs.smb.Dfs.insert(Dfs.java:289)
jcifs.smb.SmbTransport.checkStatus(SmbTransport.java:556)
jcifs.smb.SmbTransport.send(SmbTransport.java:640)
jcifs.smb.SmbSession.send(SmbSession.java:238)
jcifs.smb.SmbTree.send(SmbTree.java:119)
jcifs.smb.SmbFile.send(SmbFile.java:775)
jcifs.smb.SmbFile.queryPath(SmbFile.java:1363)
jcifs.smb.SmbFile.exists(SmbFile.java:1417)
jcifs.smb.SmbFile.isDirectory(SmbFile.java:1490)
test.deadlock.SmbStressTest$FileScanner.scanFiles(SmbStressTest.java:79)
test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)
has been blocked for 0s by:
The following thread:
Thread #12 (Thread-4) group="main" state="BLOCKED" priority="5"
daemon="false" alive="true" interrupted="false"
classLoader="sun.misc.Launcher$AppClassLoader"
jcifs.smb.Dfs.insert(Dfs.java:289)
jcifs.smb.SmbTransport.checkStatus(SmbTransport.java:556)
jcifs.smb.SmbTransport.send(SmbTransport.java:640)
jcifs.smb.SmbSession.send(SmbSession.java:238)
jcifs.smb.SmbTree.send(SmbTree.java:119)
jcifs.smb.SmbFile.send(SmbFile.java:775)
jcifs.smb.SmbFile.queryPath(SmbFile.java:1363)
jcifs.smb.SmbFile.exists(SmbFile.java:1417)
jcifs.smb.SmbFile.isDirectory(SmbFile.java:1490)
test.deadlock.SmbStressTest$FileScanner.scanFiles(SmbStressTest.java:79)
test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)
Code:
----
package test.deadlock;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Map;
import java.util.Vector;
import jcifs.Config;
import jcifs.smb.ACE;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
/*
* Stress test for Dfs/Transport deadlock.
*/
public class SmbStressTest {
protected static final int filesMax = 1000;
protected static final int maxThreads = 8;
protected final Vector<String> files = new Vector<String>();
protected boolean exit = false;
protected synchronized static void debug(String message) {
System.err.println(message);
}
/*
* Runner, scanning all directories.
*/
protected class FileScanner extends Thread {
@Override
public void run() {
try {
scanFiles();
} catch (Exception e) {
e.printStackTrace();
}
debug("worker thread ended");
}
protected void scanFiles() {
for (;;) {
String itemUrl;
// Fetch next directory.
synchronized (SmbStressTest.this) {
// Wait for queue to be not empty.
debug("waiting for a file to be available");
while (!exit && files.size() == 0) {
try {
SmbStressTest.this.wait();
} catch (InterruptedException e) {
}
}
if (exit) {
return;
}
// Get a random item.
itemUrl = files.get((int) (files.size() *
Math.random()));
// SmbStressTest.this.notifyAll();
debug("got file: " + url);
}
// Let's look at this file
try {
// Recreate file by scanning each segment to
reproduce the
// issue more easily.
final String[] segments =
itemUrl.substring(url.length()).split("/");
SmbFile smbFile = new SmbFile(url, new
NtlmPasswordAuthentication(domain, username, password),
SmbFile.FILE_SHARE_READ);
for (int i = 0; i < segments.length; i++) {
if (i + 1 < segments.length) {
smbFile = new SmbFile(smbFile, segments[i]
+ "/", SmbFile.FILE_SHARE_READ);
if (!smbFile.isDirectory()) {
throw new
IllegalStateException("unexpected file here");
}
} else {
smbFile = new SmbFile(smbFile, segments[i],
SmbFile.FILE_SHARE_READ);
if (smbFile.isDirectory()) {
throw new
IllegalStateException("unexpected directory here");
}
}
if (!smbFile.exists()) {
throw new IllegalStateException("unexpected
file not found: " + smbFile.getPath());
}
}
final String path = smbFile.getPath();
if (smbFile.isDirectory()) {
System.err.println(path + " is a directory");
} else if (smbFile.isFile()) {
System.err.println(path + " is a file");
}
// Fetch some ACLs
for (ACE ace : smbFile.getSecurity(false)) {
ace.getFlags();
}
} catch (IOException io) {
System.err.println(io);
}
}
}
}
protected static String getPassword(String pwd) throws IOException {
if (pwd.length() == 0) {
System.out.print("password ? ");
System.out.flush();
InputStreamReader inp = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(inp);
return br.readLine();
} else {
return pwd;
}
}
/*
* Constructor.
*/
public SmbStressTest(String[] args) throws IOException {
if (args.length == 0) {
throw new RuntimeException("usage: .. sbm://server/dfs/
USERNAME PASSWORD DOMAIN");
}
url = args[0];
username = args[1];
password = getPassword(args[2]);
domain = args[3];
}
protected final String url;
protected final String username;
protected final String password;
protected final String domain;
/*
* Main.
*/
public void main() throws IOException {
// Root file (DFS)
final SmbFile dfsDir = new SmbFile(url, new
NtlmPasswordAuthentication(domain, username, password),
SmbFile.FILE_SHARE_READ);
// Create deadlock watchdog thread
new DeadlockWatchdog().start();
// Create worker threads
for (int i = 0; i < maxThreads; i++) {
new FileScanner().start();
}
// Collect files
final Vector<SmbFile> dirs = new Vector<SmbFile>();
dirs.add(dfsDir);
while (dirs.size() != 0) {
final SmbFile dir = dirs.remove(0);
try {
for (SmbFile file : dir.listFiles()) {
if (file.isDirectory()) {
debug("added directory: " + file);
dirs.add(file);
} else if (file.isFile()) {
debug("added file: " + file);
synchronized (this) {
while (files.size() > maxThreads * 2) {
files.remove((int) (files.size() *
Math.random()));
}
// break;
for (int i = 0; i < maxThreads; i++) {
files.add(file.toString());
}
this.notifyAll();
}
}
}
} catch (Exception io) {
System.err.println(io);
}
}
// Wait for a key and exit
System.in.read();
debug("exiting ..");
synchronized (this) {
exit = true;
this.notifyAll();
}
}
// Static main().
public static void main(String[] args) throws IOException {
Config.setProperty("jcifs.smb.client.responseTimeout", "3000");
Config.setProperty("jcifs.smb.client.soTimeout", "100");
Config.setProperty("jcifs.netbios.cachePolicy", "30");
Config.setProperty("jcifs.smb.client.tcpNoDelay", "true");
new SmbStressTest(args).main();
}
/*
* Deadlock watchdog.
*/
protected class DeadlockWatchdog extends Thread {
private void printStackTrace(PrintStream servletOutputStream,
Thread thread, StackTraceElement[] stack)
throws IOException {
final String threadName = thread.getName();
final ClassLoader threadClassLoader =
thread.getContextClassLoader();
final String threadClassLoaderName = threadClassLoader !=
null ? threadClassLoader.getClass().getName()
: null;
final ThreadGroup threadGroup = thread.getThreadGroup();
final String threadGroupName = threadGroup != null ?
threadGroup.getName() : null;
servletOutputStream.println("Thread #" +
Long.toString(thread.getId())
+ (threadName != null ? (" (" + threadName + ")") : "")
+ (threadGroupName != null ? (" group=\"" +
threadGroupName + "\"") : "") + " state=\""
+ thread.getState().toString() + "\"" + "
priority=\"" + Integer.toString(thread.getPriority())
+ "\"" + " daemon=\"" + (thread.isDaemon() ? "true"
: "false") + "\"" + " alive=\""
+ (thread.isAlive() ? "true" : "false") + "\"" + "
interrupted=\""
+ (thread.isInterrupted() ? "true" : "false") + "\""
+ (threadClassLoaderName != null ? ("
classLoader=\"" + threadClassLoaderName + "\"") : ""));
if (stack.length > 0) {
for (StackTraceElement trace : stack) {
servletOutputStream.println("\t" + trace.toString());
}
}
}
private void printStackTrace(PrintStream servletOutputStream,
Map<Thread, StackTraceElement[]> stacks, long id)
throws IOException {
for (Map.Entry<Thread, StackTraceElement[]> entry :
stacks.entrySet()) {
final Thread thread = entry.getKey();
if (thread.getId() == id) {
final StackTraceElement[] stack = entry.getValue();
printStackTrace(servletOutputStream, thread, stack);
servletOutputStream.println();
}
}
}
@Override
public void run() {
watchdog();
debug("watchdog thread ended");
}
protected void watchdog() {
final ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
for (;;) {
final long[] ids = tmx.findMonitorDeadlockedThreads();
// Deadlock detected ?
if (ids != null && ids.length != 0) {
// Notify other threads
synchronized (SmbStressTest.this) {
exit = true;
SmbStressTest.this.notifyAll();
// Alert
System.err.println();
System.err.println("** Deadlock detected! **");
// Print offending stack traces
final Map<Thread, StackTraceElement[]> stacks =
Thread.getAllStackTraces();
final ThreadInfo[] infos = tmx.getThreadInfo(ids);
for (final ThreadInfo ti : infos) {
final long ms = ti.getWaitedTime();
System.err.println("The following thread:");
try {
printStackTrace(System.err, stacks,
ti.getThreadId());
printStackTrace(System.err, stacks,
ti.getLockOwnerId());
} catch (IOException e) {
}
System.err.println("has been blocked for "
+ (ms / 1000) + "s by:");
}
System.err.flush();
// EXIT.
System.exit(-1);
}
}
// Sleep ..
try {
// debug("sleeping ..");
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// Exit ?
synchronized (SmbStressTest.this) {
if (exit) {
break;
}
}
}
}
}
}
More information about the jCIFS
mailing list