Dr_avalanche

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.

Code Snippet

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

Code Snippet

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

Code Snippet

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

Code Snippet

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



Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Bruno van Dooren

What you want is very easy: simply use an attribute to make sure that only one thread accesses your watchdog object at a time.

http://www.dotnet247.com/247reference/articles/0/4543.aspx

That way you can solve the problem without having to write any syncrhonization code yourself.





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Dr_avalanche

Thanks,

uhm that link leads me to nowhere, do I have to register somewhere to see that

Cheers,

/Andreas





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Bruno van Dooren

Sorry about that. The link was working yesterday.

Use this one instead:

http://msdn2.microsoft.com/en-us/library/system.runtime.remoting.contexts.synchronizationattribute.aspx





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Dr_avalanche

Hi,

Ok so I changed the code. I added a constructor, but the compiler still complains about not having a "proper"

constructor, iĦŻve probably misunderstood something here.

Error:

1>.\functions.cpp(19) : error C2512: 'System::Runtime::Remoting::Contexts::ContextAttribute::ContextAttribute' : no appropriate default constructor available

Code Snippet

//.h

namespace watchdog {

[Synchronization]

public ref class loggerFunction: public ContextAttribute {

public:

loggerFunction();

//~loggerFunction();

void WriteFile( String^ );

// ..

// and some more functions

//.cpp

namespace watchdog {

loggerFunction::loggerFunction()

{

}

// writefile declaration and other functions
}

any pointers would be helpful.

Cheers,

/Andreas





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Bruno van Dooren

I think the problem is that you inherit from ContextAttribute. You should use ContextBoundObject instead:

Code Snippet

using namespace System;

using namespace System::Runtime::Remoting::Contexts;

namespace ClassLib {

[Synchronization]

public ref class Class1 : public ContextBoundObject

{

// TODO: Add your methods for this class here.

};

}





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Dr_avalanche

Hi,

Ah thanks, ofc it should be ContextBoundobject, but even with that change i get exceptions thrown

Error:

Server stack trace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access,
nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions
ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share)
at watchdog.WatchdogFunctions.WriteFile(String ) in c:\test\watchdog\watchdog\loggerfunction.cpp:line 81
at System.Runtime.Remoting.Messaging.Message.Dispatch(Object target, Boolean
fExecuteInContext)
at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMe
sage msg, Int32 methodPtr, Boolean fExecuteInContext)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage re
Msg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgD
ta, Int32 type)
at watchdog.WatchdogFunctions.WriteFile(String )
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,
ontextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart(Object obj)
System.IO.IOException: The process cannot access the file 'C:\\test\watchdog\debug\log.txt' because i
is being used by another process.

Or is it working as intended

Cheers,

/Anders





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Bruno van Dooren

Dr_avalanche wrote:


System.IO.IOException: The process cannot access the file 'C:\\test\watchdog\debug\log.txt' because i
is being used by another process.

The logfile you are using is opened by another process. Writing with 2 processes into 1 file is not a good idea (apart from the fact that it doesn't work of course :-))

If you want multiple processes to use the same log, perhaps it would be a solution create a logger service, and have all processes send their log info to that service. If you implement a stream interface to do that you can make it as easy as logging to file.

If you don't use multiple processes, perhaps there was still an active process from another debugging session still holding a handle to the logfile





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Dr_avalanche

Hi,

Yep, I thought it was possibly to solve it this way with Monitor/Synchronization or

critical section or the likes of them. Thus stopping the different processes from using the same function

simultaneously. But its seems iĦŻve misunderstood the use of these.

About creating a loggerservice, are there any .net 3.0 cli/c++ expamples or articles about it close at hand

I will search for it, but if anyone have a link on the top of their heads spill the beans Smile

Nothing fancy I just want to log a few strings.

... and thanks for your help so far Bruno van Dooren!

/Cheers

/Andreas





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Bruno van Dooren

No Problem.

'Windows service' is one of the standard VC C++/CLI project templates you can use to create a new service project.

I don't have any C++/CLI service examples at hand, but if you go to www.codeproject.com you should be able to find plenty of them. if you don't find C++/CLI service examples, then use a C# service example and see how it works. translating from C# to C++/CLI is usually just a syntax change so it should be easy enough to get started even if you don't find a C++/CLI service immediately.





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Dr_avalanche

Hi,

What are the alternatives to making a 'windows service' For logging I could use Windows event log, right

I have other functions that can collide since all the 9 threads (processes) is monitoring one module each

and will write to the log, send a mail and then reboot. although uncommon but possible they can all try to write

tot he log and this is wher ei am now - mailing and writing collides.

Should I write to an array and then dump it to file and mail on regular basis

Cheers,

/Andreas





Re: Visual C++ Language Multithreading, Syncronisation problem, how to use Monitor::Enter() or what to use?

Bruno van Dooren

If you need to log only occasional events (i.e. you don't generate a continuous stream of data) then the EventLog class is what you need. It allows you to write to the windows eventlog in a safe way. meaning you can write to the event lof form within multiple processes and not have concurrency conflicts.