Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

SimpleMulti.cxx

00001 /*
00002  * Copyright 1991-2002 Eric M. Hopper <hopper@omnifarious.org>
00003  * 
00004  *     This program is free software; you can redistribute it and/or modify it
00005  *     under the terms of the GNU Lesser General Public License as published
00006  *     by the Free Software Foundation; either version 2 of the License, or
00007  *     (at your option) any later version.
00008  * 
00009  *     This program is distributed in the hope that it will be useful, but
00010  *     WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *     Lesser General Public License for more details.
00013  * 
00014  *     You should have received a copy of the GNU Lesser General Public
00015  *     License along with this program; if not, write to the Free Software
00016  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 /* $Header: /home/hopper/src/cvs/C++/StrMod/SimpleMulti.cxx,v 1.11 2002/11/25 05:40:05 hopper Exp $ */
00020 
00021 // For a log, see ChangeLog
00022 
00023 // Revision 1.2  1996/08/31 15:45:03  hopper
00024 // Fixed several bugs in class SimpleMultiplexer that caused it to lock up
00025 // when searching for the plug list.
00026 //
00027 // Revision 1.1  1996/08/24 12:55:52  hopper
00028 // New source module for SimpleMultiplexer class, and related plug classes.
00029 //
00030 
00031 #ifdef __GNUG__
00032 #  pragma implementation "SimpleMulti.h"
00033 #endif
00034 
00035 #include "StrMod/SimpleMulti.h"
00036 #include "StrMod/StrChunk.h"
00037 #include <UniEvent/Dispatcher.h>
00038 #include <UniEvent/Event.h>
00039 #include <UniEvent/EventPtr.h>
00040 #include <algorithm>
00041 #include <cassert>
00042 
00043 namespace strmod {
00044 namespace strmod {
00045 
00046 /** A private class for StrPlugs on the 'many' side of a SimpleMultiplexer.
00047  */
00048 class SimpleMultiplexer::MultiPlug : public StreamModule::Plug
00049 {
00050    friend class SimpleMultiplexer;
00051    friend class SinglePlug;
00052  public:
00053    static const STR_ClassIdent identifier;
00054 
00055    inline virtual int AreYouA(const lcore::ClassIdent &cid) const;
00056 
00057    inline SimpleMultiplexer &getParent() const;
00058 
00059    virtual int side() const                           { return(MultiSide); }
00060 
00061    //! Get the 'has_written_' flag of this plug.
00062    bool getHasWritten() const                         { return(has_written_); }
00063 
00064    //! Set the 'has_written_' flag of this plug.
00065    void setHasWritten(bool nval)                      { has_written_ = nval; }
00066 
00067  protected:
00068    //! A constructor.
00069    inline MultiPlug(SimpleMultiplexer &parent);
00070    //! A destructor.  Calls unPlug()
00071    inline virtual ~MultiPlug();
00072 
00073    virtual const lcore::ClassIdent *i_GetIdent() const  { return &identifier; }
00074 
00075    virtual const StrChunkPtr i_Read();
00076 
00077    virtual void i_Write(const StrChunkPtr &ptr);
00078 
00079    virtual bool needsNotifyWriteable() const        { return(true); }
00080    virtual bool needsNotifyReadable() const         { return(true); }
00081 
00082    virtual void otherIsReadable();
00083 
00084    virtual void otherIsWriteable();
00085 
00086    /** Set the list position of this plug.
00087     * This function is to set the listpos_ iterator that's used for quick list
00088     * operations.  In fact, the primary purpose of this variable is to set it up
00089     * so the list doesn't have to be scanned to find this plug when the plug
00090     * needs to be moved to the bottom of the list.
00091     *
00092     * Of course, this means that the data structure the plugs are stored in
00093     * needs to preserve iterators through list operations.
00094     */
00095    void setListPos(const MPlugList::iterator &newpos) { listpos_ = newpos; }
00096 
00097    //! Get the list position of this plug.  See setListPos for more details.
00098    const MPlugList::iterator &getListPos() const    { return(listpos_); }
00099 
00100  private:
00101    MPlugList::iterator listpos_;
00102    bool has_written_;
00103    bool other_iswriteable_;
00104    bool other_isreadable_;
00105 };
00106 
00107 //! The event that is triggered when the SimpleMultiplexer should scan the
00108 //! MultiPlugs for ones that are readable.
00109 class SimpleMultiplexer::ScanEvent : public unievent::Event {
00110  private:
00111    typedef unievent::Dispatcher Dispatcher;
00112  public:
00113    static const STR_ClassIdent identifier;
00114 
00115    //! This keeps a reference to parent.
00116    ScanEvent(SimpleMultiplexer &parent) : parent_(&parent)                   { }
00117 
00118    inline virtual void triggerEvent(Dispatcher *dispatcher = 0);
00119 
00120    void parentGone()                                    { parent_ = 0; }
00121 
00122  protected:
00123    virtual const lcore::ClassIdent *i_GetIdent()        { return &identifier; }
00124 
00125  private:
00126    SimpleMultiplexer *parent_;
00127 };
00128 
00129 int SimpleMultiplexer::MultiPlug::AreYouA(const lcore::ClassIdent &cid) const
00130 {
00131    return((identifier == cid) || Plug::AreYouA(cid));
00132 }
00133 
00134 SimpleMultiplexer &SimpleMultiplexer::MultiPlug::getParent() const
00135 {
00136    return(static_cast<SimpleMultiplexer &>(Plug::getParent()));
00137 }
00138 
00139 const STR_ClassIdent SimpleMultiplexer::identifier(21UL);
00140 const STR_ClassIdent SimpleMultiplexer::MultiPlug::identifier(22UL);
00141 const STR_ClassIdent SimpleMultiplexer::SinglePlug::identifier(23UL);
00142 const STR_ClassIdent SimpleMultiplexer::ScanEvent::identifier(32UL);
00143 
00144 SimpleMultiplexer::MultiPlug::MultiPlug(SimpleMultiplexer &parent)
00145      : Plug(parent), has_written_(false),
00146        other_iswriteable_(false), other_isreadable_(false)
00147 {
00148 }
00149 
00150 SimpleMultiplexer::MultiPlug::~MultiPlug()
00151 {
00152    unPlug();
00153 }
00154 
00155 const StrChunkPtr SimpleMultiplexer::MultiPlug::i_Read()
00156 {
00157 //     cerr << "SimpleMultiplexer::MultiPlug::i_Read\n";
00158    SimpleMultiplexer &parent = getParent();
00159 
00160    assert(parent.mchunk_ && (parent.readable_multis_ > 0));
00161 
00162    StrChunkPtr retval = parent.mchunk_;
00163 
00164    parent.multiDidRead(*this);
00165 
00166    return(retval);
00167 }
00168 
00169 void SimpleMultiplexer::MultiPlug::i_Write(const StrChunkPtr &ptr)
00170 {
00171 //     cerr << "SimpleMultiplexer::MultiPlug::i_Write\n";
00172    // Make sure that I'm allowed to be written to.
00173    assert(!has_written_);
00174    assert(getFlagsFrom(*this).canwrite_);
00175    // Make sure that the world knows I'm being written to.
00176    assert(getFlagsFrom(*this).iswriting_);
00177 
00178    // Set up a local alias for my parent.
00179    SimpleMultiplexer &parent = getParent();
00180 
00181    // Make sure that the plug I need to write to exists, and can be written to.
00182    assert((parent.splug_.pluggedInto() != 0)
00183           && (parent.splug_.pluggedInto()->isWriteable()));
00184 
00185    // I can't be written to again.
00186    has_written_ = true;
00187    setWriteable(false);
00188    // Treat has_written_ going to true as other.canread_ going to false.
00189    // This means that my parent module won't count me when trying to decide if
00190    // splug_.canread should be true or not.
00191    if (other_isreadable_)
00192    {
00193       parent.adjustMultiReadables(ADJ_Down);
00194    }
00195 
00196    // Make sure that setting those flags didn't somehow alter the other plug's
00197    // state.
00198    assert((parent.splug_.pluggedInto() != 0)
00199           && (parent.splug_.pluggedInto()->isWriteable()));
00200 
00201    // Get a reference to the other plug.
00202    Plug &other = *parent.splug_.pluggedInto();
00203 
00204    setIsWriting(other, true);
00205    // Preserve the old value of the isreading_ flag so it can be restored.
00206    //
00207    // I don't need to preserve the previous flag because I can be sure the
00208    // other's iswriting_ flag is false, because other.isWriteable() returns
00209    // true.
00210    bool oldisread = getFlagsFrom(parent.splug_).isreading_;
00211    setIsReading(parent.splug_, true);
00212    // Actually do the write.
00213    parent.splug_.writeOther(ptr);
00214    // Reset the flags.
00215    setIsWriting(other, false);
00216    setIsReading(parent.splug_, oldisread);
00217    // Tell my parent to post a scan event that will reset my has_written_
00218    // flag, as well as move me to the end of the list.
00219    parent.postScan(*this);
00220 }
00221 
00222 void SimpleMultiplexer::MultiPlug::otherIsReadable()
00223 {
00224 //     cerr << "SimpleMultiplexer::MultiPlug::otherIsReadable()\n";
00225 
00226    bool newreadable = false;
00227 
00228    if (pluggedInto() == 0)
00229    {
00230       newreadable = false;
00231    }
00232    else
00233    {
00234       newreadable = getFlagsFrom(*(pluggedInto())).canread_;
00235    }
00236    if (newreadable != other_isreadable_)
00237    {
00238       other_isreadable_ = newreadable;
00239       // If has_written_, the readable flag makes no difference, and the
00240       // parent has already been notified of my state.
00241       if (!has_written_)
00242       {
00243          getParent().adjustMultiReadables(newreadable ? ADJ_Up : ADJ_Down);
00244       }
00245    }
00246 }
00247 
00248 void SimpleMultiplexer::MultiPlug::otherIsWriteable()
00249 {
00250 //     cerr << "SimpleMultiplexer::MultiPlug::otherIsWriteable()\n";
00251 
00252    bool newwriteable = false;
00253 
00254    if (pluggedInto() == 0)
00255    {
00256       newwriteable = false;
00257    }
00258    else
00259    {
00260       newwriteable = getFlagsFrom(*(pluggedInto())).canwrite_;
00261    }
00262    if (newwriteable != other_iswriteable_)
00263    {
00264       other_iswriteable_ = newwriteable;
00265       getParent().adjustMultiWriteables(newwriteable ? ADJ_Up : ADJ_Down);
00266    }
00267 }
00268 
00269 void SimpleMultiplexer::ScanEvent::triggerEvent(Dispatcher *dispatcher)
00270 {
00271    if (parent_ != 0)
00272    {
00273       parent_->doScan();
00274    }
00275 }
00276 
00277 //: Used as a predicate to search for a MultiPlug to read from.
00278 class SimpleMultiplexer::mpother_readable_p {
00279  public:
00280    bool operator ()(MultiPlug *p) {
00281       return(!p->getHasWritten() && (p->pluggedInto() != 0)
00282              && p->pluggedInto()->isReadable());
00283    }
00284 };
00285 
00286 const StrChunkPtr SimpleMultiplexer::SinglePlug::i_Read()
00287 {
00288 //     cerr << "SimpleMultiplexer::SinglePlug::i_Read\n";
00289    SimpleMultiplexer &parent = getParent();
00290    MPlugList::iterator pptr;
00291    {
00292       MPlugList::iterator end = parent.mplugs_.end();
00293       pptr = find_if(parent.mplugs_.begin(), end,
00294                      mpother_readable_p());
00295       assert(pptr != end);
00296    }
00297    MultiPlug &sibling = *(*pptr);
00298    assert(sibling.pluggedInto() != 0);
00299    assert(sibling.getHasWritten() == false);
00300    Plug &other = *(sibling.pluggedInto());
00301    assert(other.isReadable());
00302 
00303    parent.setWriteableFlagFor(&sibling, false);
00304    sibling.setHasWritten(true);
00305    parent.postScan(sibling);
00306    parent.adjustMultiReadables(ADJ_Down);
00307    assert(other.isReadable());
00308 
00309    setIsReading(other, true);
00310    StrChunkPtr retval = sibling.readOther();
00311    setIsReading(other, false);
00312 
00313    return(retval);
00314 }
00315 
00316 //: Automatically delete an array when it goes out of scope.
00317 class SimpleMultiplexer::auto_mpptr
00318 {
00319  public:
00320    auto_mpptr(MultiPlug **p) : p_(p)  { }
00321    ~auto_mpptr()                     { delete[] p_; }
00322 
00323  private:
00324    MultiPlug **p_;
00325 };
00326 
00327 //: Find MultiPlugs that are plugged in.
00328 class SimpleMultiplexer::mp_notpluggedin_p {
00329  public:
00330    bool operator ()(Plug *p) {
00331       return(p->pluggedInto() == 0);
00332    }
00333 };
00334 
00335 void SimpleMultiplexer::SinglePlug::i_Write(const StrChunkPtr &chnk)
00336 {
00337 //     cerr << "SimpleMultiplexer::SinglePlug::i_Write\n";
00338    SimpleMultiplexer &parent = getParent();
00339    assert(!parent.mchunk_);
00340    MultiPlug **scanplugs = new MultiPlug *[parent.mplugs_.size()];
00341    auto_mpptr delary(scanplugs);
00342 
00343    MultiPlug **endscan = remove_copy_if(parent.mplugs_.begin(),
00344                                         parent.mplugs_.end(),
00345                                         scanplugs,
00346                                         mp_notpluggedin_p());
00347    parent.mchunk_ = chnk;
00348    parent.readable_multis_ = endscan - scanplugs;
00349    setWriteable(false);
00350    for (MultiPlug **curppp = scanplugs; curppp != endscan; ++curppp)
00351    {
00352       MultiPlug &curp = **curppp;
00353       assert(getFlagsFrom(curp).canread_ == false);
00354       if (curp.pluggedInto() != 0)
00355       {
00356 //       cerr << "Setting a multi readable.\n";
00357          curp.setReadable(true);
00358       }
00359       else
00360       {
00361          parent.multiDidRead(curp);
00362       }
00363    }
00364 }
00365 
00366 //! Find MultiPlugs that haven't been written to.
00367 class SimpleMultiplexer::mp_written_p {
00368  public:
00369    bool operator ()(MultiPlug *p) {
00370       return(p->getHasWritten());
00371    }
00372 };
00373 
00374 void SimpleMultiplexer::SinglePlug::otherIsWriteable()
00375 {
00376 //     cerr << "SimpleMultiplexer::SinglePlug::otherIsWriteable\n";
00377    SimpleMultiplexer &parent = getParent();
00378    MultiPlug **scanplugs = new MultiPlug *[parent.mplugs_.size()];
00379    auto_mpptr delary(scanplugs);
00380 
00381    MultiPlug **endscan = remove_copy_if(parent.mplugs_.begin(),
00382                                         parent.mplugs_.end(),
00383                                         scanplugs,
00384                                         mp_written_p());
00385 //     cerr << "Setting " << (endscan - scanplugs) << " plugs.\n";
00386    for (MultiPlug **curppp = scanplugs; curppp != endscan; ++curppp)
00387    {
00388       MultiPlug &curp = **curppp;
00389       assert(curp.getHasWritten() == false);
00390       curp.setWriteable(pluggedInto()
00391                         && getFlagsFrom(*pluggedInto()).canwrite_);
00392    }
00393 }
00394 
00395 SimpleMultiplexer::SimpleMultiplexer(unievent::Dispatcher &disp)
00396      : splug_(*this), splug_created_(false), scan_posted_(false),
00397        scan_(new ScanEvent(*this)), dispatcher_(disp),
00398        readable_multis_(0), readable_multiothers_(0), writeable_multiothers_(0)
00399 {
00400    scan_->AddReference();
00401 }
00402 
00403 SimpleMultiplexer::~SimpleMultiplexer()
00404 {
00405    scan_->parentGone();
00406    splug_.unPlug();
00407    while (mplugs_.size() > 0) {
00408       MultiPlug *mplug = mplugs_.front();
00409 
00410       mplug->setListPos(mplugs_.end());
00411       mplugs_.pop_front();
00412       mplug->unPlug();
00413       delete mplug;
00414    }
00415    scan_->DelReference();
00416    if (scan_->NumReferences() <= 0)
00417    {
00418       delete scan_;
00419    }
00420 }
00421 
00422 bool SimpleMultiplexer::ownsPlug(const Plug *plug) const
00423 {
00424    const StreamModule::Plug * const p = plug;
00425    if ((p == &splug_) && splug_created_) {
00426       return(true);
00427    } else {
00428       MPlugList::const_iterator end = mplugs_.end();
00429       return(find(mplugs_.begin(), end, plug) != end);
00430    }
00431 }
00432 
00433 void SimpleMultiplexer::doPost()
00434 {
00435    dispatcher_.addEvent(scan_);
00436 }
00437 
00438 void SimpleMultiplexer::moveToEnd(MultiPlug &toend)
00439 {
00440 //     cerr << "SimpleMultiplexer::moveToEnd\n";
00441    MPlugList::iterator oldpos = toend.getListPos();
00442    toend.setListPos(mplugs_.insert(mplugs_.end(), &toend));
00443    mplugs_.erase(oldpos);
00444 }
00445 
00446 void SimpleMultiplexer::multiDidRead(MultiPlug &mplug)
00447 {
00448 //     cerr << "SimpleMultiplexer::multiDidRead\n";
00449 //     cerr << "readable_multis_ == " << readable_multis_ << "\n";
00450    assert(readable_multis_ > 0);
00451    setReadableFlagFor(&mplug, false);
00452 
00453    if (--readable_multis_ == 0)
00454    {
00455 //        cerr << "readable_multis_ == 0 && writeable_multiothers_ == "
00456 //         << writeable_multiothers_ << "\n";
00457       mchunk_.ReleasePtr();
00458       if (writeable_multiothers_ > 0)
00459       {
00460          setWriteableFlagFor(&splug_, true);
00461       }
00462       else
00463       {
00464          setWriteableFlagFor(&splug_, false);
00465       }
00466    }
00467 }
00468 
00469 void SimpleMultiplexer::adjustMultiReadables(AdjDir dir)
00470 {
00471    assert((dir == ADJ_Down) || (dir == ADJ_Up));
00472    if (dir == ADJ_Down)
00473    {
00474       assert(readable_multiothers_ > 0);
00475       if (--readable_multiothers_ == 0)
00476       {
00477          setReadableFlagFor(&splug_, false);
00478       }
00479    }
00480    else
00481    {
00482       assert(readable_multiothers_ < mplugs_.size());
00483       ++readable_multiothers_;
00484       setReadableFlagFor(&splug_, true);
00485    }
00486 }
00487 
00488 void SimpleMultiplexer::adjustMultiWriteables(AdjDir dir)
00489 {
00490    assert((dir == ADJ_Down) || (dir == ADJ_Up));
00491    if (dir == ADJ_Down)
00492    {
00493       assert(writeable_multiothers_ > 0);
00494       if (--writeable_multiothers_ == 0)
00495       {
00496          setWriteableFlagFor(&splug_, false);
00497       }
00498    }
00499    else
00500    {
00501       assert(writeable_multiothers_ < mplugs_.size());
00502       ++writeable_multiothers_;
00503       if (!mchunk_)
00504       {
00505          setWriteableFlagFor(&splug_, true);
00506       }
00507    }
00508 }
00509 
00510 inline void SimpleMultiplexer::plugDisconnected(Plug *plug)
00511 {
00512    if (plug->side() == MultiSide)
00513    {
00514       MultiPlug * const mp = static_cast<MultiPlug *>(plug);
00515 
00516       if (mp->getFlagsFrom(*mp).canread_)
00517       {
00518          multiDidRead(*mp);
00519       }
00520       mp->otherIsReadable();
00521       mp->otherIsWriteable();
00522    }
00523    else
00524    {
00525       splug_.otherIsWriteable();
00526    }
00527 }
00528 
00529 StreamModule::Plug *SimpleMultiplexer::i_MakePlug(int side)
00530 {
00531    if ((side == SingleSide) && !splug_created_)
00532    {
00533       splug_created_ = true;
00534       return(&splug_);
00535    }
00536    else if (side == MultiSide)
00537    {
00538       MultiPlug *mp = new MultiPlug(*this);
00539       mplugs_.push_front(mp);
00540       mp->setListPos(mplugs_.begin());
00541       if (splug_.pluggedInto() != 0)
00542       {
00543          bool writeflag = splug_.getFlagsFrom(*splug_.pluggedInto()).canread_;
00544          setWriteableFlagFor(mp, writeflag);
00545       }
00546       return(mp);
00547    }
00548    else
00549    {
00550       assert(false);
00551       return(0);
00552    }
00553 }
00554 
00555 void SimpleMultiplexer::doScan()
00556 {
00557 //     cerr << "SimpleMultiplexer::doScan\n";
00558 
00559    while (delplugs_.size() > 0)
00560    {
00561       MultiPlug * const mp = delplugs_.front();
00562       assert(mp->pluggedInto() == 0);
00563       MPlugList::iterator pos = mp->getListPos();
00564       mp->setListPos(mplugs_.end());
00565       mplugs_.erase(pos);
00566       delplugs_.pop_front();
00567       delete mp;
00568    }
00569 
00570    scan_posted_ = false;
00571    MultiPlug **scanplugs = new MultiPlug *[mplugs_.size()];
00572    auto_mpptr delary(scanplugs);
00573 
00574    MultiPlug **endscan = copy(mplugs_.begin(), mplugs_.end(), scanplugs);
00575    for (MultiPlug **curppp = scanplugs; curppp != endscan; ++curppp)
00576    {
00577       MultiPlug &curp = **curppp;
00578       if (curp.getHasWritten())
00579       {
00580 //       cerr << "Setting a true has_written to false.\n";
00581          assert(splug_.getFlagsFrom(curp).canwrite_ == false);
00582          curp.setHasWritten(false);
00583          if (curp.pluggedInto()
00584              && curp.getFlagsFrom(*curp.pluggedInto()).canread_)
00585          {
00586             adjustMultiReadables(ADJ_Up);
00587          }
00588       }
00589       if (!curp.getHasWritten())
00590       {
00591          const bool writeflag = splug_.pluggedInto()
00592             && splug_.getFlagsFrom(*splug_.pluggedInto()).canwrite_;
00593 //       if (splug_.getFlagsFrom(curp).canwrite_ == false && writeflag)
00594 //       {
00595 //          cerr << "DS: Going from 0 to 1\n";
00596 //       }
00597          curp.setWriteable(writeflag);
00598       }
00599    }
00600 //     cerr << "------SimpleMultiplexer::doScan\n";
00601 }
00602 
00603 bool SimpleMultiplexer::deletePlug(Plug *plug)
00604 {
00605    if ((&splug_ == plug) && splug_created_)
00606    {
00607       splug_.unPlug();
00608       splug_created_ = false;
00609       return(true);
00610    }
00611    else
00612    {
00613       MPlugList::iterator end = mplugs_.end();
00614       MPlugList::iterator search = find(mplugs_.begin(), end, plug);
00615       if (search != end)
00616       {
00617          MultiPlug * const mp = *search;
00618          assert(mp == plug);
00619          mp->unPlug();
00620          delplugs_.push_front(mp);
00621          postScan(*mp);
00622          return(true);
00623       }
00624    }
00625    return false;
00626 }
00627 
00628 }  // End namespace strmod
00629 }  // End namespace strmod

Generated on Wed Jan 29 00:32:44 2003 for libNet by doxygen1.3-rc1