// $Header: /cvs/gled-1.2/GledCore/Glasses/ZQueen.cxx,v 1.15 2005/05/26 17:34:39 matevz Exp $

// Copyright (C) 1999-2005, Matevz Tadel. All rights reserved.
// This file is part of GLED, released under GNU General Public License version 2.
// For the licensing terms see $GLEDSYS/LICENSE or http://www.gnu.org/.

//________________________________________________________________________
// ZQueen
//
// A Queen controls its portion of the ID space.
// Glasses are adopted/instantiated and enlightened by it.
// Queens (like Kings) are exported to all Moons but are not
// necesarily activated immediately (controled by bMandatory).
// Queen's status is given by bRuling.
//
// Although queens are always at least partially exposed, MIRs with
// alpa=queen are *not* broadcasted to all Moons by the Saturn's mir
// processing code. Exported methods that should be called also at
// inactive queens must be pushed there by hand. This service is
// offered by the BroadcastMIRToNonRulingReflections() method. For
// example see ZQueen::AddDependency.
//
//________________________________________________________________________

#include "ZQueen.h"
#include <Stones/ZComet.h>
#include "ZQueen.c7"
#include "ZKing.h"
#include <Ephra/Saturn.h>
#include <Gled/GledNS.h>

#include <Gled/GThread.h>
#include <TSystem.h>

ClassImp(ZQueen)

/**************************************************************************/

 void ZQueen::_init()
{
  bMandatory = false;
  bFollowDeps = false;
  bRuling = bAwaitingSceptre = false;

  mMinID = mMaxID = 0;
  mIDsUsed = mIDsPurged = mIDsFree;
  mAvgPurgLen = 32; mSgmPurgLen = 0.2;
  mPurgedMS = 5000; mDeletedMS = 5000;

  mZeroRCPolicy = ZRCP_Delete;

  bStamping   = true;
  bStampIdOps = false;

  mAuthMode = AM_LensThenQueen;
  mAlignment = A_Good;
  mMapNoneTo = ZMirFilter::R_Deny;
  mProtector = 0;

  mDeps = 0; mOrphans = 0;
}

/**************************************************************************/

 void ZQueen::bootstrap()
{
  // Initializes a new queen and creates mandatory lists.
  // Called from ZKing.
  // Not applicable for activation or instantiation of not Ruling Queens.

  static const string _eh("ZQueen::bootstrap ");

  assert(bRuling == false);
  assert(mIDMap.size() == 0 && mMinID && mMaxID > mMinID);
  assert(mPurgatory.size() == 0);  

  try {
    mQueen = this;
    SetStamps(1);
    mSaturn->Enlight(this, mMinID);
  } catch(string exc) {
    ISerr(_eh + "Queen enlightenment failed.");
    throw;
  }
  mIDMap.insert
    ( pair<ID_t,LensDetails*>
      (mSaturnID, produce_lens_details(mSaturnID, this))
    ).first;
  mCreationID = mSaturnID + 1;
  mIDsUsed   = 1;
  mIDsPurged = 0;
  mIDsFree   = mIDSpan - 1;

  ZHashList* l = new ZHashList(GForm("Deps of %s", mName.Data()));
  l->SetElementFID(ZQueen::FID());
  CheckIn(l); SetDeps(l); l->SetMIRActive(false);

  l = new ZHashList(GForm("Orphans of %s", mName.Data()));
  CheckIn(l); SetOrphans(l); l->SetMIRActive(false);

  bRuling = true;
}

 void ZQueen::embrace_of_life(ZComet& comet)
{
  // Initializes *this* queen with unstreamed queen.
  // Copies Links and List contents, no ref-counting is performed.

  ZQueen* queen = comet.mQueen;
  assert(VFID() == queen->VFID());

  { // Re-stream the queen and read it back into *this*
    TBuffer qb(TBuffer::kWrite);
    queen->Streamer(qb);
    qb.SetReadMode(); qb.SetBufferOffset(0);
    ZHashList* ex_deps = mDeps;
    Streamer(qb);
    RebuildLinkRefs(&comet);
    RebuildListRefs(&comet);
    mDeps = ex_deps;
  }
}
/**************************************************************************/

 bool ZQueen::has_free_ids(ID_t n_needed)
{
  if(n_needed + 1 > mIDsFree) {
    release_purgatory(n_needed - mIDsFree + 1);
  } else {
    if(mIDsPurged > ID_t((1 + mSgmPurgLen)*mAvgPurgLen + 1)) {
      release_purgatory(0);
    }
  }
  return (n_needed <= mIDsFree);
}

 ID_t ZQueen::next_free_id(QueenIDMap_i i)
{
  static const string _eh("ZQueen::next_free_id ");

  bool wrapp = false;
  QueenIDMap_i j = i; ++j;
  while(1) {
    if(j != mIDMap.end()) {
      if(j->first > i->first + 1) break;
      i = j; ++j;
    } else {
      if(i->first < mMaxID) break;
      if(wrapp) throw(_eh + "looped twice over the end ... dying.");
      wrapp = true;
      i = mIDMap.begin(); j = i; ++j;
    }
  }
  // i holds one prior to free space; j is junk.
  return i->first + 1;
}

 ID_t ZQueen::assign_id(ZGlass* lens)
{
  // Assigns an ID to lens.

  static const string _eh("ZQueen::assign_ID ");

  if(mCreationID == 0) {
    mCreationID = next_free_id(mIDMap.begin());
  }

  ID_t this_id = mCreationID ? mCreationID : next_free_id(mIDMap.begin());

  pair<QueenIDMap_i, bool> poo = mIDMap.insert
    ( pair<ID_t, LensDetails*>
        (this_id, produce_lens_details(this_id, lens))
    );
  QueenIDMap_i ins_pos = poo.first;
  ++mIDsUsed; --mIDsFree;
  mCreationID = mIDsFree ? next_free_id(ins_pos) : 0;
  if(bStampIdOps) Stamp(FID());

  return this_id;
}

