Thread View: borland.public.cppbuilder.internet.socket
22 messages
22 total messages
Started by Lesley Anne
Mon, 19 Feb 2007 18:11
Passing info between threads
Author: Lesley Anne
Date: Mon, 19 Feb 2007 18:11
Date: Mon, 19 Feb 2007 18:11
91 lines
4748 bytes
4748 bytes
(As the subject line suggests, this is actually a threading question rather
than a socket question, but threading didn't seem to fit the topics for any
of the boards here. Socket communication is why I needed the threads in the
first place though, so this was as close as I could find to a relevant place
to ask this.)
I've worked occasionally with multi-threaded apps before, but in those
instances the main VCL thread would spawn temporary helper threads which
would do one thing and then finish. I would use Synchronize() to pass info
from the helper threads back to the VCL thread, but the only info passed
from the VCL thread to the helper threads would be passed as parameters to
the thread class's constructor.
Now I've got a different situation, where I need to keep a secondary thread
active throughout the life of an application, and be able to repeatedly pass
information to it from the main VCL thread. I'm trying to figure out how to
do that in a thread-safe manner. Perhaps I should start with showing what I
have so far, and then explain my concerns about it.
Declared globally in the main form's unit, and extern in the thread class's
unit, I have:
String VCLToSctpMessage;
unsigned int VCLToSctpAssoc;
TCriticalSection *pLockSendString;
TCriticalSection *pLockAssocID;
Within one of the functions on my main form, I have:
pLockSendString->Acquire();
pLockAssocID->Acquire();
try {
VCLToSctpMessage = String(buffer);
VCLToSctpAssoc = associationID[Site];
}
__finally {
pLockSendString->Release();
pLockAssocID->Release();
}
if (sctpThread)
sctpThread->Send();
And in my SCTP thread (a TThread descendant) the Send function looks like
this:
// -----------------------------------------------
void TsctpThread::Send(void)
{
unsigned char Message[4096];
unsigned int associationID;
int Length;
pLockSendString->Acquire();
pLockAssocID->Acquire();
try {
Length = VCLToSctpMessage.Length();
strcpy(Message, VCLToSctpMessage.c_str());
associationID = VCLToSctpAssoc;
}
__finally {
pLockSendString->Release();
pLockAssocID->Release();
}
sctp_send(associationID, 0, Message, Length,
SCTP_GENERIC_PAYLOAD_PROTOCOL_ID,
SCTP_USE_PRIMARY, SCTP_NO_CONTEXT,
SCTP_INFINITE_LIFETIME,
SCTP_ORDERED_DELIVERY,
SCTP_BUNDLING_DISABLED);
}
// -----------------------------------------------
OK, now I'm pretty sure that the TCriticalSections will make it thread-safe
in terms of not having both threads access the variables at the same time,
but I'm not sure whether I can count on the sctpThread reading in the values
of the variables before another function in the main VCL thread (or another
instance of the same function) changes the variables to something else.
If the first section of code above (the part in my main form) were to fire
off twice in rapid succession, once setting VCLToSctpMessage to "hello" and
then setting it to "goodbye", could I count on the Send function in my
sctpThread picking up "hello" for the first instance and "goodbye" for the
second, or could it wind up seeing "goodbye" twice?
If my concern is valid, and it could show up as "goodbye" twice, then how do
I fix it? I got stuck with repeatedly using global variables because I
didn't think the scope rules would allow me to send local variables to the
thread, but I don't want to allow re-use of those global variables until the
thread has seen their current values. (Or would it be OK to pass local
variables to the thread? How would that work?)
Re: Passing info between threads
Author: Paul at NCF
Date: Mon, 19 Feb 2007 21:16
Date: Mon, 19 Feb 2007 21:16
23 lines
1754 bytes
1754 bytes
Hi,
I might be missing something here, or misunderstanding your design or
problem. But it seems you want to pass a string and assoc ID to the thread,
to then call sctp_send to send this information?
Why don't you just call the send method in the thread with (stack based)
parameters rather than globals:
if (sctpThread)
sctpThread->Send( String(buffer), associationID[Site] )
Then make the Send method thread safe with a critical section to do any
actions that need it (around adding or removing items from lists / queues).
eg. put the parameters passed in on to a queue, and signal the thread there
is a job to do. The thread then wakes up, and removes the oldest job from
the queue thread safely, until the queue is empty.
As I said, I might be missing something.
Regards,
Paul
Re: Passing info between threads
Author: Lesley Anne
Date: Mon, 19 Feb 2007 21:48
Date: Mon, 19 Feb 2007 21:48
31 lines
2085 bytes
2085 bytes
> I might be missing something here, or misunderstanding your design or > problem. But it seems you want to pass a string and assoc ID to the > thread, to then call sctp_send to send this information? Yes. > Why don't you just call the send method in the thread with (stack based) > parameters rather than globals: > > if (sctpThread) > sctpThread->Send( String(buffer), associationID[Site] ) That's the effect I'm trying to achieve, but I'm having trouble figuring out a thread-safe method of doing that. > Then make the Send method thread safe with a critical section to do any > actions that need it (around adding or removing items from lists / > queues). If the parameters are passed in, how/where do I put in a critical section lock? And does that do anything to help the scope problem if the VCL thread has gone on to something else by the time the SCTP thread picks this up? > eg. put the parameters passed in on to a queue, and signal the thread > there is a job to do. The thread then wakes up, and removes the oldest > job from the queue thread safely, until the queue is empty. How do I do that?
Re: Passing info between threads
Author: Paul at NCF
Date: Mon, 19 Feb 2007 22:54
Date: Mon, 19 Feb 2007 22:54
11 lines
1465 bytes
1465 bytes
Quick question: What components are you trying to use, and are you dropping them on the main form / instantiating them in the main form, or are you instantiating them in the 'worker' thread? Or both? ie. I create say a UDP component in the thread and don't use it or reference it on the main form at all, that way I don't have to worry about the VCL components and VCL being non-thread safe (or using syncronize). Paul
Re: Passing info between threads
Author: Paul at NCF
Date: Tue, 20 Feb 2007 08:09
Date: Tue, 20 Feb 2007 08:09
56 lines
2545 bytes
2545 bytes
Hi,
Off to work soon, so just in case this helps, an example help thread:
void
ClientTask::SendAMessage( int ID,
char * Message )
{
if (MyMbx)
{
// class/structure to add params to input Q
AMessage * sl = new AMessage( int ID, char * Message );
// My own thread safe mainbox, add item to end of list. It has
critical section around add and remove methods,
// and signals the thread using its TEvent when adding
// You could use TThreadList (LockList, UnlockList, Add, Remove
etc), and TEvent directly: event->SetEvent();
MyMbx->AddMailMsg(sl);
}
}
ClientTask::Execute()
{
while (!Terminated))
{
// get msg off queue, if one exists (thread safe), or TThreadList
msg = (AMessage *) MyMbx->RemoveMailMsg();
// if no msg, wait for wakeup
if (!msg)
{
// wait for multiple objects API using TEvent handle, or
timeout
signalled = WaitForWakeUp(1000);
// get message off fron of Q, as before this is my Q system, but
you could use TThreadList
msg = (AMessage *) MyMbx->RemoveMailMsg();
}
// if msg on queue, send
if (msg)
{
SendToServer(msg);
delete msg;
}
} // do forever (or nearly)
}
> That's the effect I'm trying to achieve, but I'm having trouble figuring
> out a thread-safe method of doing that.
Re: Passing info between threads
Author: Bob Gonder
Date: Tue, 20 Feb 2007 15:29
Date: Tue, 20 Feb 2007 15:29
92 lines
3360 bytes
3360 bytes
"Lesley Anne" <mspfila (dash) brl (at) yahoo (dot) com> wrote:
I'm a bit late, so I'll just throw out some comments
> pLockSendString->Acquire();
> pLockAssocID->Acquire();
You are releasing in the wrong order.
When using multiple locks, always (in all segments or your program)
lock them in the same order, and always release in the exact opposite
order. This reduces the dreaded Deadlock.
> pLockSendString->Release();
> pLockAssocID->Release();
And then, it looks like you are always using Both locks, never a
single one, which makes one wonder why you need 2? A single lock
between the sender (VCL) and the receiver (thread) sould suffice.
Ok, one way to use 2:
(This is redundant and slow/inefficient [and wrong])
VCL: LockOne() // wait for Copy
SetData()
UnlockOne() // allow Copy
NotifyThread() // wake up thread
LockTwo() // wait for thread to awaken
UnlockTwo() // allow thread to complete
Thread: LockTwo()
ThreadNotify()
LockOne() // lock the data
UnlockTwo() // inform VCL we are awake
CopyData()
UnlockOne() // allow changes to data
LockTwo() // wait for VCL to resume
ProcessData()
No, there's still a race on Two.
I suppose one could attempt a Third, but it seems to be going in the
wrong direction.
There seems to be a concern with VCL being faster than Thread, and
overwritting or dropping data.
Single Lock:
VCL:
LockData() // wait for thread
SetData()
UnlockData()
NotifyThread()
Thread:
LockData()
ProcessData()
UnlockData()
Yes, there is still a race in that VCL could cycle through before
Thread gets going and locks the data.
One way out is a simple sentinal.
VCL:
static volatile bool DataFull = false;
while( DataFull ){ ProcessMessages() }
SetData()
DataFull = true
ThreadSend( DataFull )
ThreadSend( bool & DataFull ):
ProcessData()
DataFull = false
VCL can cycle through, but will get stuck until Thread has finished.
> (Or would it be OK to pass local
>variables to the thread? How would that work?)
You would need to wait for the thread to copy them.
VCL:
SetData();
ThreadSend( LocalData )
WaitForSingleObject( hDataAquiredEvent, 10000 );
Thread:
CopyData();
Signal( hDataAquiredEvent );
ProcessData()
(There is probably some TEvent that makes that easier)
Re: Passing info between threads
Author: Lesley Anne
Date: Tue, 20 Feb 2007 16:39
Date: Tue, 20 Feb 2007 16:39
50 lines
3557 bytes
3557 bytes
"Paul at NCF" <notplmacca@clara.co.uk> wrote in message news:45da2a86@newsgroups.borland.com... > Quick question: > > What components are you trying to use, and are you dropping them > on the main form / instantiating them in the main form, or are you > instantiating them in the 'worker' thread? Or both? > > ie. I create say a UDP component in the thread and don't use it > or reference it on the main form at all, that way I don't have > to worry about the VCL components and VCL being non-thread safe > (or using syncronize). The worker thread handles all SCTP communications, while the VCL thread handles everything else (which is primarily the GUI at the moment). There are no components for SCTP, just a library, but that library is initialized and used exclusively from the SCTP thread. The thread is instantiated at application startup, and immediately calls sctp_initLibrary() (which allocates a raw socket that will be used in all communications, and does some other initializations of library variables) and sctp_registerInstance() (which lets the library know what functions to call for events, as well as things like local addresses and port, and how many input and output streams to use). After that the thread calls sctp_eventLoop() continually until Terminated. The main VCL thread has to tell the SCTP thread when to make an association (along with address and port parameters of where to connect to), when to send a message (along with parameters for the text of the message as well as which association to send it to), and when to disconnect (along with a parameter for which association to shut down). When it makes a connection, the SCTP thread needs to tell the VCL thread the ID for that association (so the VCL thread can use that ID later to tell the SCTP thread where to send messages). When it receives a message from the far end, it has to send that message to the VCL thread for processing (which in our test app is just a matter of displaying it on the screen, but will eventually involve more complex handling). And there are various status messages it passes back to the VCL thread as well for display/logging. We're using Synchronize for everything passed from the SCTP thread to the VCL thread, and critical sections around everything passed from the VCL thread to the SCTP thread. We've got it all working, except that I'm not positive whether the way we pass things from the VCL to SCTP threads will still work correctly when the events fire more rapidly.
Re: Passing info between threads
Author: Lesley Anne
Date: Tue, 20 Feb 2007 16:41
Date: Tue, 20 Feb 2007 16:41
86 lines
4290 bytes
4290 bytes
"Bob Gonder" <notbg@notmindspring.invalid> wrote in message
news:h21mt21bg7maqp25d2lqkr9b1iet0eu3g6@4ax.com...
> "Lesley Anne" <mspfila (dash) brl (at) yahoo (dot) com> wrote:
>
> I'm a bit late, so I'll just throw out some comments
>
> > pLockSendString->Acquire();
> > pLockAssocID->Acquire();
>
> You are releasing in the wrong order.
> When using multiple locks, always (in all segments or your program)
> lock them in the same order, and always release in the exact opposite
> order. This reduces the dreaded Deadlock.
OK, I'll fix that. Thanks.
> > pLockSendString->Release();
> > pLockAssocID->Release();
>
> And then, it looks like you are always using Both locks, never a
> single one, which makes one wonder why you need 2? A single lock
> between the sender (VCL) and the receiver (thread) should suffice.
We used separate locks for each variable we were setting because this Send
function needed to pass both variables, but the Disconnect function just
needed to pass the AssociationID. (There's also a Connect function that
uses a third TCriticalSection to lock both DestinationAddress and
DestinationPort variables.) I just showed the Send in my first post because
I thought including all three confused the issue more than it clarified it.
> One way out is a simple sentinal.
>
> VCL:
> static volatile bool DataFull = false;
>
> while( DataFull ){ ProcessMessages() }
> SetData()
> DataFull = true
> ThreadSend( DataFull )
>
> ThreadSend( bool & DataFull ):
> ProcessData()
> DataFull = false
>
> VCL can cycle through, but will get stuck until Thread has finished.
I like that this version allows other processing to go on while it's
waiting, but if that ProcessMessages() picks up another Send function, it
could be processed before the earlier one. I'm not sure yet whether we're
going to care about the order of our messages (in most of our applications
we don't), but it would be something to keep in mind in case we end up
copying this into an application where we do care about message order.
(Even if we don't care about the order of sent messages, if that
ProcessMessages picked up a Disconnect function, having Send and then
Disconnect turned into Disconnect and then Send could cause an error.)
> > (Or would it be OK to pass local
> > variables to the thread? How would that work?)
>
> You would need to wait for the thread to copy them.
>
> VCL:
> SetData();
> ThreadSend( LocalData )
> WaitForSingleObject( hDataAquiredEvent, 10000 );
> Thread:
> CopyData();
> Signal( hDataAquiredEvent );
> ProcessData()
>
> (There is probably some TEvent that makes that easier)
This looks like the safer way to do it, so long as the wait doesn't get long
enough to hurt performance. Since this is the first app we're working on
using the SCTP library, I don't know whether it can lock out the SCTP thread
for long intervals or not. (I'm hoping it doesn't anyway.) We'll probably
try this (or perhaps its TEvent equivalent) and then do some performance
testing.
Does this waiting for an event signaling that the thread has finished
reading its input parameters take care of making it thread-safe? (i.e. Does
it allow us to just pass in parameters without needing the critical
section?)
Re: Passing info between threads
Author: Remy Lebeau (TeamB)
Date: Tue, 20 Feb 2007 18:49
Date: Tue, 20 Feb 2007 18:49
12 lines
1266 bytes
1266 bytes
"Lesley Anne" <mspfila (dash) brl (at) yahoo (dot) com> wrote in message news:45d9e83a$1@newsgroups.borland.com... > threading didn't seem to fit the topics for any of the boards here. If you read the newsgroup descriptions at http://info.borland.com/newsgroups/cppbnewsdesc.html, you will see exactly where to post threading questions. Gambit
Re: Passing info between threads
Author: Lesley Anne
Date: Tue, 20 Feb 2007 21:53
Date: Tue, 20 Feb 2007 21:53
12 lines
1529 bytes
1529 bytes
"Remy Lebeau (TeamB)" <no.spam@no.spam.com> wrote in message news:45db42ad@newsgroups.borland.com... > > > If you read the newsgroup descriptions at > http://info.borland.com/newsgroups/cppbnewsdesc.html, > you will see exactly where to post threading questions. Thanks for a link to the descriptions, but it doesn't seem to be very current. There are quite a few boards not listed there, and it lists some that don't exist. It says to post threading questions on borland.public.cppbuilder.winapi, but there is no such board on the server.
Re: Passing info between threads
Author: Lesley Anne
Date: Tue, 20 Feb 2007 21:58
Date: Tue, 20 Feb 2007 21:58
9 lines
1366 bytes
1366 bytes
I wrote in message news:45db6dc5$1@newsgroups.borland.com... > > It says to post threading questions on borland.public.cppbuilder.winapi, > but there is no such > board on the server. Never mind. I found it. I guess it's now listed as "nativeapi" rather than "winapi". (It would be nice if the description page used the current names, though.)
Re: Passing info between threads
Author: Remy Lebeau (TeamB)
Date: Tue, 20 Feb 2007 23:04
Date: Tue, 20 Feb 2007 23:04
18 lines
1428 bytes
1428 bytes
"Lesley Anne" <mspfila (dash) brl (at) yahoo (dot) com> wrote in message news:45db6dc5$1@newsgroups.borland.com... > Thanks for a link to the descriptions, but it doesn't seem to be very > current. No, it is not current, but it is still mostly relevant. > It says to post threading questions on borland.public.cppbuilder.winapi, > but there is no such board on the server. It was renamed to ".nativeapi" a long time ago. Gambit
Re: Passing info between threads
Author: Bob Gonder
Date: Wed, 21 Feb 2007 02:24
Date: Wed, 21 Feb 2007 02:24
68 lines
3599 bytes
3599 bytes
"Lesley Anne" <mspfila (dash) brl (at) yahoo (dot) com> wrote:
>> > (Or would it be OK to pass local
>> > variables to the thread? How would that work?)
>>
>> You would need to wait for the thread to copy them.
>>
>> VCL:
>> SetData();
>> ThreadSend( LocalData )
>> WaitForSingleObject( hDataAquiredEvent, 10000 );
>> Thread:
>> CopyData();
>> Signal( hDataAquiredEvent );
>> ProcessData()
>
>Does this waiting for an event signaling that the thread has finished
>reading its input parameters take care of making it thread-safe? (i.e. Does
>it allow us to just pass in parameters without needing the critical
>section?)
Yes. The thread has copied the application's local data into it's own
local data before signaling, so the application data is free to
disappear.
I see that you are also concerned about the Thread falling behing the
application, and getting messages out of order. One way out is to add
a ring buffer.
The application stuffs data into the head of the ring, then tells the
thread that new data is available. (Once again, the data is now in an
escrow buffer, so the application data can change or "go away".)
The thread takes data off the tail of the ring, and sends it.
It is possable to construct a ring buffer so that no locking is
required. Only reason to use locks would be if you have more than one
writter (application) thread, or if you have more than one thread
reading from it.
When the ring fills, the application stalls until the sender can free
up a spot.
VCL:
while( IsRingBufferFull() ) Sleep(0); // wait for an opening
StuffDataIntoRingBuffer();
NextRingBufferHead(); // fullfills RingBufferHasData()
Signal( hSendDataAvailableEvent ); // wake up
Thread:
while( ! Terminated )
{
WaitForSingleObject( hSendDataAvailableEvent, 1000 );
while( RingBufferHasData() )
{
Data = ReadRingBufferData();
NextRingBufferTail(); // unblocks VCL while()
Send( Data );
};
};
To bring this back on-topic....
I don't know if there is an STL ring buffer, but you might want to
investigate using auto-pointers to hold your messages, so the buffer
would be holding pointers to messages instead of the actual messages.
Since message may be of variable length, it might be hard to design a
ring buffer of sufficient size to hold raw messages.
If you find that your ring fills up and stays full, even if you make
it quite large, then you might want to look into boosting the thread
priority (or adding another socket if the application could use it)
Re: Passing info between threads
Author: Paul at NCF
Date: Wed, 21 Feb 2007 08:58
Date: Wed, 21 Feb 2007 08:58
8 lines
1339 bytes
1339 bytes
Looks like you have enough information now from other. Hope it all works out. "Paul at NCF" <notplmacca@clara.co.uk> wrote in message news:45daac91@newsgroups.borland.com... > Hi, > > Off to work soon, so just in case this helps, an example help thread:
Re: Passing info between threads
Author: Lesley Anne
Date: Wed, 21 Feb 2007 22:00
Date: Wed, 21 Feb 2007 22:00
113 lines
5537 bytes
5537 bytes
"Bob Gonder" <notbg@notmindspring.invalid> wrote in message
news:vj8nt2llf7deda6ioqggci4rds885qcmne@4ax.com...
>
> I see that you are also concerned about the Thread falling behind
> the application, and getting messages out of order. One way out
> is to add a ring buffer.
> The application stuffs data into the head of the ring, then tells
> the thread that new data is available. (Once again, the data is
> now in an escrow buffer, so the application data can change or
> "go away".) The thread takes data off the tail of the ring, and
> sends it.
This sounds like the queue approach that Paul was talking about. I've
looked at TThreadList, and think I can use that for a queue of commands I
want the thread to process. Here's how that looks so far:
In the header, I defined a structure to hold commands and their parameters.
It looks like this:
enum TSctpCommand { scmdConnect, scmdShutDown, scmdSend };
typedef struct SctpCmdParamRec {
TSctpCommand Command;
unsigned char MsgBuffer[MAX_BUFFER_LENGTH];
int MsgLen;
unsigned int AssocID;
unsigned char DestAddr[SCTP_MAX_IP_LEN];
unsigned short DestPort;
int SiteIndex;
} TSctpCmdParams;
Then I have:
TThreadList* SctpCommandList;
defined globally in my main unit (and extern in the thread unit), which gets
instantiated with 'new' in my main form's constructor.
Whenever the main form wants to initiate something in the SCTP thread, it
does something like this:
// --------------------------------------------------------------
void __fastcall TForm1::Send(String Message, int Site)
{
if (sctpThread) {
TSctpCmdParams *SctpCmdParams = new TSctpCmdParams;
SctpCmdParams->Command = scmdSend;
SctpCmdParams->AssocID = associationID[Site];
strcpy(SctpCmdParams->MsgBuffer, Message.c_str());
SctpCmdParams->MsgLen = Message.Length();
SctpCommandList->Add(SctpCmdParams);
sctpThread->HandleCommand();
} else
Memo1->Lines->Add("ERROR: sctp Thread does not exist");
}
// --------------------------------------------------------------
(The Connect and Disconnect functions in the main form look almost identical
to this except that they set different members of SctpCmdParams.)
In the SCTP Thread unit, anything coming from the VCL Thread will enter via
this function:
// --------------------------------------------------------------
void TsctpThread::HandleCommand(void)
{
TList* CmdList;
TSctpCmdParams* SctpCmdParams = NULL;
CmdList = SctpCommandList->LockList();
if (CmdList->Count > 0) {
SctpCmdParams = (TSctpCmdParams*)CmdList->First();
CmdList->Remove(SctpCmdParams);
}
SctpCommandList->UnlockList();
if (SctpCmdParams) {
switch (SctpCmdParams->Command) {
case scmdConnect:
Connect(SctpCmdParams->DestAddr,
SctpCmdParams->DestPort,
SctpCmdParams->SiteIndex);
break;
case scmdShutDown:
ShutDown(SctpCmdParams->AssocID);
break;
case scmdSend:
Send(SctpCmdParams->MsgBuffer,
SctpCmdParams->MsgLen,
SctpCmdParams->AssocID);
break;
}
delete SctpCmdParams;
}
// should probably add an 'else' here for an error message
}
// --------------------------------------------------------------
This does have the VCL thread allocating structures which the SCTP thread
eventually deletes, but I think that should work so long as the TThreadList
buffers the two accesses adequately. The VCL thread never looks at the
SctpCmdParams it created again after putting it on the TThreadList, and the
SCTP thread uses and deletes it after getting it off of that list.
There is a bit of overhead in my TSctpCmdParams structure, because space for
all possible parameters gets allocated each time, even though only the
parameters relevant to that particular command get used. I'm not too
worried about that, though, because by far the largest piece of it is the
MsgBuffer, and that's what I'm going to need the most often anyway. (The
Send command is the one that can get called a lot. Connect and ShutDown
commands, where that MsgBuffer is unnecessary, are infrequent events.)
Re: Passing info between threads
Author: Paul at NCF
Date: Thu, 22 Feb 2007 08:34
Date: Thu, 22 Feb 2007 08:34
32 lines
2494 bytes
2494 bytes
Hi, I think you're nearly there but just a few things: > Then I have: > TThreadList* SctpCommandList; > defined globally in my main unit (and extern in the thread unit), which > gets instantiated with 'new' in my main form's constructor. Does MainFrom need to know about the command list or its workings? You can make the threadlist a member of the thread, not the form, add a AddCommand(SctpCmdParams) method to the thread which does SctpCommandList->Add(SctpCmdParams), or go one step further and make the parameters of that method the fields you need to pass to the thread that need adding to 'its' queue. That way you can change the entire queueing system in the thread and the main form would not know or be affected. > SctpCommandList->Add(SctpCmdParams); > sctpThread->HandleCommand(); I don't think this helps your performance or queueing. This adds the command to the list in the MainForms scope, and also does the operation of processing the queue in the main forms scope. The HandleCommand needs to be done via the threads execute method to make the thread do the work, the main form needs to add and then signal the thread. If you want to get the principle of the queue processing working initially you can just get the thread execute to Sleep(10ms) and then check if the queue is non-empty in its while loop. Hope this helps. Paul
Re: Passing info between threads
Author: Lesley Anne
Date: Thu, 22 Feb 2007 20:04
Date: Thu, 22 Feb 2007 20:04
62 lines
4015 bytes
4015 bytes
"Paul at NCF" <notplmacca@clara.co.uk> wrote in message
news:45dd557c$1@newsgroups.borland.com...
>
>
> Does MainForm need to know about the command list or its workings? You
> can make the threadlist a member of the thread, not the form, add a
> AddCommand(SctpCmdParams) method to the thread which does
> SctpCommandList->Add(SctpCmdParams), or go one step further and make the
> parameters of that method the fields you need to pass to the thread that
> need adding to 'its' queue. That way you can change the entire queuing
> system in the thread and the main form would not know or be affected.
Wouldn't that defeat the purpose? In order for the queue to protect the two
threads from each other, it needs to bridge the gap between the two. If my
thread puts the message into the queue itself, and then later takes it off,
then what's the queue there for?
> > SctpCommandList->Add(SctpCmdParams);
> > sctpThread->HandleCommand();
>
> I don't think this helps your performance or queuing. This adds the
> command to the list in the MainForms scope, and also does the operation of
> processing the queue in the main forms scope.
That looks to me like it adds the command in the main form, but then calls
the sctp thread to handle it.
> the main form needs to add and then signal the thread.
Isn't that just what it's doing here? The first line above adds the
command, then the second line tells the thread there's a command for it to
handle.
> The HandleCommand needs to be done via the threads execute method to make
> the thread do the work,
> If you want to get the principle of the queue processing working initially
> you can just get the thread execute to Sleep(10ms) and then check if the
> queue is non-empty in its while loop.
I don't want the thread to sleep, because some of the applications I use
this for may be handling high traffic volumes, and I don't want to slow down
performance by blocking the communication library. The while loop in the
thread's Execute function looks like this right now:
while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
}
If I could easily check whether there's anything in the queue, then maybe I
could add a line here (so that it alternates with the library's
sctp_eventLoop function) to check for any commands it has pending. However,
even seeing whether the TThreadList is empty or not requires locking it
first. If this thread keeps continually locking the list (even when there's
nothing there), would the main thread have a harder time getting its own
lock when it wants to add something? I prefer for the main unit to tell the
thread when there's a command in the list for it to handle. That way, the
thread would only need to lock the list when it's retrieving a command off
of it.
Re: Passing info between threads
Author: Paul at NCF
Date: Thu, 22 Feb 2007 22:46
Date: Thu, 22 Feb 2007 22:46
56 lines
3723 bytes
3723 bytes
Hi, I may have lost the plot, but I thought you wanted to pass information to a (secondary) thread from the main VCL thread (mainform)? > Wouldn't that defeat the purpose? In order for the queue to protect the > two threads from each other, it needs to bridge the gap between the two. > If my thread puts the message into the queue itself, and then later takes > it off, then what's the queue there for? If you call the threads AddCommand method to add to the queue, the main VCL thread is running and calling into the thread. So the VCL thread is adding to the queue. The thread could then get signaled and woken up, and remove the item off the queue. > That looks to me like it adds the command in the main form, but then calls > the sctp thread to handle it. Yes, thats what the example looked like that you showed? > Isn't that just what it's doing here? The first line above adds the > command, then the second line tells the thread there's a command for it to > handle. Not sure it did. Looked to me like the VCL thread added the command to the queue, and then calls the threads handle command to take it off the queue and process it? > I don't want the thread to sleep, because some of the applications I use > this for may be handling high traffic volumes, and I don't want to slow > down performance by blocking the communication library. The while loop in > the thread's Execute function looks like this right now: Yes, I wouldn't do that normally, I was suggesting it to get something basically working without setting up the signalling and wait for objects API. > If I could easily check whether there's anything in the queue, then maybe > I could add a line here (so that it alternates with the library's > sctp_eventLoop function) to check for any commands it has pending. > However, even seeing whether the TThreadList is empty or not requires > locking it first. If this thread keeps continually locking the list (even > when there's nothing there), would the main thread have a harder time > getting its own lock when it wants to add something? I prefer for the > main unit to tell the thread when there's a command in the list for it to > handle. That way, the thread would only need to lock the list when it's > retrieving a command off of it. WaitForMultipleObjects would only wake up the thread when something was signalled. And it would only block the VCL thread when it was locked, and vv. As I said, I may have lost the plot and be completely misunderstanding what you are trying to do. Paul
Re: Passing info between threads
Author: Lesley Anne
Date: Thu, 22 Feb 2007 23:25
Date: Thu, 22 Feb 2007 23:25
42 lines
2698 bytes
2698 bytes
"Paul at NCF" <notplmacca@clara.co.uk> wrote in message
news:45de1d0f@newsgroups.borland.com...
>
> I may have lost the plot, but I thought you wanted to pass information to
> a (secondary) thread from the main VCL thread (mainform)?
Yes.
> So the VCL thread is adding to the queue.
Yes.
> The thread could then get signaled and woken up, and remove the item off
> the queue.
Yes. That's what the HandleCommand is there for, and why it's in the
sctpThread.
> Not sure it did. Looked to me like the VCL thread added the command to
> the queue, and then calls the threads handle command to take it off the
> queue and process it?
Isn't that what you just said it should do?
> WaitForMultipleObjects would only wake up the thread when something was
> signalled. And it would only block the VCL thread when it was locked
But I can't have the thread sleeping or waiting or anything else that would
keep it from processing the library's event loop. It might be a while
before the next time the main unit initiates an outgoing message, but in the
meantime, the library still needs to keep looking for incoming messages. It
relies on sctp_eventLoop() being called continuously. Whatever I add into
the Execute's while loop has to co-exist peacefully with:
while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
}
and not block that eventLoop from happening.
Re: Passing info between threads
Author: Bob Gonder
Date: Fri, 23 Feb 2007 04:31
Date: Fri, 23 Feb 2007 04:31
118 lines
5266 bytes
5266 bytes
"Lesley Anne" wrote:
>"Paul at NCF" wrote in message
>>
>> The thread could then get signaled and woken up, and remove the item off
>> the queue.
>
>Yes. That's what the HandleCommand is there for, and why it's in the
>sctpThread.
Yes, it is a part of the Thread Class, but it is being called by the
VCL Thread. It is being executed by the VCL thread, not the sctp
thread. The Thread::Execute() needs to be calling the HandleCommand
code when data becomes available to send.
>> Not sure it did. Looked to me like the VCL thread added the command to
>> the queue, and then calls the threads handle command to take it off the
>> queue and process it?
>
>Isn't that what you just said it should do?
No, it should _signal_ the thread to send the data, not _call_ the
thread function that does the actual work.
>> WaitForMultipleObjects would only wake up the thread when something was
>> signalled. And it would only block the VCL thread when it was locked
>
>But I can't have the thread sleeping or waiting or anything else that would
>keep it from processing the library's event loop.
Why do you have an event loop? I thought you were using blocking
calls.
> It might be a while
>before the next time the main unit initiates an outgoing message, but in the
>meantime, the library still needs to keep looking for incoming messages. It
>relies on sctp_eventLoop() being called continuously. Whatever I add into
>the Execute's while loop has to co-exist peacefully with:
void __fastcall TForm1::Send(String Message, int Site)
{
if (sctpThread) {
TSctpCmdParams *SctpCmdParams = new TSctpCmdParams;
SctpCmdParams->Command = scmdSend;
SctpCmdParams->AssocID = associationID[Site];
strcpy(SctpCmdParams->MsgBuffer, Message.c_str());
SctpCmdParams->MsgLen = Message.Length();
SctpCommandList->Add(SctpCmdParams);
evSendEvent->SetEvent( ); // <<<---
} else
Memo1->Lines->Add("ERROR: sctp Thread does not exist");
}
....... ....... TsctpThread::Execute( ..... )
{
/* setup the thread and sending socket info */
/* not shown */
> while (! Terminated ) {
// no delay or locks
// acts like non-existant if( evSendEvent->Signaled )
if( evSendEvent->WaitFor( 0 ) == wrSignaled )
{
// Only Locks when Signaled
TList* List = SctpCommandList->LockList();
if( List->Count > 0 )
{
TSctpCmdParams *SctpCmdParams =
(TSctpCmdParams*)List->First();
List->Delete( 0 );
SctpCommandList->UnlockList();
HandleCommand( SctpCmdParams );
> if (sctp_eventLoop() < 0)
> break ;
// In case 2 events came in and we missed the first
evSendEvent->SetEvent( );
continue; // skip the sleep()
}else{
// no command listed, probably got here
// from the above SetEvent();
SctpCommandList->UnlockList();
};
};
> if (sctp_eventLoop() < 0)
> break ;
// give up time slice so other threads can do something.
// since we have nothing to do right now.
Sleep( 0 );
> }
/* Socket and Thread cleanup */
/* not shown */
}
>and not block that eventLoop from happening.
I'm not sure what sctp_eventLoop is supposed to do for you.
In my stuff, I have One thread for Sending, and a Second thread for
Receiving.
In the Send thread, I WaitForSingleObject( hSendEvent, 1000 )
Which wakes up every second and checks for termination.
Otherwise, it sits using no CPU.
When Send request comes in, it does a blocking send, and checks for
another request before resuming the Wait. The App puts up a request,
then Signal( hSendEvent )
On the Application side, I have the same setup, blocking for a Receive
event that the Receive Thread sends when data becomes available.
The Receive Thread is mostly blocked on the Receive().
The Send Thread is mostly blocked on the WaitForSingleObject()
Re: Passing info between threads
Author: Paul at NCF
Date: Fri, 23 Feb 2007 08:04
Date: Fri, 23 Feb 2007 08:04
96 lines
3554 bytes
3554 bytes
Hi,
I was going to send an example this morning, but looks like Bob has beat me
to it. However as a bit more help:
I use a set of classes that I've built up to standardise what I want my
threads/tasks to do for me and my fellow team members. It may not be
perfect, but I've yet to make any changes to it in the last year or so.
I've built up all the other classes for sockets and serial comms around
this.
Hope this might help.
Paul
__fastcall CTask::CTask( char * name_string,
ulong identity,
ulong priority )
: TThread(true)
{
// make sure thread is deleted by owner
FreeOnTerminate = false;
// initialise member variables
MyHandlesCount = 0;
MyHandles = NULL;
MyEvent = NULL;
// create event and store handle
// unnamed os object to avoid unnecessary issues!!
MyEvent = new TEvent ( NULL, false, false, "" );
MyHandles[MyHandlesCount++] = (HANDLE) MyEvent->Handle;
}
void
CTask::WakeUpSignal( void )
{
MyEvent->SetEvent();
}
bool
CTask::WaitForWakeUp( int wait_time )
{
bool result = false;
ulong apilimit = MyHandlesCount - 1;
WMOresult = WaitForMultipleObjects( MyHandlesCount, MyHandles, false,
wait_time );
// test for API failure code
if( (WMOresult >= WAIT_OBJECT_0)
&& (WMOresult <= (WAIT_OBJECT_0 + apilimit))
){
result = false;
}
else
if( (WMOresult >= WAIT_ABANDONED_0)
&& (WMOresult <= (WAIT_ABANDONED_0 + apilimit))
){
result = true;
}
else
if (WMOresult == WAIT_TIMEOUT)
{
// timeout waiting for event
result = true;
}
else
{
int i = GetLastError();
result = true;
}
return result;
}
void Controller::SendMessageToTask( SomeMessageType * message )
{
// list type hidden from task owner, it doesn't need to know
if (MyMbx)
{
// mailbox signals the task event when adding a message
MyMbx->AddMailMsg(message);
}
}
void Controller::Execute()
{
while (!Terminated)
{
// wait for wake up if nothing to process
if (MyMbx->GetMailMsg() == NULL)
{
result = WaitForWakeUp( 100 );
}
etc.....
}
Re: Passing info between threads
Author: Michael Harris
Date: Fri, 23 Feb 2007 15:20
Date: Fri, 23 Feb 2007 15:20
64 lines
2799 bytes
2799 bytes
Lesley
data is stored in a csv type file format.
the main vcl thread updates these files with writeeclisive.
then post a message to the thread to update its data.
so the thread is busy. only interrupted if some additional or current data
is changed
sctp_eventLoop may need to periodically check the message que ?
while (! Terminated ) {
if (sctp_eventLoop() < 0)
break ;
if(!DoThreadMessage( ) )
break;
}
bool __fastcall TMonitorThread::DoThreadMessage(void)
{
if (Terminated) return False;
MSG msg;
ZeroMemory (&msg, sizeof (MSG));
if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE ))
{
switch(msg.message)
{
case WM_QUIT :
Terminate();
return False;
case TM_CLIENTCHANGED :
RenewItemInfo(msg.lParam);
break;
(...) etc
}
}
return True;
}
--
Michael
"Lesley Anne" wrote in message
> But I can't have the thread sleeping or waiting or anything else that
> would keep it from processing the library's event loop. It might be a
> while before the next time the main unit initiates an outgoing message,
> but in the meantime, the library still needs to keep looking for incoming
> messages. It relies on sctp_eventLoop() being called continuously.
> Whatever I add into the Execute's while loop has to co-exist peacefully
> with:
>
> while (! Terminated ) {
> if (sctp_eventLoop() < 0)
> break ;
> }
>
> and not block that eventLoop from happening.
>
>
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads