C++/CLI, VS2005, .NET framework (3.0)
Hi,
What I want to do:
Write a software watchdog, and log whenever a module fails to respond.
Problem:
Threads collide when they try to write to the log.
Error:
Unhandled Exception: System.IO.IOException: The process cannot access the file '
C:\test\watchdog\debug\log.txt' because it is being used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, I
nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions o
ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share)
at watchdog.loggerFunction.WriteFile(String ) in c:\test\watchdog\watchdog\loggerFunction.cpp:line 74
at watchdog.TimerThread.WorkerThread(Object Name) in c:\test\watchdog\watchdog\watchdogthread.cpp:line 27
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart(Object obj)
Code: File access
I think its in this function I should use monitor/lock or a semaphore (even tried with [synchronization])
but not 100% shure how to use them.
namespace watchdog{
void loggerFunction::WriteFile(String^ logentry) {
// where and what should I use to avoid collisions
Monitor::Enter(LoggerFunction::typeid);
String^ logname = "Log";
String^ filenameextension= ".txt";
String^ filename;
bool writelogheader = false;
filename = logname + filenameextension ;
if (!File::Exists(filename)) {
writelogheader = true;
}
FileStream^ filestream = gcnew FileStream(filename, FileMode::OpenOrCreate, FileAccess::ReadWrite,FileShare::ReadWrite); // open or create
StreamWriter^ streamwrite = gcnew StreamWriter(filestream);
{
streamwrite->BaseStream->Seek(0, SeekOrigin::End);
if (writelogheader) { // write header for new file
streamwrite->Write("DATE: TIME: Log Entry:\r\n");
streamwrite->Write("------------------------------------\r\n");
}
//formatting DateTime, last digits is 100th of a second
streamwrite->Write("{0}", DateTime::Now.ToString("yyyyMMdd hh:mm:ss:ff"));
streamwrite->Write(" ");
streamwrite->Write(String::Concat(logentry , "\r\n"));
streamwrite->Flush();
streamwrite->Close();
filestream->Close();
}
Monitor::Exit(LoggerFunction::typeid);
}
};
Code: thread.h
namespace watchdog{
using namespace System;
public ref class TimerThread {
public:
void WorkerThread(Object^ Name);
};
public value class Alive{
public:
String^ MyName;
int SleepTimeMS;
int WhoAmI;
};
}
Code: thread.cpp
namespace watchdog {
void TimerThread::WorkerThread(Object^ Name){
Alive^ name = (Alive^) Name;
LoggerFunction^ functions = gcnew LoggerFunction;
if ( Thread::CurrentThread->Name == nullptr )
{ // to avoid invalid operation exception
Thread::CurrentThread->Name = name->ModuleName;
}
while (true) {
Thread::Sleep(name->SleepTimeMS); //Begin napping.
if(IAmAlive == false) {
functions->WriteFile(name->ModuleName);
// do more work and finally shutting down
Thread::Sleep(Timeout::Infinite);
}
else{
IAmAlive = false;
// set IAmAlive to false and sleep
// hopefully before waking up a
// message has set it to true
}
}
}
}
Code Main.c
namespace watchdog {
public ref class cMain {
public:
static void pMain(){
// init values
Alive AliveCheck;
LoggerFunction^ functions = gcnew LoggerFunction;
TimerThread^ T = gcnew TimerThread;
AliveCheck.SleepTimeMS = 2000;
AliveCheck.ModuleName = "the first";
AliveCheck.WhoAmI = 0;
Thread^ first =
gcnew Thread(gcnew ParameterizedThreadStart(T, &TimerThread::WorkerThread));
first->Start(AliveCheck);
// add more threads until i have nine
AliveCheck.SleepTimeMS = 2000;
AliveCheck.ModuleName = "the ninth";
AliveCheck.WhoAmI = 9;
Thread^ nine =
gcnew Thread(gcnew ParameterizedThreadStart(T, &TimerThread::WorkerThread));
nine->Start(AliveCheck);
}
};
}
Hints, helpful pointers or code is much appriciated.
Cheers,
Andreas