ZQueen::LensDetails* ZQueen::produce_lens_details(ID_t id, ZGlass* lens)
{
  return new LensDetails(lens);
}

 void ZQueen::release_purgatory(ID_t n_needed)
{
  // Releases ID-space occupied by recently dead lenses.
  // Also performs deletion of lenses that were still referenced
  // by viewers at their death-time.

  static const string _eh("ZQueen::release_purgatory ");

  ID_t n_cleared = 0;
  ID_t happy = ID_t((1 - mSgmPurgLen)*mAvgPurgLen);

  GTime now(GTime::I_Now);
  while(mIDsPurged && (n_needed || mIDsPurged > happy)) {

    ID_t id = mPurgatory.front();
    QueenIDMap_i i = mIDMap.find(id);
    assert(i != mIDMap.end());

    if(i->second->mState != LensDetails::LS_Dead)
      break;

    GTime dt = now - i->second->mDeletionTime;
    if(dt.ToMiliSec() < mDeletedMS)
      break;

    delete i->second;
    mIDMap.erase(i);
    mPurgatory.pop_front();
    --mIDsPurged; ++mIDsFree;
    if(n_needed) --n_needed;
    ++n_cleared;

  }

  // broadcast release_moon_purgatory
  if(n_cleared) {
    auto_ptr<ZMIR> moonir( S_release_moon_purgatory(n_cleared) );
    mSaturn->markup_posted_mir(*moonir, mSaturn->GetSaturnInfo());
    mSaturn->BroadcastMIR(*moonir, mReflectors);
  }

  if(n_cleared) {
    if(bStampIdOps) Stamp(FID());
    ISdebug(1, _eh + Identify() + GForm(" cleared %d entries.", n_cleared));
  }

  if(!mZombies.empty()) release_zombies();
}

 void ZQueen::release_moon_purgatory(ID_t n_to_release)
{
  // Mirrors purgatory release at moons.
  // Also performs deletion of lenses that were still referenced
  // by viewers at their death-time.

  static const string _eh("ZQueen::release_purgatory ");

  ID_t n_done = 0;
  while(n_done < n_to_release) {
    ID_t id = mPurgatory.front();
    mPurgatory.pop_front();
    QueenIDMap_i i = mIDMap.find(id);
    if(i != mIDMap.end()) {
      delete i->second;
      mIDMap.erase(i);
    } else {
      ISwarn("Safromastundhell.");
    }
    --mIDsPurged; ++mIDsFree;
    ++n_done;
  }
  ISdebug(1, _eh + Identify() + GForm(" cleared %d entries.", n_done));
  if(bStampIdOps) Stamp(FID());

  if(!mZombies.empty()) release_zombies();
}

 int ZQueen::release_zombies()
{
  static const string _eh("ZQueen::release_zombies ");

  int zc = 0;
  while( ! mZombies.empty() && mZombies.front()->mEyeRefCount == 0) {
    ISdebug(0, _eh + GForm("EyeRefCount=0 for %s. Killing a zombie.",
			   mZombies.front()->Identify().c_str()));
    delete mZombies.front();
    mZombies.pop_front();
    ++zc;
  }
  return zc;
}

/**************************************************************************/
// Lens ID/ptr handling
/**************************************************************************/

 ZGlass* ZQueen::DemangleID(ID_t id)
{
  // This should serve to properly demangle external references for comets.
  // And check that the id is within the queen's dependencies.

  ZGlass* l = mSaturn->DemangleID(id);
  if(l && DependsOn(l->mQueen))
    return l;
  return 0;
}

/**************************************************************************/

 ID_t ZQueen::CheckIn(ZGlass* lens)
{
  static const string _eh("ZQueen::CheckIn ");

  GLensWriteHolder _wrlck(this);
  if(mKing->GetLightType() != ZKing::LT_Moon) {
    if( ! has_free_ids(1) ) {
      throw(_eh + "no ID space available.");
    }
  }
  ID_t new_id;
  try {
    new_id = assign_id(lens);
  }
  catch(string exc) {
    ISerr(_eh + "ID assignment failed: " + exc);
    throw;
  }
  try {
    lens->mQueen = this;
    lens->SetStamps(1);
    mSaturn->Enlight(lens, new_id);
  }
  catch(string exc) {
    ISerr(_eh + "enlightenment failed: " + exc);
    // THIS INDICATES A SERIOUS BUG: fail miseribly.
    fprintf(stderr, "%s detected inconsistency in Saturn ID management. "
	    "Stopping server.n", _eh.c_str());
    mSaturn->Shutdown();
    throw;
  }
  return new_id;
}

