Raised exception in the case of concurrent file access with StreamReader - c#

I found a post talking about handling concurrent file access with StreamWriter.
The problem is that the answers do not manage the scenario where the file is being accessed but multiple processes.
Let's tell it shortly :
I have multiple applications
I need a centralised logging system in dataBase
If database fail, I need a fallback on a file system log
There is a known concurrency scenario, where multiple applications (processes) will try to write in that file.
This can be managed by re-attempt the writing after a short delay.
But I don't want ot reattempt if it's a security error or filename syntax error.
The code is here :
// true if an access error occured
bool accessError = false;
// number fo writing attemps
int attempts = 0;
do
{
try
{
// open the file
using (StreamWriter file = new StreamWriter(filename, true))
{
// write the line
file.WriteLine(log);
// success
result = true;
}
}
/////////////// access errors ///////////////
catch (ArgumentException)
{
accessError = true;
}
catch (DirectoryNotFoundException)
{
accessError = true;
}
catch (PathTooLongException)
{
accessError = true;
}
catch (SecurityException)
{
accessError = true;
}
/////////////// concurrent writing errors ///////////////
catch (Exception)
{
// WHAT EXCEPTION SHOULD I CATCH HERE ?
// sleep before retrying
Thread.Sleep(ConcurrentWriteDelay);
}
finally
{
attempts++;
}
// while the number of attemps has not been reached
} while ((attempts < ConcurrentWriteAttempts)
// while we have no access error
&& !accessError
// while the log is not written
&& !result);
My only question is the type of exception that will be raised in the case of concurrency writting. I already know things can be done differently. Let me add a few considerations :
No, I don't want to use NLog in that scenario
Yes I handle concurrency with IOC + Mutex for the in-process concurrency
Yes I really want all log to be written in the same file

It will be an IOException with text:
"The process cannot access the file '{0}' because it is being used by another process."
This is a simplistic approach:
static bool LogError(string filename, string log)
{
const int MAX_RETRY = 10;
const int DELAY_MS = 1000; // 1 second
bool result = false;
int retry = 0;
bool keepRetry = true;
while (keepRetry && !result && retry < MAX_RETRY )
{
try
{
using (StreamWriter file = new StreamWriter(filename, true))
{
// write the line
file.WriteLine(log);
// success
result = true;
}
}
catch (IOException ioException)
{
Thread.Sleep(DELAY_MS);
retry++;
}
catch (Exception e)
{
keepRetry = false;
}
}
return result;
}

Related

How can I gracefully terminate a BLOCKED thread?

There are plenty of places that deal with terminating C# threads gracefully. However, they rely on a loop or if condition executing inside a loop, which assumes that this statement will be executed frequently; thus, when the stop bool flag is set, the thread exits quickly.
What if I have a thread in which this is not true? In my case, this is a thread set up to receive from a server, which frequently blocks on a call to read data from the input stream, where none is yet provided so it waits.
Here is the thread in question's loop:
while (true)
{
if (EndThread || Commands.EndRcvThread)
{
Console.WriteLine("Ending thread.");
return;
}
data = "";
received = new byte[4096];
int bytesRead = 0;
try
{
bytesRead = stream.Read(received, 0, 4096);
}
catch (Exception e)
{
Output.Message(ConsoleColor.DarkRed, "Could not get a response from the server.");
if (e.GetType() == Type.GetType("System.IO.IOException"))
{
Output.Message(ConsoleColor.DarkRed, "It is likely that the server has shut down.");
}
}
if (bytesRead == 0)
{
break;
}
int endIndex = received.Length - 1;
while (endIndex >= 0 && received[endIndex] == 0)
{
endIndex--;
}
byte[] finalMessage = new byte[endIndex + 1];
Array.Copy(received, 0, finalMessage, 0, endIndex + 1);
data = Encoding.ASCII.GetString(finalMessage);
try
{
ProcessMessage(data);
}
catch (Exception e)
{
Output.Message(ConsoleColor.DarkRed, "Could not process the server's response (" + data + "): " + e.Message);
}
}
The if statement at the top of the block does what the usual stopping-a-thread-gracefully setup does: checks a flag, terminates the thread if it's set. However, this thread is usually to be found waiting a few lines further down, at stream.Read.
Given this, is there any way to gracefully terminate this thread (i.e. no Aborting), and clean up its resources (there's a client that needs to be closed)?
Assuming you can use async / Tasks, the way to do clean stopping of async and IO operations is with a CancelationToken that is connected to a CancelationTokenSource. The below code snippet illustrates a simplified example of its usage when applied to a simplified version of your code.
class MyNetworkThingy
{
public async Task ReceiveAndProcessStuffUntilCancelled(Stream stream, CancellationToken token)
{
var received = new byte[4096];
while (!token.IsCancellationRequested)
{
try
{
var bytesRead = await stream.ReadAsync(received, 0, 4096, token);
if (bytesRead == 0 || !DoMessageProcessing(received, bytesRead))
break; // done.
}
catch (OperationCanceledException)
{
break; // operation was canceled.
}
catch (Exception e)
{
// report error & decide if you want to give up or retry.
}
}
}
private bool DoMessageProcessing(byte[] buffer, int nBytes)
{
try
{
// Your processing code.
// You could also make this async in case it does any I/O.
return true;
}
catch (Exception e)
{
// report error, and decide what to do.
// return false if the task should not
// continue.
return false;
}
}
}
class Program
{
public static void Main(params string[] args)
{
using (var cancelSource = new CancellationTokenSource())
using (var myStream = /* create the stream */)
{
var receive = new MyNetworkThingy().ReceiveAndProcessStuffUntilCancelled(myStream, cancelSource.Token);
Console.WriteLine("Press <ENTER> to stop");
Console.ReadLine();
cancelSource.Cancel();
receive.Wait();
}
}
}
.

c# BackgroundWorker pause

I have a small C# application that communicate with a server and get some data via API request, using POST method. It is an Apache server by the way.
My problem is that my C# app sends a tons of requests continuously, and the server creates a tons of log files.
I use a BackgroundWorker and I want to pause it for a few seconds, but Thread.Sleep(5000) doesn't working.
This app is running in the system tray it doesn't have a GUI, just get some content, and print them out.
Code:
private void _bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
JSONParser parser = new JSONParser(_config.Prop.server + "content/api.php", "print", "getAll");
try
{
while (!_bgWorker.CancellationPending)
{
try
{
JSONPrintNeeds needs = parser.DownloadAll();
List<JSONPrintNeed> temp = new List<JSONPrintNeed>();
foreach (JSONPrintNeed need in needs.data)
{
temp.Add(need);
}
foreach (JSONPrintNeed need in temp)
{
Printer printer = new Printer(need.megrendeles);
printer.PrintController = new StandardPrintController();
List<String> installed = new List<String>();
for (int i = 0; i < PrinterSettings.InstalledPrinters.Count; i++)
{
installed.Add(PrinterSettings.InstalledPrinters[i]);
}
if (installed.Contains(need.nyomtato))
{
printer.PrinterSettings.PrinterName = need.nyomtato;
}
int format = int.Parse(need.format);
switch (format)
{
case 0:
default: // txt
printer.Print();
break;
case 1: // html
SetDefaultPrinter(need.nyomtato);
browser.DocumentText = need.megrendeles;
browser.Print();
break;
}
JSONResult result = parser.DeleteOne(int.Parse(need.ny_id));
}
parser.DeleteAll();
Thread.Sleep(5000);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
you can use EventWaitHandle for sync threads.
EventWaitHandle flag = new EventWaitHandle(false, EventResetMode.AutoReset);
if (stop)
{
flag.WaitOne(5000);
}

How to implement "retry/abort" mechanism for writing files that may be used by another process?

This is really short question. I don't understand try-catch mechanism completely.
This is my current code:
public static void WriteText(string filename, string text)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
}
catch(Exception exc)
{
MessageBox.Show("File is probably locked by another process.");
}
}
Background:
Im writing application that shares configuration files with another application.
I need some dialog messagebox with "retry" and "abort" buttons, when that file is used by other application. When that message will appear - I will close that other application and I will try to rewrite that file again by pressing "Retry" button.
Whatr we have is using a counter for re-tries and possibly a thread sleep.
So something like
int tries = 0;
bool completed = false;
while (!completed)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
completed = true;
}
catch(Exception exc)
{
tries++;
//You could possibly put a thread sleep here
if (tries == 5)
throw;
}
}
Even though there's a good answer already I'll submit one that's more tuned towards the OP's question (let the user decide instead of using a counter).
public static void WriteText(string filename, string text)
{
bool retry = true;
while (retry)
{
try
{
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
retry=false;
}
catch(Exception exc)
{
MessageBox.Show("File is probably locked by another process.");
// change your message box to have a yes or no choice
// yes doesn't nothing, no sets retry to false
}
}
}
If you need more info on how to implement the messagebox check out the following links;
http://msdn.microsoft.com/en-us/library/0x49kd7z.aspx
MessageBox Buttons?
I would do it like that:
public static void WriteText(string filename, string text, int numberOfTry = 3, Exception ex = null)
{
if (numberOfTry <= 0)
throw new Exception("File Canot be copied", ex);
try
{
var file = new System.IO.StreamWriter(filename);
file.Write(text);
file.Close();
}
catch (Exception exc)
{
WriteText(filename,text,--numberOfTry,ex);
}
}
I like it more like this (example tries to save a RichTextBox on close and allows retrying save or aborting close):
protected override void OnClosing(CancelEventArgs e)
{
if (richTextBox_Query.Modified)
{
DialogResult result;
do
try
{
richTextBox_Query.SaveFile(
Path.ChangeExtension(Application.ExecutablePath, "sql"),
RichTextBoxStreamType.UnicodePlainText);
result = DialogResult.OK;
richTextBox_Query.Modified = false;
}
catch (Exception ex)
{
result = MessageBox.Show(ex.ToString(), "Exception while saving sql query",
MessageBoxButtons.AbortRetryIgnore);
e.Cancel = result == DialogResult.Abort;
}
while (result == DialogResult.Retry);
}
base.OnClosing(e);
}

