🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

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
#1297
Author: Lesley Anne
Date: Mon, 19 Feb 2007 18:11
91 lines
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
#1298
Author: Paul at NCF
Date: Mon, 19 Feb 2007 21:16
23 lines
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
#1299
Author: Lesley Anne
Date: Mon, 19 Feb 2007 21:48
31 lines
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
#1300
Author: Paul at NCF
Date: Mon, 19 Feb 2007 22:54
11 lines
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
#1301
Author: Paul at NCF
Date: Tue, 20 Feb 2007 08:09
56 lines
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
#1302
Author: Bob Gonder
Date: Tue, 20 Feb 2007 15:29
92 lines
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
#1303
Author: Lesley Anne
Date: Tue, 20 Feb 2007 16:39
50 lines
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
#1304
Author: Lesley Anne
Date: Tue, 20 Feb 2007 16:41
86 lines
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
#1306
Author: Remy Lebeau (TeamB)
Date: Tue, 20 Feb 2007 18:49
12 lines
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
#1307
Author: Lesley Anne
Date: Tue, 20 Feb 2007 21:53
12 lines
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
#1308
Author: Lesley Anne
Date: Tue, 20 Feb 2007 21:58
9 lines
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
#1309
Author: Remy Lebeau (TeamB)
Date: Tue, 20 Feb 2007 23:04
18 lines
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
#1310
Author: Bob Gonder
Date: Wed, 21 Feb 2007 02:24
68 lines
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
#1311
Author: Paul at NCF
Date: Wed, 21 Feb 2007 08:58
8 lines
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
#1326
Author: Lesley Anne
Date: Wed, 21 Feb 2007 22:00
113 lines
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
#1330
Author: Paul at NCF
Date: Thu, 22 Feb 2007 08:34
32 lines
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
#1235
Author: Lesley Anne
Date: Thu, 22 Feb 2007 20:04
62 lines
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
#1238
Author: Paul at NCF
Date: Thu, 22 Feb 2007 22:46
56 lines
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
#1239
Author: Lesley Anne
Date: Thu, 22 Feb 2007 23:25
42 lines
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
#1243
Author: Bob Gonder
Date: Fri, 23 Feb 2007 04:31
118 lines
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
#1244
Author: Paul at NCF
Date: Fri, 23 Feb 2007 08:04
96 lines
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
#1248
Author: Michael Harris
Date: Fri, 23 Feb 2007 15:20
64 lines
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