Thread Sync Problems


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 7 of 7

Thread: Thread Sync Problems

  1. #1
    Join Date
    Aug 2008
    Posts
    3

    Thread Sync Problems

    Hi everyone, really hoping someone can give me a pointer here

    I am using Rad Studio 2007, in the C++ personality

    I need to read streaming data from a com port as fast as i can

    I have created a thread object which uses overlapped IO to read from a com port, and the recieved data is stored in a circular buffer owned by this same thread

    The class has 3 members, 1 private, which is used to copy the recieved data from the com port into the front of the buffer

    2 public, one called DataAvail() which simply returns the current size of the buffer, the other called GetData() which copies data from the tail of the buffer for use by the main process.

    my idea was to use something like the following in the main process:


    Code:
    while (1){
      while (COM->DataAvail() < 200)
      {
        Application->ProcessMessages();
      }
    
    COM->GetData(Buffer, 200);
    }

    Now this works for a short time, but then my thread stops running its Execute() loop and makes the thread object a FFinished = true, i cannot figure out why, i certainly do not stop it and i dont seem to get any info back as to why it has happened

    I think it has to do with syncronising the shared access to the buffer between the main process thread and the com thread, so i have tried using Critical sections around all the buffer code, this didnt help, also i have tried using mutexes around the private function to copy data into the buffer, and the public function to get it out, still no luck


    If i remove the

    COM->GetData(Buffer, 200);

    line from the process, the thread runs forever and i do not get the problem, which is why im sure its a sync issue with the buffer

    Can anyone tell me a way to safely get the data out of a buffer in another thread? or maybe why my thread stops so unexpectedly?

    Im sure its something simple but im lost

    I have included my class (sorry about all the mods but i have tried so many things, and sorry about the long post)

    Kindest regards

    Billy

    Code:
    //---------------------------------------------------------------------------
    
    #ifndef thdCC_ChannelH
    #define thdCC_ChannelH
    #include <Classes.hpp>
    #include "stdio.h"
    
    //Created on the Heap
    #define CC_IN_BUFFER_SIZE	4096
    //Created on the stack
    #define CC_MAX_RX_CHUNK		512
    
    class TCC_Channel : public TThread
    {
    private:
    	HANDLE hSerial;
    	HANDLE hReadEvent;
    
    
    	unsigned int FBaud;
    	unsigned char FComNumber;
    	unsigned int FRXChunkSize;
    	int FLastError;
    	bool FPortState;
    
    	CRITICAL_SECTION FRXBufferLock;
    
    	unsigned char *FInBuffer;
    	unsigned int FInBuffer_Size;
    	unsigned int FInBuffer_In_Pos;
    	unsigned int FInBuffer_Out_Pos;
    
    	bool __fastcall SetCommDefaults(HANDLE hSerial);
    	void __fastcall ShutdownCom(void);
    	void __fastcall CopyBlockToInBuffer(void* Data, unsigned int Length);
    	bool __fastcall SetupAndOpenCom(void);
    	void __fastcall SetRXChunkSize(unsigned int Value) { FRXChunkSize = (Value > CC_MAX_RX_CHUNK) ? CC_MAX_RX_CHUNK : Value; };
    
    protected:
    	void __fastcall Execute();
    public:
    	HANDLE hMutex;
    	__fastcall TCC_Channel(bool CreateSuspended);
    	__fastcall ~TCC_Channel();
    	unsigned int __fastcall DataAvail(void);
    	unsigned int __fastcall GetData(void* Data, unsigned int Length);
    	void __fastcall ClearRecieveBuffer(void);
    
    	__property unsigned int Baud = { read = FBaud, write = FBaud };
    	__property unsigned char ComNumber = { read = FComNumber, write = FComNumber };
    	__property unsigned int RXChunkSize = { read = FRXChunkSize, write = SetRXChunkSize };
    	__property int LastError = { read = FLastError };
    	__property bool PortOpen = { read = FPortState };
    };
    #endif
    
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "thdCC_Channel.h"
    #pragma package(smart_init)
    
    
    __fastcall TCC_Channel::TCC_Channel(bool CreateSuspended)
    	: TThread(CreateSuspended)
    {
    	FLastError = 0;
    	FBaud = 115200;
    	FComNumber = 1;
    	FInBuffer = new unsigned char [CC_IN_BUFFER_SIZE];
    	FInBuffer_Size = 0;
    	FInBuffer_In_Pos = 0;
    	FInBuffer_Out_Pos = 0;
    	FRXChunkSize = 1;
    	FPortState = false;
    
    	//if (!InitializeCriticalSectionAndSpinCount(&FRXBufferLock, 0x80000400))
    	//	MessageDlg("Unable to init RX critical sections", mtError, TMsgDlgButtons() << mbOK, 0);
    
    	//Do we need to boost the thread?
    	//Priority = tpHighest;
        hMutex = CreateMutex(NULL, false, "COM3RXLOCK");
    }
    
    __fastcall TCC_Channel::~TCC_Channel()
    {
    	ShutdownCom();
    
    	DeleteCriticalSection(&FRXBufferLock);
    
    	delete [] FInBuffer;
    }
    
    bool __fastcall TCC_Channel::SetupAndOpenCom(void)
    {
    	AnsiString PipeName = "COM" + IntToStr(FComNumber);
    	hSerial = CreateFile(PipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
    			FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);
    
    	if (hSerial == INVALID_HANDLE_VALUE){
    		FLastError = GetLastError();
    		return false;
    		}
    	//COMMPROP Props;
    	//GetCommProperties(hSerial, &Props);
    
    	SetCommDefaults(hSerial);
    
    	PipeName = "RxEvent";// + IntToStr(FComNumber);
    	hReadEvent = CreateEvent(NULL, true, false, PipeName.c_str());
    	if (!hReadEvent){
    		FLastError = GetLastError();
    		return false;
    		}
    
    	//This is for debug on the buffer
    	memset(FInBuffer, 0x00, CC_IN_BUFFER_SIZE);
    
    	FPortState = true;
    	return true;
    }
    
    void __fastcall TCC_Channel::Execute()
    {
    	OVERLAPPED ovRead;
    	OVERLAPPED ovWrite;
        	DWORD dwBytesRead = 0;
    	DWORD dwBytesWritten = 0;
    	unsigned char RecTemp[CC_MAX_RX_CHUNK];
    
    	memset(&ovRead, 0, sizeof(ovRead));
    
    	if (SetupAndOpenCom())
    		ovRead.hEvent = hReadEvent;
    	else {
    		MessageDlg("Error creating port: " + IntToStr(LastError), mtError, TMsgDlgButtons() << mbOK, 0);
    		return;
    		}
    
    	while(1){
    
    		if (!FPortState){
    			SleepEx(250, false);
    			continue;
    			}
    
    		WaitForSingleObject(hMutex, INFINITE);
    
    
    		// Check if a read is outstanding
    		if (HasOverlappedIoCompleted(&ovRead)){
    
    			//Copy the last read contents
    			GetOverlappedResult(hSerial, &ovRead, &dwBytesRead, false);
    			if (dwBytesRead > 0)
    				CopyBlockToInBuffer(RecTemp, dwBytesRead);
    			// Issue a serial port read
    			if (!ReadFile(hSerial, RecTemp, FRXChunkSize, &dwBytesRead, &ovRead)){
    
    				FLastError = GetLastError();
    				if (FLastError != ERROR_IO_PENDING){
    					ShutdownCom();
    					MessageDlg("RX Error, comm shutting down", mtError, TMsgDlgButtons() << mbOK, 0);
    					return;
    					}
    				}
    			}
    
    
    		ReleaseMutex(hMutex);
    		}
    }
    
    void __fastcall TCC_Channel::ShutdownCom(void)
    {
        	ClearRecieveBuffer();
    	CloseHandle(hSerial);
    	CloseHandle(hReadEvent);
    	FPortState = false;
    }
    
    bool __fastcall TCC_Channel::SetCommDefaults(HANDLE hSerial)
    {
    	DCB dcb;
    	COMMTIMEOUTS commtimeouts;
    
    	memset(&dcb,0,sizeof(dcb));
    	dcb.DCBlength = sizeof(dcb);
    	if (!GetCommState(hSerial,&dcb))
    		return FALSE;
    
    	dcb.BaudRate = FBaud;
    	dcb.ByteSize = 8;
    	dcb.Parity = 0;
    	dcb.StopBits = ONESTOPBIT;
    
    	if (!SetCommState(hSerial, &dcb))
    		return FALSE;
    
    	commtimeouts.ReadIntervalTimeout = 3000;
    	if (!SetCommTimeouts(hSerial, &commtimeouts))
    		return FALSE;
    
    	return TRUE;   
    }
    
    void __fastcall TCC_Channel::ClearRecieveBuffer(void)
    {
    	PurgeComm(hSerial, PURGE_RXABORT | PURGE_RXCLEAR);
    	EnterCriticalSection(&FRXBufferLock);
    	try {
    		FInBuffer_Size = FInBuffer_In_Pos = FInBuffer_Out_Pos = 0;
    		}
    	__finally {
    		LeaveCriticalSection(&FRXBufferLock);
    		}
    };
    
    unsigned int __fastcall TCC_Channel::GetData(void* Data, unsigned int Length)
    {
    	//EnterCriticalSection(&FRXBufferLock);
    	//WaitForSingleObject(hMutex, INFINITE);
    
    	unsigned int SizeModifier = FInBuffer_Size;
    
    	try {
    
    		if (Length > SizeModifier)
    			Length = SizeModifier;
    
    		if ((FInBuffer_Out_Pos + Length) < CC_IN_BUFFER_SIZE){
    			memcpy(Data, &FInBuffer[FInBuffer_Out_Pos], Length);
    			FInBuffer_Out_Pos += Length;
    			SizeModifier = Length;
    			}
    		else {
    			int Remain = (CC_IN_BUFFER_SIZE - FInBuffer_Out_Pos);
    			memcpy(Data, &FInBuffer[FInBuffer_Out_Pos], Remain);
    			FInBuffer_Out_Pos = 0;
    			SizeModifier = Remain;
    			int Remain2 = (Length - Remain);
    
    			if (Remain2 > 0){
    				memcpy((unsigned char*)Data + Remain, &FInBuffer[FInBuffer_Out_Pos], Remain2);
    				FInBuffer_Out_Pos = Remain2;
    				SizeModifier += Remain2;
    				}
    			}
    
    		FInBuffer_Size -= SizeModifier;
    		}
    	__finally {
    		//LeaveCriticalSection(&FRXBufferLock);
    		//ReleaseMutex(hMutex);
    		}
    
    	return Length;
    }
    
    unsigned int __fastcall TCC_Channel::DataAvail(void)
    {
    	WaitForSingleObject(hMutex, INFINITE);
    
    	unsigned int val;
    	//EnterCriticalSection(&FRXBufferLock);
    	try {
    		val = FInBuffer_Size;
    		}
    	__finally {
    		//LeaveCriticalSection(&FRXBufferLock);
    		}
    	ReleaseMutex(hMutex);
    	return val;
    }
    
    void __fastcall TCC_Channel::CopyBlockToInBuffer(void* Data, unsigned int Length)
    {
    	//EnterCriticalSection(&FRXBufferLock);
    	//WaitForSingleObject(hMutex, INFINITE);
    
    	//try {
    		unsigned int SpaceAvailable = CC_IN_BUFFER_SIZE - FInBuffer_Size;
    		unsigned int SizeModifier;
    
    		if ((FInBuffer_In_Pos + Length) < CC_IN_BUFFER_SIZE){
    			memcpy(&FInBuffer[FInBuffer_In_Pos], Data, Length);
    			FInBuffer_In_Pos += Length;
    			SizeModifier = Length;
    			}
    		else {
    			memcpy(&FInBuffer[FInBuffer_In_Pos], Data, SpaceAvailable);
    			FInBuffer_In_Pos = 0;
    			SizeModifier = SpaceAvailable;
    			int Remain = (Length - SpaceAvailable);
    
    			if (Remain > 0){
    				memcpy(&FInBuffer[FInBuffer_In_Pos], (unsigned char*)Data + SpaceAvailable, Remain);
    				FInBuffer_In_Pos = Remain;
    				SizeModifier += Length;
    				}
    			}
    
    		if (SizeModifier > CC_IN_BUFFER_SIZE){
    			FInBuffer_Size = CC_IN_BUFFER_SIZE;
    			FInBuffer_Out_Pos = FInBuffer_In_Pos + 1;
    			}
    		else
    			FInBuffer_Size += SizeModifier;
    		//}
    	//__finally {
    	   //	ReleaseMutex(hMutex);
    		//LeaveCriticalSection(&FRXBufferLock);
    	   //	}
    }

  2. #2
    Join Date
    Dec 2003
    Posts
    3,366
    I looked all over for a FFinished = true and did not see it (?!) so I do not understand what you are saying about a thread finishing or something.

    I had two thoughts that may be creating chaos for you: if the machine is multiprocessor (or multi core) then creating a thread means you have code running a the same time, as opposed to classical threading where only one process is truly active at one time. Is there anything in your code that cannot handle this? For example using the buffer while writing to it, or reading and writing to the serial port at the same time, etc?

    The other thing is to make sure it is working in a simpler setup. You probably did all this but strip it down to a read thread and do nothing main that kicks the thread off, make sure that much is working then build it back up in pieces. You have to *know* that the pieces work individually before threading, so when you thread it up you can be sure that the mistake is in the threading and not the individual blocks.

    If you can clarify what you meant about ffinished I will look at the code again. If you think it is a sync issue, you will want a mutex and such. Stop the reader thread, get the data, and reactivate the reader or flip flop 2 buffers (read into a, flip, read into b use a to process, flip ....). Its a serial port so you should not run out of time on a modern machine if you just stop reading long enough to copy the data out. The port has a buffer that should hold several miliseconds worth of data, and a milisecond is a very long time on a 3ghz processor...
    Last edited by jonnin; 08-11-2008 at 09:33 AM.

  3. #3
    Join Date
    Aug 2008
    Posts
    3
    Hi Jonnin

    Quote Originally Posted by jonnin
    I looked all over for a FFinished = true and did not see it (?!) so I do not understand what you are saying about a thread finishing or something.
    Sorry, FFinished is a private member of the VCL's TThread object, this is the only way i found out what was happening, after the execute loop seems to stop running unexpectedly, i inspect the TThread object and notice the FFinished member has been set - but how it gets set is the real mystery


    Quote Originally Posted by jonnin
    I had two thoughts that may be creating chaos for you: if the machine is multiprocessor (or multi core) then creating a thread means you have code running a the same time
    Yes this has been on my mind too, which is what has led me to the origional question of how to actually exchange data between threads totally thread-risk free, ive tried mutexes and critical sections, now im experimenting with the Syncronize method, still no luck - possible a thread safe named pipe or something?!

    But yes, im sure its a access collision with the buffer somewhere, its not giving anything away tho it seems

    if i remove the COM_>GetData() call, then all is well - this one line kills it, and that call goes straight to the buffer management routine in the class

    Thanks for you help, im so stuck with this it seems

  4. #4
    Join Date
    Aug 2008
    Posts
    3
    Hi,

    I found the problem and have fixed that part, now the thread doesnt 'finish' and sure enough it was in the buffer sharing routines

    but the multi-core part does raise a paranoia question for me - how can we have anything atomic in a truly parrellel processing environment - i mean, even two independent threads trying to open the same mutex at the same time 'could' end up with both being granted possibly?!

    billy

  5. #5
    Join Date
    Nov 2003
    Posts
    4,118
    synchronization objects such as a mutex, critical section etc use intrinsic locking mechanisms to avoid race conditions so there shouldn't be a problem using them on a multicore machine. Other types have to be used either in a thread-local manner (that is, each thread gets a private instance of a certain object) or using a synchronized access.
    Danny Kalev

  6. #6
    Join Date
    Dec 2003
    Posts
    3,366
    yes, trust the thread's implementation to have covered this. While multiple core computers are newish, multi processor computers are ancient -- there were many of them in the early 80s and home users could get at them in the early 90s -- so you can trust that the issue has been thought out by the thread library designers.

    Windows supported it way back in NT 4 (maybe even NT 3?) for the few folks with 2 processor motherboards (mostly, engineering workstations had 2-4 processors).

  7. #7
    Join Date
    Mar 2009
    Posts
    1
    Quote Originally Posted by billysdomain View Post
    Hi,

    I found the problem and have fixed that part, now the thread doesnt 'finish' and sure enough it was in the buffer sharing routines

    billy
    I have the same exact problem.
    What do you have fixed the problem?
    Giorgio

Similar Threads

  1. Problem with thread priorities
    By erray in forum C++
    Replies: 8
    Last Post: 10-11-2007, 04:17 AM
  2. Replies: 0
    Last Post: 01-13-2006, 07:55 AM
  3. Thread problems
    By Phaelax in forum Java
    Replies: 0
    Last Post: 08-31-2005, 04:05 AM
  4. applet and thread problems
    By aneesha in forum Java
    Replies: 0
    Last Post: 05-06-2000, 10:38 PM
  5. Applet and thread problems
    By Aneesha in forum Java
    Replies: 0
    Last Post: 05-06-2000, 10:36 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center