4
\$\begingroup\$

I know I've already asked a similar question, but this feels quite different to me.

The main goal is to wait for a file to be accessible (read: waiting until the file lock releases), since it seems like there's no way to do this built into the .NET framework I've written a quick and dirty function to do this.

The requirements which should be kept in mind with this:

  • The file shouldn't be locked often. Actually, it should be locked quite rarely (figure how often clients manage to still hit that).
  • There's no need for a limit, if the file is locked for longer then a second, something is fuc^H^H^Hscrewed up.
  • It should block the main thread until the file is available.
///<summary>
/// Tries to open the specified file over and over again until it is acessible.
///</summary>
public static FileStream WaitForStream(String filename, FileAccess fileAccess, FileShare, fileShare)
{
    while(true) // Gives me the chills
    {
        try
        {
            return new FileStream(filename, FileMode.OpenOrCreate, fileAccess, fileShare);
        }
        catch (IOException ex)
        {
            Logger.LogError(ex); // Information purposes only
            Threading.Thread.Sleep(250); // Just looks wrong
        }
    }

    return null; // Only to keep the compiler happy.
}

The problems I see myself:

  • while(true)
  • Thread.Sleep()
  • It might loop forever, but as I said, if that happens we need to intervene anyway.

Reimplementing this with a similar solution I already used smells like absolute overkill to me, given that this should be built-in functionality anyway. What stuff didn't I think about?

\$\endgroup\$
2
  • 2
    \$\begingroup\$ stackoverflow.com/questions/1304/… \$\endgroup\$
    – Adam
    Dec 28, 2011 at 12:05
  • 1
    \$\begingroup\$ Definitely problematic - if there's a different IOException that has no chance of resolving, you're effectively in an infinite loop. You should have a maximum retry count (or a maximum time limit) as a parameter that it gives up after. \$\endgroup\$ Dec 28, 2011 at 14:10

1 Answer 1

3
\$\begingroup\$

How about making this an asynchrounous operation that only returns if the stream has successfully been opened. you can get rid of the while(true) and the thread sleep plus the new mthod call cn have a retry count.

Here is what it might look like if you use C# 4.0 (not tested).

Task<FileStream> OpenStreamAsync(String filename, FileAccess fileAccess, FileShare fileShare,int retryCount){

    if (retryCount < 1)
        throw new Exception("maximum retry reached");

    return 
        Task.Factory.StartNew(() => new FileStream(filename, FileMode.OpenOrCreate, fileAccess, fileShare))
        .ContinueWithTask( task => {
            if (task.IsCompleted)
                return task;

            Logger.LogError(ex); // Information purposes only
            return TaskExtenstion2.Wait(TimeSpan.FromMilliseconds(250))
                .ContinueWithTask(t => OpenStreamAsync(filename, fileAccess, fileShare,retryCount--) );
        });
}

public static class TaskExtenstion2 {

    /// Set the completionSource to the same values as the task
    public static void SetCompletionSource<TResult>(this Task<TResult> task, TaskCompletionSource<TResult> completionSource){
        if (task.IsCompleted){
            completionSource.SetResult(task.Result);
        }else if (task.IsCanceled){
            completionSource.SetCanceled();
        }else if (task.IsFaulted){
            completionSource.SetException(task.Exception);
        }
    }

    /// Continues a task with another task genrated by the specified function
    public static Task<U> ContinueWithTask<T,U>(this Task<T> sourceTask, Func<Task<T>,Task<U>> continuation){
        var completionSource = new TaskCompletionSource<U>();
        sourceTask.ContinueWith(firstTask => {
            var secondTask = continuation(firstTask);
            secondTask.ContinueWith(task => task.SetCompletionSource(completionSource));
        });
        return completionSource.Task;
    }

     /// returns true after a certain amount of time 
     public static Task<bool> Wait(TimeSpan span){
         var completionSource = new TaskCompletionSource<bool>();
         Timer timer = null;
         timer = new Timer(_ => {
             using(timer) {
               completionSource.SetResult(true);
             }
         },null,span,TimeSpan.MaxValue);
         return completionSource.Task;
     }
}

if you still want to block the main thread, getting the Result property of the returning task will make sure of that

var stream = OpenStreamAsync("myFile", FileAccess.ReadWrite, FileShare.None,20).Result;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.