Converting an application to work on a network

  • Thread starter Thread starter > Adrian
  • Start date Start date
A

> Adrian

I have converted a number of applications to enable them to work together on
a network. I have been led to believe that I can do this as follows:

FileStream fs = new FileStream(some code);
while (!fs.CanRead){allow the processor to handle another thread;}

Hoverer, doing some tests I find out that the solution isn't working.

Is there a simple way out?
 
Hi,

I don't think that the CanRead property is dynamically updated. I believe
it acquires its value when the FileStream is created.

Try the following instead:

public static FileStream OpenWhenReady(string path, FileAccess access)
{
FileStream stream = null;

do
{
try
{
stream = new FileStream(path, FileMode.Open, access);
}
catch (IOException)
{
System.Threading.Thread.Sleep(500);
}
catch (UnauthorizedAccessException)
{
System.Threading.Thread.Sleep(500);
}
}
while (stream == null);

return stream;
}

Use the method like this:

using (FileStream stream = OpenWhenReady(path, FileAccess.Read))
{
StreamReader reader = new StreamReader(stream);
Console.WriteLine(reader.ReadToEnd());
}
 
Dave Sexton said:
Hi,

I don't think that the CanRead property is dynamically updated. I believe
it acquires its value when the FileStream is created.

Try the following instead:

public static FileStream OpenWhenReady(string path, FileAccess access)
{
FileStream stream = null;

do
{
try
{
stream = new FileStream(path, FileMode.Open, access);
}
catch (IOException)
{
System.Threading.Thread.Sleep(500);
}
catch (UnauthorizedAccessException)
{
System.Threading.Thread.Sleep(500);
}
}
while (stream == null);

return stream;
}

Use the method like this:

using (FileStream stream = OpenWhenReady(path, FileAccess.Read))
{
StreamReader reader = new StreamReader(stream);
Console.WriteLine(reader.ReadToEnd());
}
********************************
Dave,

What you suggest looks very good. In the
mean time, the following came to my mind,
namely using the directory as means for the
file management, like so; what do you think?
I guess your solution is the professional one.

Adrian

void block(int arg)
{
string blocker_file = "block"+arg.ToString();
while(File.Exists(blocker_file))
{
System.Threading.Thread.Sleep(200);
}
StreamWriter fs = File.CreateText(blocker_file);
}

void unblock(int arg)
{
string blocker_file = "block"+arg.ToString();
while(true)
{
try
{
File.Delete(blocker_file);
break;
}
catch
{
System.Threading.Thread.Sleep(200);
continue;
}
}
}
 
Hi Adrian,

My code isn't meant to synchronize access to the file, it's simply meant to
wait for availability. However, there are ways to synchronize access, some
of which could be included in my example:

A. Specify the FileShare enum parameter when constructing the FileStream
B. Call FileStream.Lock once it has been opened (Win32 LockFile)
C. Use standard, cross-process synchronization mechanisms such as Mutex
(This will only allow synchronization from within your application
processes)
D. Create a synchronization controller service to synchronize access to
your application's shared resources, such as files. Host the service on
IIS
or the remoting framework using Tcp or Ipc.

Personally, I don't like the idea of using a temporary file to provide
synchronization, as in your example.
 
Dave,
Can't I do it like this, following your solution?
Adrian.

public static FileStream whenReady(string path, int arg)
{
FileStream stream = null;
do
{
try
{
if(arg == 0) stream = new FileStream(path,
FileMode.Open,FileAccess.Read);
if(arg == 1) stream = new FileStream(path,
FileMode.OpenOrCreate,FileAccess.Write);
if(arg == 3) stream = new FileStream(path,
FileMode.Append,FileAccess.Write);
}
catch (IOException)
{
System.Threading.Thread.Sleep(500);
}
catch (UnauthorizedAccessException)
{
System.Threading.Thread.Sleep(500);
}
}
while (stream == null);
return stream;
}

******************
 
Hi Adrian,

