Re: interthread communication
On 20 Aug., 19:38, Joseph M. Newcomer <newco...@flounder.com> wrote:
On Fri, 20 Aug 2010 06:23:49 -0700 (PDT), mfc <mfcp...@googlemail.com> wr=
ote:
Hi,
I`ve one design question: I created one user-interface-thread for a
serial-port to write some data to the port.
Unfortunately, it seems that I have to use two user-interface threads
to get that what I want.
The first thread has a message-map, where I`ve installed a message
(ON_THREAD_MESSAGE(UWM_GET_DATA, OnGetData)). This message will be
called if some new data will be received by the ethernet connection.
void CSerialCollectDataTh::OnGetData(WPARAM wParam, LPARAM lParam)
{
if(the first call)
{
//generate second ui-thread for sending data by the serial p=
ort
m_TxSerialThread = (CSerialWrite
*)AfxBeginThread(RUNTIME_CLASS(CSerialWrite),
THREAD_PRIORITY_NORMAL, // priority =
0, //
EATE_SUSPENDED);
if ( m_TxSerialThread == NULL) return;
m_TxSerialThread ->ResumeThread();
m_TxSerialThread->PostThreadMessage(UWM_START_TX_DMX,
(WPARAM)SerialHandle, (LPARAM)0);
****
Note that this defaults the WPARAM and LPARAM, so you don't need to speci=
fy the LPARAM.
Since this is a writer thread, typically you will PostThreadMessage to it=
for each packet
you want to write.
There is a very serious defect here; you are expecting to be able to Post=
ThreadMessage
immediately after an AfxBeginThread, but this cannot possibly work. Wh=
y? Because the
thread may not be running yet! And until the thread runs, and creates =
its message queue,
PostThreadMessage will fail, so most likely nothing will happen. Inste=
ad, you have to do
something like
BOOL CSerialWrite::InitInstance()
{
MSG msg;
::PeekMessage(&msg, 0, 0, NULL, PM_NOREMOVE);
target->PostMessage(UWM_QUEUE_EXISTS);
...
}
Now, the ::PeekMesage call has created the queue, so you know that you ca=
n send the thread
messages; you cannot do ANY PostThreadMessage until you receive the UWM_Q=
UEUE_EXISTS
message. You have tried to solve a concurrent problem thinking it is a=
sequential problem
relating to syntax, and it is not.
****
thank you for this comment. I`ll update my code; until now, the
message was always received, but I will solve this problem.
}
//store the ethernet data in a clist or carray
return;
}
The second ui-thread will be created if the first data (by ethernet)
will arrive.
void CSerialWrite::OnStart(WPARAM wParam, LPARAM lParam)
{
//public member of this class: handle
handle = (SerialHandle)wParam;
//install the event notification (only dummy code)
hEvent = CreateEvent(NULL,false,false,_T(""));
SetEventNotification();
*****
Note that if you have a serial port you must open it for asynchronous I/O=
.. Did you read
my essay on serial ports?
*****
TxSerialData();
}
void CSerialWrite::TxSerialData()
{
while(running)
{
//get data packets from CSerialCollectDataTh
//tx data packet (serial port)
WriteData(buffer);
****
Where does 'buffer' come from? This generlaly makes no sense. You w=
ould pass the address
of the buffer in via a PostThreadMessage, so using a single variable cann=
ot make sense.
****
Where is the best place to allocate this cArray, including all
buffers? In the socket-class (a subclass from CAsyncSocket, where I
get all the ethernet packets)?
class CEthSoc : CConnSoc (subclass of CAsyncSocket)
{
public:
typedef struct _data {
WORD Port;
unsigned char Data[DATA_BUF_SIZE];
}DATA;
typedef CArray<DATA, DATA> DArray;
DArray ddatabuf[16]; //for all 16 serial ports
}
//at the beginning:
void CEthSoc::Init(void)
{
//tx addr to CSerialWrite thread
::PostThreadMessage(threadID, UWM_TX_BUF_PTR, (WPARAM)&ddatabuf[0],
0);
}
//a new ethernet frame arrived
void CEthSoc::RxData(ETH_DATA *ptr)
{
DATA data;
memcpy((void *)&data.Data[0], (void *)&ptr, sizeof(DATA));
data.Port = ptr->port;
ddatabuf[ptr->port].Add(data);
}
So that I don`t need the CSerialCollectDataTh thread to collect them?
How is it possible to install a buffer which is located at the heap? A
public member of this class (as I wrote it)?
*****
You would have some buffer allocated on the heap which contained your dat=
a to write. You
would PostThreadMessage a pointer to this to your writer thread. It is=
not clear why you
need a separate class to do CSerialWrite, but you would ideally call a me=
thod in it to
write the data. So why does TxSerialData not take a pointer to a buffe=
r to write?
****
writer-thread: CSerialWrite
void CSerialWrite::OnGetBufPtr(WPARAM wParam, LPARAM)
{
//wparam includes the pointer to the CArray of all 16 serial ports
}
Is it also possible for the CSerialWrite thread to delete some buffers
of the CArray if a new buffer arrived? Do I have to install any kind
of semaphoren or critical_sections?
WaitForSingleObject: the signal for the hEvent will be generated by
the device driver for this external serialport device (FTDI device).
****
Yes. But note that if it fails, you have no way to kill this thread. =
Read my essay on
worker threads and the use of WaitForMultipleObjects using a shutdown eve=
nt so you can
sucessfully stop the thread.
****
ok, I will do this!
Moreover is there a simpler solution available than the one I`m trying
to use? CSerialCollectDataTh has only the task to get all incoming
data packets by the ethernet connection collected to one clist /
cArray.
CSerialWrite will write these data packets to the serial port.
*****
The thread writes them to the serial port. The thread accepts buffer p=
ointers and upon
completion of the write, frees the object pointed to. I prefer CByteAr=
ray for this
purpose.
*****
The problem is that could be possible that two or more ethernet
packets for one specific serial port will be received in the time,
when only one packet will be transmitted by the serial port. Therefore
I thought it would be a good solution to install an dynamic CArray for
each serial port.
void CSerialWrite::WriteToSerialPort()
{
// write to serial port
//check the Carray size (if there`s only one buffer -> transmit
this buffer again)
//ptr to the CArray
UINT size = ptrbuf->GetSize()
if(size>1)
{
//delete this buffer
ptrbuf->RemoveAt(0);
//get next buffer to tx
ptrbufnext = ptrbuf->GetPos(0);
}
}
Algorithm: If there`s only one ethernet packet in the CArray for this
serial port, then transmit this buffer again - if there are more than
one ethernet packet for this serial port, delete the buffer of the
CArrray which we have already transmitted by the serial port and get
the next buffer to transmit (serial port).
best regards
Hans
Moreover I`ve installed another thread class for the specific
Casyncsocket receiving these data packets. I`m not sure if it is much
better to collect the data packets in this thread class and getting
them by SendMessage() from the CSerialWrite() thread.
*****
Note that your network code must implement the notion that a "packet" is =
a stream of bytes
and needs to possibly do many Receive() calls to get a packet; that a pac=
ket can be split
across one or more Receive() calls. Having read a "packet" from the ne=
twork, you then
allocate a buffer on the heap, put the packet into it, and PostThreadMess=
age to your
serial port writer. This seems reasonable. Do not use SendMessage b=
etween threads.
joe
****
best regards
Hans