/* * The Unix Channel * * by Michel Beaudouin-Lafon * * Copyright 1990-1993 * Laboratoire de Recherche en Informatique (LRI) * * Client side: services * * $Id$ * $CurLog$ */ #include "Service.h" /*?class UchService An object of class \typ{UchService} (derived from \typ{UchMsgStream}) exists in a client process to represent the server it is connected to. The class \typ{UchService} is virtual: you must create subclasses that redefine at least \fun{NewMessage} from class \fun{UchMsgStream}, and if necessary \fun{ConvertAnswer}. \fun{NewMessage} should decipher the incoming message, transform it into an event, and put in into the event queue with \fun{PutEvent}. It can also handle non event messages (for instance errors). ?*/ /*?class UchEvtMsgQueue An event queue is a linked list of events. Events are normally appended to the end of the queue and extracted from the beginning. ?*/ /*?nodoc?*/ UchEvtMsgQueue :: UchEvtMsgQueue () : CcuSmartData (), Queue () { } /*?nodoc?*/ UchEvtMsgQueue :: ~UchEvtMsgQueue () { } #ifdef DOC /*? Append an event to the queue. ?*/ void UchEvtMsgQueue :: Put (UchEventMsg* msg) { } /*? Put an event back in the queue (therefore it becomes the first event of the queue). ?*/ void UchEvtMsgQueue :: PutBack (UchEventMsg* msg) { } /*? Return the first event from the queue and remove it. ?*/ UchEventMsg* UchEvtMsgQueue :: Get () { } /*? Return the first event from the queue without removing it. ?*/ UchEventMsg* UchEvtMsgQueue :: Peek () { } #endif /* DOC */ /*? Construct an empty service. ?*/ UchService :: UchService () : UchMsgStream (), EvQueue (0) { } /*? Construct a service connected to address \var{a}. ?*/ UchService :: UchService (UchAddress* a) : UchMsgStream (0, a), EvQueue (0) { } /*?nodoc?*/ UchService :: UchService (const UchService& s) : UchMsgStream (s), EvQueue (s.EvQueue) { } /*?nodoc?*/ UchService :: ~UchService () { EvQueue = 0; // deletes it } /*?nodoc?*/ UchChannel* UchService :: Copy () const { return new UchService (*this); } /*? Set the event queue to be used to store the incoming events. If not set, a default event queue is created. This function is intended to share a queue between several servers. ?*/ void UchService :: SetEvQueue (UchEvtMsgQueue* evq) { EvQueue = evq; } /*?nextdoc?*/ UchEventMsg* UchService :: PeekEvent (bool wait) { Flush (); if (!EvQueue) EvQueue = new UchEvtMsgQueue; UchEventMsg* ev = EvQueue->Peek (); if (ev || ! wait) return ev; while (! ev) { HandleRead (); ev = EvQueue->Peek (); } return ev; } /*? These functions flush the output buffer, and check the events already in the queue. If there is at least one, it is returned; \fun{GetEvent} also removes it from the event queue. If the event queue is empty and \var{wait} is TRUE, the function blocks until an event arrives, else it returns 0 without blocking. These functions also create the event queue if it was not set with \fun{SetEvQueue}. ?*/ UchEventMsg* UchService :: GetEvent (bool wait) { Flush (); if (!EvQueue) EvQueue = new UchEvtMsgQueue; UchEventMsg* ev = EvQueue->Get (); if (ev || ! wait) return ev; while (! ev) { HandleRead (); ev = EvQueue->Get (); } return ev; } /*?nextdoc?*/ void UchService :: PutEvent (UchEventMsg* ev) { if (! EvQueue) EvQueue = new UchEvtMsgQueue; ev->From = this; EvQueue->Put (ev); } /*? These functions are similar to the functions \fun{Put} and \fun{PutBack} on the event queue of the server. They create the event queue if it was not set with \fun{SetEvQueue}, and set the event's server. ?*/ void UchService :: PutBackEvent (UchEventMsg* ev) { if (! EvQueue) EvQueue = new UchEvtMsgQueue; ev->From = this; EvQueue->PutBack (ev); } /*?class UchEventMsg This class derives from \typ{UchMessage}, so it inherits the usual virtual functions \fun{ReadFrom} and \fun{WriteTo} that must be redefined in each derived class. An event is created when a client receives an asynchronous message from its server. Events are linked together in event queues. Events must derive from this class. ?*/ #ifdef DOC // fake entries for inline functions /*? Construct an event. ?*/ UchEventMsg :: UchEventMsg () { } #endif /* DOC */ /*?nodoc?*/ UchEventMsg :: ~UchEventMsg () { } #ifdef DOC /*? Return the server that sent this event. The service is known only if the event was appended to the event queue with \fun{UchService::PutEvent}, else it is 0. ?*/ UchService* UchEventMsg :: GetService () { } #endif /* DOC */ /*?class UchGenEvtMsg This is a sample derived class of \typ{UchEventMsg}. It defines events that contain a pointer to a \typ{UchMessage}. This message must be allocated dynamically because it is deleted by the destructor. The virtual functions \fun{ReadFrom} and \fun{WriteTo} are defined to act upon the message stored in the event. The following example fetches a word from the input buffer, creates a message depending on its value (\typ{FOO_MSG} and \typ{BAR_MSG} have been derived from \typ{UchMessage}), and transfers the data from the buffer to the event with \fun{Get}. This piece of code typically appears in the body of \fun{NewMessage}: \begin{ccode} UchGenEvtMsg* ev = new UchGenEvtMsg; sword type; if (! buf.Peek (&type)) return; switch (type) { case Foo : ev->SetMsg (new FOO_MSG); break; case Bar : ev->SetMsg (new BAR_MSG); break; ... } if (!buf.Get (ev)) // protocol error PutEvent (ev); \end{ccode} ?*/ #ifdef DOC /*?nextdoc?*/ UchGenEvtMsg :: UchGenEvtMsg () { } /*? Construct a generic event. The second constructor sets its message. The message is deleted when the event is destroyed. Thus the message must have been allocated dynamically. ?*/ UchGenEvtMsg :: UchGenEvtMsg (UchMessage* m) { } #endif /* DOC */ /*?nodoc?*/ UchGenEvtMsg :: ~UchGenEvtMsg () { if (Msg) delete Msg; } #ifdef DOC /*?nextdoc?*/ void UchGenEvtMsg :: SetMsg (UchMessage* m) { } /*? Set and get the message associated to the event. When setting the value, the previous message of the event (if any) is deleted. ?*/ UchMessage* UchGenEvtMsg :: GetMsg () { } #endif /* DOC */ /*?nodoc?*/ void UchGenEvtMsg :: ReadFrom (UchMsgBuffer& buf, lword l) { if (Msg) Msg->ReadFrom (buf, l); } /*?nodoc?*/ void UchGenEvtMsg :: WriteTo (UchMsgBuffer& buf) { if (Msg) Msg->WriteTo (buf); }