Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Apr : Re: TIdTCPServer - threads, global data contd.
| Subject: | Re: TIdTCPServer - threads, global data contd. |
| Posted by: | "Stephen Odgaard" (s..@cowi.dk) |
| Date: | Sun, 16 Apr 2006 11:18:50 |
"Martin James" <mjames_falcon@dial.pipex.com> wrote in message
news:4441d06c$1@newsgroups.borland.com...
>> The purpose of the Application is to work as a sort of Router, which
>> means
>> that I have to send data from Clients connected, but I also have data
>> initiated by the server (or other Clients to be sent to a given Client)
>
> So, a 'chat' type app.
For the discussion here you can compare it to that. (A n-connection 'chat'
type);
>> As I read the thread the conclusion is that data should be stored in eg.
>> a
>> TThreadList, or in a special strucuture with a critical section.
>
> Producer-Consumer queues can be useful.
>
>> A couple of
>> questions:
>>
>> Reg. the OnExecute Event handler:
>> 1a) Can I from the main thread get access to a list of connected clients
>> (ie. with connected clients)
>
> Mostly, yes. Indy servers have a lockable list of client contexts. The
> is,
> AFAIK, an area of uncertainty in that, for a short period, a connection
> may
> be disconnected but not yet removed from the list. You need to bear that
> in
> mind - you might occasionally get an exception you do not want <g>
>
>> 1b) What initiatiates the OnExecute Event?
>
> It is called in a continuous loop. The caller is a thread, either
> directly
> or, in the case of Indy 10 supercore, via a fiber.
I need to understand exactly what you mean by continuous loop, but blocking
(u mentioned elsewhere).
The question is thus by what and how often it is unblocked?
>> 1c) How can I (eg. from the main thread) "provoke" the OnExecute Event
>> handler of a given client to be executed in order to empty an outbound
> info
>> queue?
>
> One way is to conver the socket to non-blocking, (see 'Interrupting
> ReadFromStack call?' thread from Aart). This allows a thread to wait on
> both a socket and a synchro object, (eg. a semaphore), associated with an
> object queue.
>
Ok, this sounds like a way to go, although getting somewhat complex with the
changing of the behaviour.
>> 1d) How would you store data in relation to a specific client for use
> during
>> later Execute handles (or another thread)?
>
> Well, this is the thing. The data can be stored in a buffer field of a
> data-carrier class. A client reads data into an instance, arranges for it
> to be 'routed/distributed/queued' and creates, (or depools), another one
> to
> receive data during the next read.
>
> The awkward issue is controlling the lifetime of the objects if they are
> queued to multiple threads. A reference count in the object could allow
> the
> object to be freed when the last thread has finished using it. The object
> would need a CS to protect its refCount. The refCount would also need to
> be
> decremented upon a client disconnect so that buffers are not left lying
> around by disappearing clients.
>
> If your 'router' only routes between pairs of clients, and does not
> 'broadcast', then the issue does not arise.
Well what I actually want is to store some client specific information (eg.
about the capabilities of the connected client).
After looking futher into this, it would seem that using the AContext.Data
object should be a handle at my disposition for a user defined class.
Correct?
>
>> Thread safe global data.
>> You mentioned the TThreadList as an example of storing threadsafe data.
> And
>> alternatively to make ones own class with a critical section.
>> 2a) Do you have an example of such a class implementation for my
>> inspiration?
>> 2b) Do you Mathijs have any working code example based on the discussion
> you
>> had with Martin and Remy that you could share?
>>
>
> A P-C queue class that a thread can wait on for objects is fairly easily
> built, (see class at end of post). The difficulty is that the client
> socket
> is normally set to blocking and so does not allow the thread calling
> 'onExecute' to wait on both the socket and the object queue. Aart gets
> round
> this by setting the socket to non-blocking for the wait and setting it
> back
> to blocking again once the wait has been signalled. This involves extra
> kernel calls, but appears to work OK. The socket could be left as
> non-blocking, but the effect on Indy of such a design is somewhat
> unknown -
> ReadFromStack calls would presumably be OK, (since the socket would be
> guaranteed to be readable if an FD_READ status was returned after the
> socket
> wait was signalled), but, as Aart says, it is possible that writes would
> return with a 'operation would block' exception, so requiring
> non-blocking-type state-machine coding or some bodge, eg. setting the
> socket
> to blocking mode only after such an exception and re-writing.
Ok, this sounds a bit concerning to me when you say the effects on Indy is
somewhat unknown.
"Martin James" <mjames_falcon@dial.pipex.com> wrote in message
news:4441d36d@newsgroups.borland.com...
>
> "Stephen Odgaard" <sso@cowi.dk> wrote in message
> news:44418887$1@newsgroups.borland.com...
>> Trying to answer some of my own questions. (please correct me if im wrong
> or
>> an unsafe route)
>> I should just mention that im using Indy v. 10
>>
>> It seems the key to 1a) and 1d) would be to maintain a TThreadlist of
>> connected TIDContext's with their individual data.
>> 1c) should then be possible to perform by getting the AContext from the
>> TThreadlist above and then a) either executing the AContext.OnRun event,
> or
>> b)even simpler by executing AContext.Connection.IOHandler.WriteLn()
>>
>> Is the above understanding/approach correct and safe?
>
> Sort of. You should not issue any call that might block from inside a
> lock.
> Doing so allows the possibility that an interrupted connection, (eg.
> crashed
> client, or cable fell out of router), could cause the write call to block
> for an extended period. During this period, your app would be stuck
> because
> nothing else could get at the list. If you get the context from the list
> and then unlock the list before attempting the write, there is the
> possibility that the client may disconnect and the context be freed before
> the write starts, resulting an an AV. I'm afraid that Indy is a but
> awkward
> to use in this respect.
>
> IMHO, it's much safer overall if the data can be queued, rather than
> attempting direct writes.
Right - In order to assure an awareness of what clients are connected to the
server, I would have to get access to a list of active AContexts.
They must be maintained by either the TidTCPServer or its assigned
TidScheduler. Can you point me in the right direction on how to get this
awareness?
The P-C queue could actually be part of the AContext.Data structure could it
not? That way the AContext.Connect and .Disconnect could create it and free
it again.
Juging by the "Interrupting ReadFromStack call?" thread this method is not
very common (only Aart), and I could be concerned of unknown problems. So
before selecting this method I would like to hear if there are other
alternative approaches.
> Rgds,
> Martin
Cheers
Stephen