Yes, apparently the default is FileShare.Read (I assumed
FileShare.ReadWrite). The code in my example will prevent write-access to
the file in other processes while it remains opened.

In your latest code sample, why not just accept a FileMode argument instead
of int?
 
Dave,
That threw up an error.
Thank you for your help,
you saved my neck.

Adrian.
 
Dave,

When arg = o (reading) and reading is done in a try/catch loop
the application will freeze, so I made a slight addition to your
solution, namely added a posting to a log file if arg = 0 and
the file doesn't exist. Should the situation arise, then it will
be easily rectified.

Many thanks again,
Adrian.

By the way are you able to solve my other little problem
that I posted concerning shifting the margin a bit to the left
in the print routine?, q.v. my posting if you have time.

Thanks again,

Adrian.
 
Hi Adrian,

What is the error?

I tested my code and it worked fine. I haven't tested it with your
modifications.
 
Dave,

Passing in the file access parameter. That is why I used the argument.
I am very pleased with your solution though, thank you very much
again for it. I have been able to finish the job now. Brilliant!

Adrian.
**************
 
Hi Adrian,
When arg = o (reading) and reading is done in a try/catch loop
the application will freeze, so I made a slight addition to your
solution, namely added a posting to a log file if arg = 0 and
the file doesn't exist. Should the situation arise, then it will
be easily rectified.

Try this for yourself - it works fine for me:

static void Main()
{
string path = @"c:\__delete_me.txt";

Console.WriteLine("Press [enter] to begin");
Console.ReadLine();

LockAsync(path, FileShare.None, 2000);

using (FileStream stream = OpenWhenReady(path, FileAccess.Read))
{
Console.WriteLine("File Opened: ");
Console.WriteLine();

StreamReader reader = new StreamReader(stream);
Console.WriteLine(reader.ReadToEnd());
}

Console.ReadLine();
}

private static void LockAsync(string path, FileShare share, int
milliseconds)
{
using (EventWaitHandle wait = new EventWaitHandle(false,
EventResetMode.ManualReset))
{
ThreadStart start = delegate()
{
using (FileStream stream = new FileStream(path,
FileMode.OpenOrCreate, FileAccess.Write, share))
{
wait.Set();
System.Threading.Thread.Sleep(milliseconds);

byte[] bytes = Encoding.ASCII.GetBytes(Environment.NewLine +
DateTime.Now.ToString());

stream.Seek(0, SeekOrigin.End);
stream.Write(bytes, 0, bytes.Length);
}
};

start.BeginInvoke(delegate(IAsyncResult result)
{
start.EndInvoke(result);
}, null);

wait.WaitOne();
}
}

public static FileStream OpenWhenReady(string path, FileAccess access)
{
FileStream stream = null;

do
{
try
{
stream = new FileStream(path, FileMode.OpenOrCreate, access);
}
catch (IOException)
{
Console.WriteLine("Waiting for Lock");
System.Threading.Thread.Sleep(500);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Waiting for Lock");
System.Threading.Thread.Sleep(500);
}
}
while (stream == null);

return stream;
}
By the way are you able to solve my other little problem
that I posted concerning shifting the margin a bit to the left
in the print routine?, q.v. my posting if you have time.

Where is it posted?
 
Hi Adrian,

I'm glad to know that it works for you :)

But definitely try the test application that I just posted in a related
response. It works fine for me without freezing or throwing any unhandled
exceptions. If it doesn't work for you then I'd be concerned.
 
Hi Dave,

There are two issues which are separate. (1) I had a problem passing in the
file access parameter. I must have done something wrong. I made a small
change to your code that works fine. (2.) If a file is to be read, that does
not exist, the application will get into an endless loop. I am now logging
if a file is approached for reading that doesn't exist, enabling easy
maintenance to take place. (The system is too big to test it through for
this possible bug cause. I was made aware of this bug because one
application seized up in this way.) I have done all the work now. But I need
to write another application that will interact with the same files. Then I
will give your original code another try, and I will get back to you if I
may.

Adrian.
 