/**************************************************************************/

 void ZQueen::DepCheckMIR(ZMIR& mir)
{
  // Allow beta/gamma from queens that *this* depends on + queen
  // arguments if alpha is *this*.

  static const string _eh("ZQueen::DepCheckMIR ");

  if(mir.Beta) {
    if(! DependsOn(mir.Beta->mQueen) && (mir.Alpha!=this || mir.Beta!=mir.Beta->mQueen))
      {
	if(bFollowDeps) {
	  // !!!! missing code to add the dependency etc.
	} else {
	  throw(_eh + GForm("beta '%s', id=%d: dependency check failed.",
			    mir.Beta->GetName(), mir.BetaID));
	}
      }
  }
  if(mir.Gamma) {
    if(! DependsOn(mir.Gamma->mQueen) && (mir.Alpha!=this || mir.Gamma!=mir.Gamma->mQueen))
      {
	if(bFollowDeps) {
	  // !!!! missing code to add the dependency etc.
	} else {
	  throw(_eh + GForm("gamma '%s', id=%d: dependency check failed.",
			    mir.Gamma->GetName(), mir.GammaID));
	}
      }
  }
}

 void ZQueen::BlessMIR(ZMIR& mir)
{
  // should check that the mir is valid and within deps.
  // If whorequeen should accept new ones and append them.
  // If light-queen with FollowDeps should append with broadcast
  // PRIOR to returning from this method.
  // Called from Saturn::Unfold() prior to locking and execution.
  // Errors/non-conformance should be reported by throwing an exception.
  //
  // Oh yess ... btw ... queens can as well block all execs to their
  // subjects. Eg, when bMIRActive of alpha is false.
  // Per queen access control can be implemented here.

  static string _eh("ZQueen::BlessMIR() ");

  if(!bRuling && mir.Alpha != this)
    throw(_eh + "spooky ... not ruling, but alpha demangled");
  
  if(!mir.Alpha->GetMIRActive())
    throw(_eh + "alpha '" + mir.Alpha->GetName() + "' not MIR active");

  // Dependency check
  DepCheckMIR(mir);

  /**************************************************************************/

  // Authorization

  // Allow everything if UseAuth is false
  if(mSaturn->GetSaturnInfo()->GetUseAuth() == false) {
    return;
  }

  // Always allow SunAbsolute access
  if(mir.Caller->HasIdentity(mSaturn->mSunInfo->GetPrimaryIdentity())) {
    return;
  }

  UChar_t result = ZMirFilter::R_None;
  if(mAuthMode != AM_None) {
    ZMirFilter* fs[2] = { 0, 0 };
    switch(mAuthMode) {
    case AM_Queen: fs[0] = mProtector; break;
    case AM_Lens:  fs[0] = mir.Alpha->mGuard; break;
    case AM_QueenThenLens: fs[0] = mProtector; fs[1] = mir.Alpha->mGuard; break;
    case AM_LensThenQueen: fs[0] = mir.Alpha->mGuard; fs[1] = mProtector; break;
    }
    for(int i=0; i<2; ++i) {
      if(fs[i] != 0) {
	result |= fs[i]->FilterMIR(mir);
	if(mAlignment == A_Good && result & ZMirFilter::R_Allow) return;
	if(mAlignment == A_Evil && result & ZMirFilter::R_Deny)  break;
      }
    }
  }
  if( result & ZMirFilter::R_Deny ||
      (result == ZMirFilter::R_None && mMapNoneTo == ZMirFilter::R_Deny))
    {
      throw(_eh + "access denied");
    }
}

/**************************************************************************/
// Dependencies
/**************************************************************************/

 void ZQueen::AddDependency(ZQueen* new_dep)
{
  static const string _eh("ZQueen::AddDependency ");

  assert_MIR_presence(_eh, ZGlass::MC_IsFlare);
}

 Bool_t ZQueen::DependsOn(ZQueen* some_queen)
{
  // Returns true if *this* depends on some_queen, i.e. if my lenses
  // can reference lenses ruled by some_queen.

  return some_queen == this || some_queen == (ZQueen*)mSaturn->GetSunQueen() ||
    mDeps->Has(some_queen);
}

