In my windows form application, I'm trying to test the user's ability to access a remote machine's shared folder. The way I'm doing this (and I'm sure that there are better ways...but I don't know of them) is to check for the existence of a specific directory on the remote machine (I'm doing this because of firewall/other security restrictions that I'm confronted with in my organization). If the user has rights to access the shared folder, then it returns in no time at all, but if they don't, it hangs forever. To solve this, I threw the check into another thread and wait only 1000 milliseconds before determining that the share can't be hit by the user. However, when I do this, it still hangs as if it was never run in the same thread.
What is making it hang and how do I fix it? I would think that the fact that it is in a separate thread would allow me to just let the thread finish on it's own in the background.
Here is my code:
bool canHitPath = false;
Thread thread = new Thread(new ThreadStart(() =>
{
canHitPath = Directory.Exists(compInfo.Path);
}));
thread.Start();
thread.Join(1000);
if (canHitPath == false)
{
throw new Exception("Cannot hit folder: " + compInfo.Path);
}
Edit: I feel that I should add that the line of throwing the exception IS HIT. I've debugged this and verified it...however, when the exception is thrown, then is when my program hangs. (I might also add that the exception is caught in the calling method and I never get to the catch statement in the debugger.)
Edit: So I found the answer to this question...but I can't select my answer as the answer for 24 hours...so until then...you'll have to go to the following link: https://stackoverflow.com/a/10045338/1203288 Sorry.
Directory.Exists()
method, or some line of code within the BCL itself - Daniel Pryden 2012-04-05 17:47
I found the real problem:
The property compInfo.Path is doing a check for a directory existence on the remote file system to determine if the remote machine is 64 bit or not. Depending on the results, it returns a different value. I tried commenting out the the check and it executed successfully. This explains why I couldn't get past the throwing of the exception, I call compInfo.Path in the message of the exception.
However, I think we learned a lot from "the real problem":
Thanks everyone for your help, patience, and thoughtful input/insights.
My guess will be that this happens because when you call:
canHitInstallPath = Directory.Exists(compInfo.InstallPath);
The Exists
method holds the execution flow of the tread (it is an uninterruptible call). If it hangs for 30 seconds, them your thread will be waiting for 30 seconds until it has a chance to check if Thread.Join(1000)
has elapsed.
Note that the Thread.Join() method only blocks the calling thread (usually the application's main thread of execution) until your thread object completes. You can still have other threads executing in the background while waiting for your specific Thread to finish executing.
From:
Thread.Join Method (System.Threading)
Another thing to consider: Only checking if a folder exists tells nothing if the user can read or write files to the folder. Your best option is to try to write or read a file of the folder. This way you can make sure that the user has permissions in that folder.
EDIT
In you case threads only make sense if you can do other things while waiting for the thread to finish. If you cant, them threads are not helping you at all.
EDIT2
A link to support my answer: File Descriptors And Multithreaded Programs
EDIT3
You best option is to create a killer thread. This thread will kill the DirectoryExists
thread when it hangs for more that X seconds.
Directory.Exists()
is taking a long time because it's a remote path. If the network connection has been lost, it can take 30 seconds (or possibly even longer, IIRC) before the operating system aborts the IO operation. (You can reproduce this hang even in Windows Explorer.) What he's trying to do is to isolate that IO wait to another thread so as to detect a failure more quickly - Daniel Pryden 2012-04-05 17:29
Your comments make it clear that the exception is actually thrown and caught. So code execution progressed at least past this code and we can't tell from the snippet what it is doing.
You did make one mistake, you forgot to set the thread's IsBackground property to true. Without it, the program can't terminate. Which is one way you might conclude "it's blocking!". If this guess isn't accurate then we'll need to see the main thread's call stack to have an idea what it is doing. Best captured by turning on unmanaged debugging support and enabling the Microsoft Symbol Server so we can see the entire call stack, not just the managed parts of it.
A completely different approach is to use the Ping class to probe the server.
Maybe take a look at the Task Parallel Library. The TPL is designed to maximize performance and might handle whatever the issue might be. Though it also might be complete overkill for the situation.
Thread.Join
is a blocking call that will hold up the thread it was called from until the thread it was called on exits.
You are basically spinning up a new thread to do background work, and then telling the main thread to wait until it finishes. In effect doing it all synchronously.
Thread.Join()
that takes a timeout, so at the very least the behavior is unintuitive - Daniel Pryden 2012-04-05 17:06
Thread.Join(1000)
will return after one second no matter what. It will kill the thread after a second no matter what it is doing. It shouldn't be blocking for more than that - I've just copy-pasted this code and swapped Exists
with Thread.Sleep(30000)
and it blocks for one second only. Not sure what is wrong with OP's situation.. - J... 2012-04-05 17:09
Sleep
is an interruptible wait, while the problem the OP is encountering is with an uninterruptible IO wait. They are not the same thing at all - Daniel Pryden 2012-04-05 17:12
Join
would not depend on the thread in question. This almost feels like a bug. Either a thread is done or it is not. There should be no reason that Join
cannot count 1000ms all by itself and determine whether or not the thread has finished by then. Can anyone say why Join
cannot query the state of the thread, or why the thread itself has to participate in message passing, when we're talking about a boolean (atomic) value - J... 2012-04-05 20:04
Thread
and has a public boolean property IsDone
, set true when the thread completes (until disposed). Surely the thread does not need to be free for another thread to read that memory location. What value is there with Join
being an active query requiring a response from the thread itself? Is there a Join
alternative which doesn't care what the thread it is waiting on is doing - something which just waits for whatever time and then returns - J... 2012-04-05 20:09
Join(1000)
should abort if the actual join cannot be performed within 1 second. I wonder: if the thread that you're trying to join is actually in an uninterruptible IO wait, then can it cause both threads to get stuck in the IO wait - Daniel Pryden 2012-04-05 17:07