Hi Adrian,
(1) I had a problem passing in the
file access parameter. I must have done something wrong. I made a small
change to your code that works fine.

You shouldn't have had to make any changes to my code, only your own. My
test code works fine, accepting a FileAccess parameter. But if you've
solved the problem, then great :)
2.) If a file is to be read, that does
not exist, the application will get into an endless loop. I am now
logging
if a file is approached for reading that doesn't exist, enabling easy
maintenance to take place. (The system is too big to test it through for
this possible bug cause. I was made aware of this bug because one
application seized up in this way.) I have done all the work now. But I
need
to write another application that will interact with the same files. Then
I
will give your original code another try, and I will get back to you if I
may.

FYI, you should always check File.Exists before attempting to open a file if
you aren't sure whether it will be available, or use
FileAccess.OpenOrCreate, as in the test code that I posted.

There is a bug in my original example since it uses FileAccess.Open (sorry).
I fixed it in the test code that I posted afterwards by changing it to
FileAccess.OpenOrCreate.

If you are going to use FileAccess.Open instead of FileAccess.OpenOrCreate,
you should catch FileNotFoundException as well (since it derives from
IOException it's being caught and swallowed in my original example, which is
the bug).

Here's a revised version of the try..catch block for both my test code and
original examples (fixing some other potential bugs as well):

try
{
stream = new FileStream(path, FileMode.Open, access);
}
catch (PathTooLongException)
{
throw;
}
catch (DirectoryNotFoundException)
{
throw;
}
catch (FileNotFoundException)
{
throw;
}
catch (IOException)
{
System.Threading.Thread.Sleep(500);
}
catch (UnauthorizedAccessException)
{
System.Threading.Thread.Sleep(500);
}
 
Hi Dave,

What do you think of these three points?

1. If a file or a dir is absent then there is not going to be a conflict
between two or more users, because that is impossible. So dir and/or file
could be created and the stream returned without entering a waiting loop.

2. Even if you were to select OpenOrCreate, we are still missing the
"append" option.

3. I would prefer the user not to be confronted with exception messages. So
my preference would be not to have "throw"s.

Adrian.
 
Hi Adrian,
1. If a file or a dir is absent then there is not going to be a conflict
between two or more users, because that is impossible. So dir and/or file
could be created and the stream returned without entering a waiting loop.

The problem with that is inter-process synchronization.

When your code calls File.Exists or Directory.Exists it may return false,
but before your code gets a chance to actually create the file it may be
created by another process, resulting in an exception when your code finally
gets a chance to continue executing. This situation might be unlikely, but
if you don't take it into consideration now you might find that the
exception that results later will seem like an anomaly that can't be fixed.

By trying to create the file without first checking for its existence you're
guaranteed that either:

A. an exception will be thrown if it can't be done
B. the file will be created and you'll have access to the FileStream

In the case that an exception is thrown, you're going to want to try again,
so a loop is required. You could take an Int32 argument that specifies the
maximum number of seconds to wait (retry) until simply failing with a
TimeoutException, but that may not be necessary. It depends on how you'll
be using the method, which files you'll be accessing, how they will be
accessed and by which processes.
2. Even if you were to select OpenOrCreate, we are still missing the
"append" option.

So accept FileMode as an argument and keep the try..catch blocks as I
recommended.
3. I would prefer the user not to be confronted with exception messages.
So
my preference would be not to have "throw"s.

But the situations in which my code throws exceptions are errors that cannot
be handled gracefully, IMO. If the errors occur when using the method you
probably have a bug in the calling code.

What do you propose should happen if a file doesn't exist and FileAccess is
specified as Open?
 
Hi Adrian,

Simple correction:
What do you propose should happen if a file doesn't exist and FileAccess
is specified as Open?

What do you propose should happen if a file doesn't exist and FileMode is
specified as Open?
 
Hi Dave,

Thank you for your thorough explanation, I much appreciate it.
Your points are convincing; I will follow your suggestions and make the
changes.
Many thanks!

Adrian.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top