/**************************************************************************/
// Instantiators
/**************************************************************************/

 ZGlass* ZQueen::instantiate(FID_t fid, const Text_t* name, const Text_t* title)
{
  ZGlass* g = GledNS::ConstructLens(fid);
  if(g == 0) throw(string("ZQueen::instantiate failed lens instantiation"));
  if(name)  g->mName  = name;
  if(title) g->mTitle = title;
  CheckIn(g);
  return g;
}

 ID_t ZQueen::InstantiateWAttach(ZGlass* attach_to, ZGlass* attach_gamma,
				LID_t att_lid, CID_t att_cid, MID_t att_mid,
				LID_t new_lid, CID_t new_cid,
				const Text_t* name, const Text_t* title)
{
  // Instantiates a glass of FID(lid, cid) and attaches it to attach_to
  // by using last part of the message, which should be a properly
  // formulated Ctx call.
  // Ctx for this call is:
  // alpha ~ attach_to, beta ~ new glass, gamma ~ attach_gamma
  // Returns ID to the caller (also via MirResult if set in MIR).

  static string _eh("ZQueen::InstantiateWAttach ");

  assert(bRuling);
  ZMIR* mir = assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  if(attach_to->GetQueen() != this)
    throw(_eh + "can only attach to my own subjects.");

  if(attach_to->GetMIRActive() == false)
    throw(_eh + "attach_to is not MIR active.");


  ZGlass* lens = GledNS::ConstructLens(FID_t(new_lid, new_cid));
  if(lens == 0) throw(_eh + "failed lens instantiation");

  try {
    CheckIn(lens);
  }
  catch(string exc) {
    delete lens;
    throw(_eh + exc);
  }
  if(name)  lens->SetName(name);
  if(title) lens->SetTitle(title);

  auto_ptr<ZMIR> att_mir( new ZMIR(attach_to, lens, attach_gamma) );
  att_mir->SetLCM_Ids(att_lid, att_cid, att_mid);
  att_mir->SetCaller(mir->Caller);
  try {
    // Should Bless? Yess ... but before lens construction ... then fix beta.
    // Or ... in this case ... could Request blessing by another queen.
    mSaturn->ExecMIR(att_mir);
  }
  catch(string exc) {
    // Attach failed ...
    ISwarn(_eh + "attachment failed: " + exc);
  }
  if(lens->mRefCount <= 0) {
    // Perhaps should delete it?
    ZeroRefCount(lens);
  }

  if(mir->HasResultReq()) {
    TBuffer b(TBuffer::kWrite);
    b << lens->mSaturnID;
    mSaturn->ShootMIRResult(b);
  }

  return lens->mSaturnID;
}

 ID_t ZQueen::IncarnateWAttach(ZGlass* attach_to, ZGlass* attach_gamma,
			      LID_t att_lid, CID_t att_cid, MID_t att_mid)
{
  // Incarnates a new glass from the buffer and then attaches it in the
  // same manner as the above method.
  // Links and list contents are rebuilt in accordance with queen's dependency
  // state.

  static string _eh("ZQueen::IncarnateWAttach ");

  assert(bRuling);
  ZMIR* mir = assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  if(attach_to->GetMIRActive() == false)
    throw(_eh + "attach_to is not MIR active.");


  ZGlass* lens = GledNS::StreamLens(*mir);
  if(lens == 0) throw(_eh + "lens unstreaming failed.");

  { // Rebuild link/list refs
    lens->mQueen = this;
    int n_failed = lens->RebuildAllRefs(this);
    if(n_failed > 0) {
      ISwarn(_eh + GForm("missed %d referenced lenses.", n_failed));
    }
  }

  try {
    CheckIn(lens);
  }
  catch(string exc) {
    lens->unreference_all();
    delete lens;
    throw(_eh + exc);
  }

  auto_ptr<ZMIR> att_mir( new ZMIR(attach_to, lens, attach_gamma) );
  att_mir->SetLCM_Ids(att_lid, att_cid, att_mid);
  att_mir->SetCaller(mir->Caller);
  try {
    mSaturn->ExecMIR(att_mir);
  }
  catch(string exc) {
    // Attach failed ...
    ISwarn(_eh + "attachment failed: " + exc);
  }
  if(lens->mRefCount <= 0) {
    // Perhaps should delete it?
    ZeroRefCount(lens);
  }

  if(mir->HasResultReq()) {
    TBuffer b(TBuffer::kWrite);
    b << lens->mSaturnID;
    mSaturn->ShootMIRResult(b);
  }

  return lens->mSaturnID;
}

/**************************************************************************/
// Lens MIR-activity
/**************************************************************************/

 void ZQueen::MIRActivateLens(ZGlass* lens)
{
  static const string _eh("ZQueen::MIRActivateLens ");

  assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  if(lens->mQueen != this)
    throw(_eh + "lens " + lens->Identify() + " is not my subject.");

  if(lens == this)
    throw(_eh + "will not apply to myself " + Identify() + ".");

  lens->SetMIRActive(true);
}

 void ZQueen::MIRDeactivateLens(ZGlass* lens)
{
  static const string _eh("ZQueen::MIRDeactivateLens ");

  assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  if(lens->mQueen != this)
    throw(_eh + "lens " + lens->Identify() + " is not my subject.");

  if(lens == this)
    throw(_eh + "will not apply to myself " + Identify() + ".");

  lens->SetMIRActive(false);
}

