HiveBrain v1.2.0
Get Started
← Back to all entries
debugcsharpMinor

Waiting for a file to be accessible with Thread.Sleep() and exception catching

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
fileexceptionwaitingaccessiblewithsleepcatchingthreadforand

Problem

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.



///
/// Tries to open the specified file over and over again until it is acessible.
///
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?

Solution

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 OpenStreamAsync(String filename, FileAccess fileAccess, FileShare fileShare,int retryCount){

    if (retryCount  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(this Task task, TaskCompletionSource 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 ContinueWithTask(this Task sourceTask, Func,Task> continuation){
        var completionSource = new TaskCompletionSource();
        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 Wait(TimeSpan span){
         var completionSource = new TaskCompletionSource();
         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;

Code Snippets

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;
     }
}
var stream = OpenStreamAsync("myFile", FileAccess.ReadWrite, FileShare.None,20).Result;

Context

StackExchange Code Review Q#7210, answer score: 3

Revisions (0)

No revisions yet.