Re: Possible deadlocking between jcifs.smb.Dfs and jcifs.smb.SmbTransport

Xavier Roche <[email protected]>
Newsgroups gmane.network.samba.java
Organization Exalead
Message-ID <[email protected]>
Hi folks,

On 03/07/2012 03:09 AM, Michael B Allen wrote:
> Your soTimeout value of is borderline invalid. That soTimeout means
> that if no response is received within 100 ms the socket will close.
> And your responseTimeout is pointless if the soTimeout is less than
> the responseTimeout.

I removed the bogus Config.* lines (they were intended to increase the 
deadlocking odds), but the deadlock is still reproducible.

> Also your line numbers don't match the stock package.

Sorry, I cleaned up a bit the source. Here is the new one attached.

> But if it's not too much trouble it would be great if you could also
> post a thread dump when your "Deadlock detected!" condition is
> reached. At least it would be a little more impressive if a thread
> dump reported the deadlock too.

Sure -- here is another one:

** Deadlock detected! **
The following thread:
Thread #21 (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)

has been blocked for 0s by:
Thread #15 (Thread-7) 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:80)
         test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)


The following thread:
Thread #15 (Thread-7) 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:80)
         test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)

has been blocked for 0s by:
Thread #9 (Thread-1) group="main" state="BLOCKED" priority="5" 
daemon="false" alive="true" interrupted="false" 
classLoader="sun.misc.Launcher$AppClassLoader"
         jcifs.smb.SmbTransport.getSmbSession(SmbTransport.java:126)
         jcifs.smb.SmbTransport.getDfsReferrals(SmbTransport.java:701)
         jcifs.smb.Dfs.getReferral(Dfs.java:143)
         jcifs.smb.Dfs.resolve(Dfs.java:191)
         jcifs.smb.SmbFile.doConnect(SmbFile.java:902)
         jcifs.smb.SmbFile.connect(SmbFile.java:954)
         jcifs.smb.SmbFile.connect0(SmbFile.java:880)
         jcifs.smb.SmbFile.exists(SmbFile.java:1415)
 
test.deadlock.SmbStressTest$FileScanner.scanFiles(SmbStressTest.java:89)
         test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)


The following thread:
Thread #9 (Thread-1) group="main" state="BLOCKED" priority="5" 
daemon="false" alive="true" interrupted="false" 
classLoader="sun.misc.Launcher$AppClassLoader"
         jcifs.smb.SmbTransport.getSmbSession(SmbTransport.java:126)
         jcifs.smb.SmbTransport.getDfsReferrals(SmbTransport.java:701)
         jcifs.smb.Dfs.getReferral(Dfs.java:143)
         jcifs.smb.Dfs.resolve(Dfs.java:191)
         jcifs.smb.SmbFile.doConnect(SmbFile.java:902)
         jcifs.smb.SmbFile.connect(SmbFile.java:954)
         jcifs.smb.SmbFile.connect0(SmbFile.java:880)
         jcifs.smb.SmbFile.exists(SmbFile.java:1415)
 
test.deadlock.SmbStressTest$FileScanner.scanFiles(SmbStressTest.java:89)
         test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)

has been blocked for 0s by:
Thread #15 (Thread-7) 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:80)
         test.deadlock.SmbStressTest$FileScanner.run(SmbStressTest.java:39)


Thanks for investigating,

Xavier
SmbStressTest.java (text/x-java, 11.3 KB)
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 = 10000;
    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 IOException("unexpected file here: " + smbFile.getPath());
                            }
                        } else {
                            smbFile = new SmbFile(smbFile, segments[i], SmbFile.FILE_SHARE_READ);
                            if (smbFile.isDirectory()) {
                                throw new IOException("unexpected directory here" + smbFile.getPath());
                            }
                        }
                        if (!smbFile.exists()) {
                            throw new IOException("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: .. smb://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() > filesMax) {
                                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.netbios.cachePolicy", "30");
        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());
                                System.err.println("has been blocked for " + (ms / 1000) + "s by:");
                                printStackTrace(System.err, stacks, ti.getLockOwnerId());
                                System.err.println();
                            } catch (IOException e) {
                            }
                        }
                        System.err.flush();
                        // EXIT.
                        System.exit(-1);
                    }
                }

                // Sleep ..
                try {
                    // debug("sleeping ..");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }

                // Exit ?
                synchronized (SmbStressTest.this) {
                    if (exit) {
                        break;
                    }
                }
            }
        }
    }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.