/**************************************************************************/
// Lens deletion
/**************************************************************************/

 void ZQueen::put_lens_to_purgatory(ZGlass* lens)
{
  static const string _eh("ZQueen::put_lens_to_purgatory ");

  QueenIDMap_i map_it;

  { // Check lens state consistency.
    map_it = mIDMap.find(lens->mSaturnID);
    if(map_it == mIDMap.end())
      throw(_eh + "lens " + lens->Identify() + " not found in my subject list.");
    if(map_it->second->mState != LensDetails::LS_Alive)
      throw(_eh + "lens " + lens->Identify() + " has already been put to death-bed.");
  }

  { // Stop threads.
    mSaturn->Freeze(lens);
  }

  GLensReadHolder qrd_lck(this);

  { // Fix lens state to dying. Already done on the Sun.
    ISdebug(2, _eh + "blocking new refs to " + lens->Identify() + ".");
    lens->bAcceptRefs = false;
    lens->mGlassBits |= ZGlassBits::kDying;
  }

  { // Remove all references from lens.
    ISdebug(2, _eh + "removing all refs from " + lens->Identify() + ".");
    GLensReadHolder rdlck(lens);
    lens->ClearAllReferences();
  }

  // printf("%s %s RC=%d, MRC=%d, SRC=%d, FRC=%dn", _eh.c_str(), lens->Identify().c_str(),
  //        lens->mRefCount, lens->mMoonRefCount, lens->mSunRefCount, lens->mFireRefCount);

  { // Remove all references to lens.
    ISdebug(2, _eh + "removing all refs to " + lens->Identify() + ".");
    set<ZGlass*> done_set;
    while( ! lens->mReverseRefs.empty() ) {
      hpZGlass2Int_i i = lens->mReverseRefs.begin();
      ZGlass* ref = i->first;
      Int_t n, nold = i->second;
      {
	GLensWriteHolder reflens_wrlck(ref);
	n = ref->RemoveReferencesTo(lens); // calls DecRefCount()
      }
      if(n != nold) {
	ISwarn(_eh + "remaining reference from " + ref->Identify() +
	       " to " + lens->Identify() + ".");
	lens->mReverseRefs.erase(i);
	continue;
      }
      // printf("Removing refs to %s from %s.n", lens->Identify().c_str(), ref->Identify().c_str());
    }
    if(lens->mRefCount != 0) {
      ISerr(_eh + "ref-count not zero after enforced reverse refs removal.");
      ISerr(GForm("%s %s  RC=%d, MRC=%d, SRC=%d, FRC=%dn",
		  _eh.c_str(), lens->Identify().c_str(),
		  lens->mRefCount, lens->mMoonRefCount,
		  lens->mSunRefCount, lens->mFireRefCount));
    }
  }

  // Emit the death Ray.
  auto_ptr<Ray> death_ray
    (Ray::PtrCtor(lens, RayNS::RQN_death, ++(lens->mTimeStamp), Ray::EB_StructuralChange));
  mSaturn->Shine(death_ray);

  map_it->second->mState = LensDetails::LS_Purged;
  mPurgatory.push_back(lens->mSaturnID);
  --mIDsUsed; ++mIDsPurged;

  if(mKing->GetLightType() != ZKing::LT_Moon) {
    SaturnInfo* si = mSaturn->GetSaturnInfo();
    GTime ref_time(GTime::I_Now);
    auto_ptr<ZMIR> void_mir( S_PutLensToVoid(lens->mSaturnID) );
    ref_time += 1000l*mPurgedMS;
    mSaturn->delayed_shoot_mir(void_mir, si, ref_time);
  }  
}

 void ZQueen::PutLensToPurgatory(ZGlass* lens)
{
  // First step in the process of a lens removal.
  // Stops all detached threads via Saturn::Freeze().
  // Removes all references to a lens and sends Rays with RQN_death.
  // Then emits MIR to PutLensToVoid(lens).

  static const string _eh("ZQueen::PutLensToPurgatory ");

  assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  if(lens->mQueen != this)
    throw(_eh + "lens " + lens->Identify() + " is not my subject.");

  GMutexHolder refcnt_lck(mSubjectRefCntMutex);
  put_lens_to_purgatory(lens);

  if(bStampIdOps) Stamp(FID());
}

 void ZQueen::PutListElementsToPurgatory(ZList* list)
{
  // Puts all elements of list having mQueen==this to purgatory.
  // Copies list contents, does ZList::ClearList and then purges
  // individual elements. This optimizes removal procedure for
  // large lists.

  static const string _eh("ZQueen::PutListElementsToPurgatory ");

  assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  if(list->mQueen != this)
    throw(_eh + "lens " + list->Identify() + " is not my subject.");

  GMutexHolder refcnt_lck(mSubjectRefCntMutex);

  // This simulates ZList::Clear list but is somewhat optimized.
  lpZGlass_t foo;
  { 
    GMutexHolder listreadlock(list->mReadMutex);
    GMutexHolder listlistlock(list->mListMutex);    
    foo.swap(list->mGlasses);
    list->clear_list();
    list->StampListClear();
  }

  for(lpZGlass_i i=foo.begin(); i!=foo.end(); ++i) {
    ZGlass* lens = *i;
    if(lens->mQueen == this  &&  lens != this) {
      {
	GMutexHolder lensreadlock(lens->mReadMutex);
	hpZGlass2Int_i i = lens->mReverseRefs.find(list);
	// Elements can appear in a list several times.
	if(i != lens->mReverseRefs.end()) {
	  list->ZGlass::remove_references_to(lens);
	  lens->dec_ref_count(i, i->second);
	  lens->mReverseRefs.erase(i);
	}
      }
      put_lens_to_purgatory(lens);
    } else {
      lens->DecRefCount(list);
    }
  }

  if(bStampIdOps) Stamp(FID());
}

 void ZQueen::PutLensToVoid(ID_t lens_id)
{
  // Final step in lens removal.
  // Endarks the lens.
  // Clears up internal structures.
  // Deletes the lens (or makes it a zombie).

  static const string _eh("ZQueen::PutLensToVoid ");

  assert_MIR_presence(_eh, ZGlass::MC_IsFlare);

  ZGlass* lens = mSaturn->DemangleID(lens_id);
  if(lens == 0) {
    // There is still a possibility that we are a moon and that
    // we connected between PutLensToPurgatory() and PutLensToVoid()
    // calls. Could check timing.
    if(mKing->GetLightType() == ZKing::LT_Moon) {
      GMutexHolder refcnt_lck(mSubjectRefCntMutex);
      QueenIDMap_i i = mIDMap.find(lens_id);
      if(i == mIDMap.end()) {
	ISerr(_eh + GForm("LensDetails for id=%d (expected as purged) not found in queen's ID map.", lens_id));
	return;
      }
      assert(i->second->mLens == 0);
      assert(i->second->mState == LensDetails::LS_Purged);
      i->second->mState = LensDetails::LS_Dead;
      i->second->mDeletionTime.SetNow();
      return;
    } else {
      throw(_eh + GForm("lens id=%d not elightened.", lens_id));
    }
  }

  if(lens->mQueen != this)
    throw(_eh + "lens " + lens->Identify() + " is not my subject.");

  // printf("%s %s RC=%d, MRC=%d, SRC=%d, FRC=%dn", _eh.c_str(), lens->Identify().c_str(),
  //	    lens->mRefCount, lens->mMoonRefCount, lens->mSunRefCount, lens->mFireRefCount);

  GMutexHolder refcnt_lck(mSubjectRefCntMutex);

  QueenIDMap_i i = mIDMap.find(lens_id);
  if(i == mIDMap.end()) {
    throw(_eh + "LensDetails of " + lens->Identify() + " not found in queen's ID map.");
  }
  if(i->second->mState != LensDetails::LS_Purged) {
    ISwarn(_eh + "lens has not been put to purged state ... expect trouble.");
  }
  if(i->second->mLens != lens) {
    ISwarn(_eh + "Saturn's and Queen's lens pointers do not mach ... expect trouble.");
  }
  ISdebug(2, _eh + "endarking lens " + lens->Identify() + ".");
  {
    GMutex::Lock_e lockp = lens->mReadMutex.TryLock();
    if(lockp != GMutex::ok) {
      ISwarn(_eh + GForm("failed acquiring ReadLock on '%s'. Proceeding anyway.",
			 lens->GetName()));
    }
    try {
      mSaturn->Endark(lens);
    }
    catch(string exc) {
      if(lockp == GMutex::ok) lens->mReadMutex.Unlock();
      throw(_eh + "endarkenment failed: " + exc + " Doing nothing.");
    }
    if(lockp == GMutex::ok) lens->mReadMutex.Unlock();
  }
  
  ISdebug(2, _eh + GForm("final removal of lens '%s'[%d].", lens->GetName(), i->first));
  i->second->mLens = 0;
  i->second->mState = LensDetails::LS_Dead;
  i->second->mDeletionTime.SetNow();
  
  // Now truly delete or make a zombie.
  if(lens->mEyeRefCount == 0) {
    delete lens;
    if(mKing->GetLightType() != ZKing::LT_Moon) {
      release_purgatory(0);
    }
  } else {
    ISdebug(0, _eh + GForm("EyeRefCount=%d for %s. Making a zombie.",
			   lens->mEyeRefCount, lens->Identify().c_str()));
    mZombies.push_back(lens);
  }

  if(bStampIdOps) Stamp(FID());
}

 void ZQueen::RemoveLens(ZGlass* lens)
{
  // Initiates removal of lens. This is the method that should be called
  // by user.
  // If called as Flare broadcasting is suppressed.

  static const string _eh("ZQueen::RemoveLens ");

  if(mKing->GetLightType() == ZKing::LT_Moon)
    throw(_eh + "can not be called at a moon.");

  if(lens->mQueen != this)
    throw(_eh + "lens " + lens->Identify() + " is not my subject.");

  if(lens == this)
    throw(_eh + "attempt to delete ZQueen " + Identify() + ".");

  ZMIR* mir = GThread::get_mir();
  if(mir && mir->IsFlare())
    mir->SuppressFlareBroadcast = true;

  remove_lens(lens);
}

 void ZQueen::RemoveLenses(ZList* list, Bool_t recurse)
{
  // Remove elements of list. Does NOT cross queen boundaries.
  // Spawned in a dedicated thread.

  static const string _eh("ZQueen::RemoveLenses ");

  if(mKing->GetLightType() == ZKing::LT_Moon)
    throw(_eh + "can not be called at a moon.");

  ZMIR* mir = assert_MIR_presence(_eh);

  Bool_t syncp = mir->HasResultReq();
  remove_lenses(list, recurse, syncp);
}