Wait until resource is available?

Is there a good way to do this code, (to wait until this file is unlocked) when I get:
The process cannot access the file because it is being used by another process
I'm working on a web app so I can have concurrent access to this file from several different applications.
bool isLock = false;
do
{
try
{
File.WriteAllText(path, value, Encoding.Unicode);
isLock = false;
}
catch
{
Thread.Sleep(30);
isLock = true;
}
}while (isLock);
Your lock variable is of no use when multiple applications are in the scenario.
Rather test if you can File.OpenWrite and then write to the file.
If you cannot access the file loop and wait or write to a temporary file and start another thread looping and waiting until the temporary file can be merged.
Maybe even better would be to store immediatley to temp and let a watchdog write to your storage file.
public void SomeWhereInYourWebServerApplication
{
FileSystemWatcher fsw = new FileSystemWatcher("tempfolder");
fsw.Created += fsw_Created;
// save new entries to tempfolder
}
void fsw_Created(object sender, FileSystemEventArgs e)
{
foreach (string file in Directory.GetFiles("tempfolder"))
{
try
{
string fileText = File.ReadAllText(file);
File.AppendAllText("myStorage.txt", fileText);
File.Delete(file);
}
catch
{
// for me it's ok when we try to append the file the next time
// something new is coming
}
}
}
Nice and simple as i think.
Don't forget to do proper exception handling when files are involved.
If you absolutely must do it this way, and you have no control over the apps that are using the file, then your code is almost there.
It just needs to be made slightly more robust:
public static bool TryWriteText(string path, string text, TimeSpan timeout)
{
Contract.Requires(path != null); // Or replace with: if (path == null) throw new ArgumentNullException("path");
Contract.Requires(text != null); // Or replace with: if (text == null) throw new ArgumentNullException("text");
Stopwatch stopwatch = Stopwatch.StartNew();
while (stopwatch.Elapsed < timeout)
{
try
{
File.WriteAllText(path, text);
return true;
}
catch (IOException){} // Ignore IOExceptions until we time out, then return false.
Thread.Sleep(100); // 100ms is rather short - it could easily be 1000 I think.
} // Perhaps this should be passed in as a parameter.
return false;
}
Alternative version that rethrows the last IOException on timeout (this is arguably better since you don't hide all the exceptions):
public static void TryWriteText(string path, string text, TimeSpan timeout)
{
Contract.Requires(path != null); // Or replace with: if (path == null) throw new ArgumentNullException("path");
Contract.Requires(text != null); // Or replace with: if (text == null) throw new ArgumentNullException("text");
Stopwatch stopwatch = Stopwatch.StartNew();
while (true)
{
try
{
File.WriteAllText(path, text);
}
catch (IOException)
{
if (stopwatch.Elapsed > timeout)
throw;
}
Thread.Sleep(100);
}
}
But you should only use such code as a last resort.

Better way to write retry logic without goto [duplicate]

This question already has answers here:
Cleanest way to write retry logic?
(30 answers)
Closed 9 years ago.
Is there a better way to write this code without using goto? It seems awkward, but I can't think of a better way. I need to be able to perform one retry attempt, but I don't want to duplicate any code.
public void Write(string body)
{
bool retry = false;
RetryPoint:
try
{
m_Outputfile.Write(body);
m_Outputfile.Flush();
}
catch (Exception)
{
if( retry )
throw;
// try to re-open the file...
m_Outputfile = new StreamWriter(m_Filepath, true);
retry = true;
goto RetryPoint;
}
}
Here is the basic logic that I would use instead of a goto statement:
bool succeeded = false;
int tries = 2;
do
{
try
{
m_Outputfile = new StreamWriter(m_Filepath, true);
m_Outputfile.Write(body);
m_Outputfile.Flush();
succeeded = true;
}
catch(Exception)
{
tries--;
}
}
while (!succeeded && tries > 0);
I just added # of tries logic, even though the original question didn't have any.
#Michael's answer (with a correctly implemented out catch block) is probably the easiest to use in your case, and is the simplest. But in the interest of presenting alternatives, here is a version that factors the "retry" flow control into a separate method:
// define a flow control method that performs an action, with an optional retry
public static void WithRetry( Action action, Action recovery )
{
try {
action();
}
catch (Exception) {
recovery();
action();
}
}
public void Send(string body)
{
WithRetry(() =>
// action logic:
{
m_Outputfile.Write(body);
m_Outputfile.Flush();
},
// retry logic:
() =>
{
m_Outputfile = new StreamWriter(m_Filepath, true);
});
}
You could, of course, improve this with things like retry counts, better error propagation, and so on.
Michael's solution doesn't quite fulfill the requirements, which are to retry a fixed number of times, throwing the last failure.
For this, I would recommend a simple for loop, counting down. If you succeed, exit with break (or, if convenient, return). Otherwise, let the catch check to see if the index is down to 0. If so, rethrow instead of logging or ignoring.
public void Write(string body, bool retryOnError)
{
for (int tries = MaxRetries; tries >= 0; tries--)
{
try
{
_outputfile.Write(body);
_outputfile.Flush();
break;
}
catch (Exception)
{
if (tries == 0)
throw;
_outputfile.Close();
_outputfile = new StreamWriter(_filepath, true);
}
}
}
In the example above, a return would have been fine, but I wanted to show the general case.
What if you put it in a loop? Something similar to this, maybe.
while(tryToOpenFile)
{
try
{
//some code
}
catch
{
}
finally
{
//set tryToOpenFile to false when you need to break
}
}
public void Write(string body, bool retryOnError)
{
try
{
m_Outputfile.Write(body);
m_Outputfile.Flush();
}
catch (Exception)
{
if(!retryOnError)
throw;
// try to re-open the file...
m_Outputfile = new StreamWriter(m_Filepath, true);
Write(body, false);
}
}
Try something like the following:
int tryCount = 0;
bool succeeded = false;
while(!succeeded && tryCount<2){
tryCount++;
try{
//interesting stuff here that may fail.
succeeded=true;
} catch {
}
}
with a boolean
public void Write(string body)
{
bool NotFailedOnce = true;
while (true)
{
try
{
_outputfile.Write(body);
_outputfile.Flush();
return;
}
catch (Exception)
{
NotFailedOnce = !NotFailedOnce;
if (NotFailedOnce)
{
throw;
}
else
{
m_Outputfile = new StreamWriter(m_Filepath, true);
}
}
}
}

Categories

Resources