/**************************************************************************/
// Lens-list removal ... low-level functions
/**************************************************************************/

 void ZQueen::remove_lens(ZGlass* lens)
{
  // Low-level lens remove initiator. Called on queen's Sun.
  // Only checks if lens is already dying.

  static const string _eh("ZQueen::remove_lens ");

  {
    GMutexHolder _reflck(mSubjectRefCntMutex);

    if(lens->mGlassBits & ZGlassBits::kDying) {
      // This might be(come) completely normal.
      ISdebug(0, _eh + "queen:" + Identify() +" lens:" + lens->Identify() +" already dying.");
      return;
    }

    lens->bAcceptRefs = false;
    lens->mGlassBits |= ZGlassBits::kDying;
  }

  auto_ptr<ZMIR> purg_mir( S_PutLensToPurgatory(lens) );
  mSaturn->ShootMIR(purg_mir);
}

 void ZQueen::remove_lenses(ZList* list, Bool_t recurse, Bool_t syncmode)
{
  // Low-level massive lens remove initiator. Called on queen's Sun.

  lpZGlass_t ll;
  list->Copy(ll);

  for(lpZGlass_i i=ll.begin(); i!=ll.end(); ++i) {
    if((*i)->mQueen == this  &&  (*i) != this) {
      (*i)->bAcceptRefs = false;
      (*i)->mGlassBits |= ZGlassBits::kDying;

      if(recurse) {
	ZList* seclist = dynamic_cast<ZList*>(*i);
	if(seclist && ! seclist->IsEmpty())
	  remove_lenses(seclist, true, false);
      }
    }
  }

  auto_ptr<ZMIR> purg_mir( S_PutListElementsToPurgatory(list) );
  if(syncmode)
    mSaturn->ShootMIRWaitResult(purg_mir);
  else
    mSaturn->ShootMIR(purg_mir);
}

/**************************************************************************/
// ZeroRefCount and management of Orphans
/**************************************************************************/

 void ZQueen::ZeroRefCount(ZGlass* lens)
{
  // Called by ZGlass upon hitting refcount of 0.
  // mSubjectRefCntMutex is (and should be) locked prior to call.

  static const string _eh("ZQueen::ZeroRefCount ");

  if(mKing->GetLightType() == ZKing::LT_Moon) return;
  if(lens->mGlassBits & ZGlassBits::kDying)   return;

  switch(mZeroRCPolicy) {
  case ZRCP_Delete: {
    RemoveLens(lens);
    break;
  }    
  case ZRCP_ToOrphanage: {
    mOrphans->Add(lens);
    break;
  }
  case ZRCP_Ignore:
    break;
  }
}

 void ZQueen::CleanOrphanage()
{
  if(!bRuling) return;
  mOrphans->mListMutex.Lock();
  lpZGlass_t l; mOrphans->Copy(l);
  for(lpZGlass_i i=l.begin(); i!=l.end(); ++i) {
    if((*i)->mRefCount > 1) {
      mOrphans->Remove(*i);
    }
    /*
      else if((*i)->mRefCount == 1) {
      (*i)->mRefCount = -1;
      mOrphans->Remove(*i);
      CheckOut(*i);
      }
    */
  }
  mOrphans->mListMutex.Unlock();
}

/**************************************************************************/
// SetMandatory
/**************************************************************************/
/*
// This must be heavily changed ... must emit eunuchs etc.

 void ZQueen::SetMandatory(Bool_t mandp)
{
static string _eh("ZQueen::SetMandatory");
ZMIR* mir = assert_MIR_presence(_eh);

if(mandp == bMandatory) return;

if(mandp) {
if(bRuling) {
// mSaturn->BroadcastMIR(*mir, mReflectors);
      
} else {

}
bMandatory = true;
} else {
bMandatory = false;
}
Stamp(FID());
}
*/

/**************************************************************************/
// Reflections
/**************************************************************************/

// All Reflections are operated by King of the Queen.
// State variables and list of reflectors are also managed by the King.

 void ZQueen::CreateReflection(TBuffer& buf)
{
  // Virtual. Creates reflection that can be invoked to produce a copy
  // of queen-space on other Saturn.
  // ZQueen itself just creates and streams a ZComet.

  release_purgatory(0);
  ZComet* c = MakeComet();
  c->Streamer(buf);
  delete c;
}

 void ZQueen::InvokeReflection(TBuffer& buf)
{
  // Virtual. Invokes reflection created by a higher Saturn and thus
  // begins mirroring the object-space.
  // For ZQueen this involves unstreaming of ZComet and calling UnfoldFrom().

  static const string _eh("ZQueen::InvokeReflection ");

  ZComet c; c.Streamer(buf);
  if(c.GetType() != ZComet::CT_Queen) throw(_eh + "wrong comet type.");

  c.AssignQueen(this);
  c.SetExtDemangler(this);
  c.bWarnOn = true;

  { // remove and delete comet's Deps, wipe it from comet's queen
    ID_t cdep_id = (ID_t)c.mQueen->GetDeps();
    assert(cdep_id == mDeps->GetSaturnID());
    ZGlass* cdep = c.DemangleID(cdep_id);
    delete cdep;
    c.mIDMap.erase(cdep_id);
    c.mQueen->mDeps = 0;
    // And remove the queen itself from the list
    assert(mSaturnID == c.mQueen->mSaturnID);
    c.mIDMap.erase(mSaturnID);
  }
  { // Rebuild the comet queen itself, assign stuff to *this* and delete it
    c.mQueen->RebuildLinkRefs(&c);
    c.mQueen->RebuildListRefs(&c);
    embrace_of_life(c);
    delete c.mQueen;
  }

  c.RebuildGraph();

  UnfoldFrom(c);
  // Now there is too much stuff in UnfoldFrom ... besides, SunQueen
  // has some link-related junk, too.
}

 void ZQueen::RevokeReflection()
{
  // Virtual. Cleans up queen-space, putting it back into non-ruling state.
}

/**************************************************************************/
// Comet ops
/**************************************************************************/

 ZComet* ZQueen::MakeComet()
{
  // Produces Comet of type ST_Queen.
  // Contains all glasses ruled by the queen.
  // External references are streamed as IDs and can be reconstructed
  // at lower Moons.
  // Used for producing a snapshot of Queen-space to be sent to a Moon.

  ZComet* comet = new ZComet(GetName(), GForm("Comet[Queen] of %s", GetName()));
  comet->mType = ZComet::CT_Queen;
  comet->mQueen = this;

  for(QueenIDMap_i i=mIDMap.begin(); i!=mIDMap.end(); ++i) {
    if(i->second->mState == LensDetails::LS_Alive)
      comet->AddGlass(i->second->mLens);
  }

  return comet;
}

 void ZQueen::AdoptComet(ZList* top_dest, ZList* orphan_dest, ZComet* comet)
{
  // Adopts a Comet of type ST_CometBag.
  // External references of a Comet are ignored.

  if(top_dest == 0)       top_dest = this;
  if(orphan_dest == 0) orphan_dest = mOrphans;

  // Should assert that top_dest & orphan_dest are my subjects.
  // Or at least that their queen depends on me.
  //    But then should do further authorization!

  comet->AssignQueen(this);
  comet->RebuildGraph();
  WriteLock();  
  UInt_t free = mIDSpan - (mIDMap.size() + mPurgatory.size());
  UInt_t need = comet->mIDMap.size();
  if(free >= need) {
    for(mID2pZGlass_i i=comet->mIDMap.begin(); i!=comet->mIDMap.end(); ++i)
      CheckIn(i->second);
    for(lpZGlass_i i=comet->mTopLevels.begin(); i!=comet->mTopLevels.end(); ++i)
      top_dest->Add(*i);
    for(lpZGlass_i i=comet->mOrphans.begin(); i!=comet->mOrphans.end(); ++i)
      orphan_dest->Add(*i);
  } else {
    WriteUnlock();
    throw(string("ZQueen::Adopt(comet=") + comet->GetName() + ") ID shortage");
  }
  WriteUnlock();
}

 void ZQueen::UnfoldFrom(ZComet& comet)
{
  // Unfolds a dormant (not bRuling) queen from a comet of type ST_Queen
  // and makes it a ruling queen.
  // The incoming queen and its Deps are replaced with already present ones.
  // Does NOT call CheckIn ... does it directly with simultaneous building of
  // free block list.
  // At the end calls AdUnfoldment on all lenses.

  assert(comet.mType == ZComet::CT_Queen && comet.mQueen != 0);

  WriteLock();
  { // Reflect all lenses
    mID2pZGlass_i      i = comet.mIDMap.begin();
    QueenIDMap_i ins_pos = mIDMap.begin();
    while(i != comet.mIDMap.end()) {
      i->second->SetStamps(1);
      mSaturn->Reflect(i->second);
      ins_pos = mIDMap.insert
	( ins_pos, pair<ID_t, LensDetails*>
	  (i->first, produce_lens_details(i->first, i->second))
	  );
      ++i;
    }
  }
  { // Create dummy entries for lenses in purgatory.
    for(lID_i i=mPurgatory.begin(); i!=mPurgatory.end(); ++i) {
      LensDetails* ls = produce_lens_details(*i, 0);
      ls->mState = LensDetails::LS_Purged;
      mIDMap.insert(pair<ID_t, LensDetails*>(*i, ls));
    }
  }

  bRuling = true;

  // Call AdUnfoldment for all lenses
  for(mID2pZGlass_i i=comet.mIDMap.begin(); i!=comet.mIDMap.end(); ++i) {
    i->second->AdUnfoldment();
  }

  Stamp();
  StampLink();
  StampListRebuild();

  WriteUnlock();
}

/**************************************************************************/
// Reflectors
/**************************************************************************/

 void ZQueen::add_reflector(SaturnInfo* moon)
{
  lpSaturnInfo_i i = find(mReflectors.begin(), mReflectors.end(), moon);
  if(i == mReflectors.end()) {
    mReflectors.push_back(moon);
  }
}

 void ZQueen::remove_reflector(SaturnInfo* moon)
{
  lpSaturnInfo_i i = find(mReflectors.begin(), mReflectors.end(), moon);
  if(i != mReflectors.end()) {
    mReflectors.erase(i);
  }
}

/**************************************************************************/
// Maintenance of non-ruling queen reflections
/**************************************************************************/

 void ZQueen::BroadcastMIRToNonRulingReflections(ZMIR& mir)
{
  static const string _eh("ZQueen::BroadcastMIRToNonRulingReflections ");

  // This is way sub-optimal ... need iterator based broadcast MIR and
  // set representation of reflectors and moons (instead of list).

  lpSaturnInfo_t foo; mSaturn->CopyMoons(foo);
  for(lpSaturnInfo_i i=mReflectors.begin(); i!=mReflectors.end(); ++i) {
    foo.remove(*i);
  }
  mSaturn->BroadcastMIR(mir, foo);
}

 void ZQueen::BasicQueenChange(ZMIR& mir)
{
  static const string _eh("ZQueen::BasicQueenChange ");

  if(mir.Alpha != this)  throw(_eh + "alpha is not *this* queen.");
  if(mir.HasRecipient()) throw(_eh + "MIR is a beam, should be flare.");
  if( FID_t(mir.Lid, mir.Cid) != ZGlass::FID() )
    throw(_eh + "FID does not correspond to ZGlass.");
  if(mir.Mid != ZGlass::Mid_SetName() && mir.Mid != ZGlass::Mid_SetTitle())
    throw(_eh + "MID does not correspond to SetName or SetTitle.");

  BroadcastMIRToNonRulingReflections(mir);
}

/**************************************************************************/
// Stamping
/**************************************************************************/

 void ZQueen::EmitRay(auto_ptr<Ray>& ray)
{
  mSaturn->Shine(ray);
}

/**************************************************************************/
// tmp
/**************************************************************************/

 void ZQueen::ListAll()
{
  // attempt at looking at all subjects based on ids
  // else might be forced into having local hash.

  for(QueenIDMap_i i=mIDMap.begin(); i!=mIDMap.end(); ++i) {
    ZGlass* l = i->second->mLens;
    if(l != 0) {
      cout <<"ID="<< i->first <<": "<< l->GetName() <<
	"["<< l->VGlassInfo()->fName <<"]n";
    } else {
      cout <<"ID="<< i->first <<": <reserved>n";
    }
  }
}

/**************************************************************************/


ROOT page - Home page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.