/******************************************************************************/ /* */ /* X r d C m s M a n T r e e . c c */ /* */ /* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University */ /* All Rights Reserved */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ #include #include "XProtocol/YProtocol.hh" #include "XrdCms/XrdCmsManTree.hh" #include "XrdCms/XrdCmsNode.hh" #include "XrdCms/XrdCmsTrace.hh" using namespace XrdCms; /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ XrdCmsManTree::XrdCmsManTree(int maxC) : maxTMI(0), numConn(0), maxConn(maxC), atRoot(0), conLevel(0), conNID(-1), numWaiting(0), myStatus(Active) { snprintf(buff, sizeof(buff), "%d", maxC); } /******************************************************************************/ /* A b o r t */ /******************************************************************************/ void XrdCmsManTree::Abort() { XrdSysMutexHelper Monitor(myMutex); // An abort may be issued to make sure no one is waiting to participate in // tree construction. It's usually issued when the manager object has been // permanently redirected. // if (myStatus != Aborted && numWaiting) {for (int i = 0; i < maxTMI; i++) if (tmInfo[i].Status == Waiting) {tmInfo[i].Level = 0; Redrive(i);} } // Indicate we have aborted // myStatus = Aborted; } /******************************************************************************/ /* C o n n e c t */ /******************************************************************************/ int XrdCmsManTree::Connect(int nID, XrdCmsNode *nP) { static CmsDiscRequest discRequest = {{0, kYR_disc, 0, 0}}; XrdSysMutexHelper Monitor(myMutex); char mybuff[16]; int i; // Rule 0: If we aborted tell the client to just stop doing this // if (myStatus == Aborted) return 0; // Rule 1: If we are already connected, thell the caller to disband the // connection as we must have a connection to an interior node. // if (myStatus == Connected) return 0; numConn++; tmInfo[nID].nodeP = nP; // Rule 2: If we connected to a root node then consider ourselves connected // only if all connections are to the root. // if (tmInfo[nID].Level == 0) {if (numConn == maxConn) {myStatus = Connected; conLevel = 0; atRoot = 1; Say.Emsg("ManTree", "Now connected to", buff, "root node(s)"); } tmInfo[nID].Status = Connected; return 1; } // Rule 3: We connected to an interior node. Disband all other existing // connections (these should only be to root nodes) and consider // ourselves connected. // for (i = 0; i < maxTMI; i++) if (i != nID && tmInfo[i].Status == Connected) {tmInfo[i].nodeP->Send((char *)&discRequest, sizeof(discRequest)); tmInfo[i].Status = Pending; } myStatus = Connected; conLevel = tmInfo[nID].Level; conNID = nID; atRoot = 0; // Document our connection configuration // snprintf(mybuff, sizeof(mybuff), "%d", conLevel); Say.Emsg("ManTree", "Now connected to supervisor at level", mybuff); return 1; } /******************************************************************************/ /* D i s c */ /******************************************************************************/ void XrdCmsManTree::Disc(int nID) { // A connected caller has lost it's connection. // myMutex.Lock(); if (tmInfo[nID].Status == Connected || tmInfo[nID].Status == Pending) numConn--; tmInfo[nID].Status = Active; if (atRoot || (conLevel && conNID == nID)) myStatus = Active; tmInfo[nID].nodeP = 0; myMutex.UnLock(); } /******************************************************************************/ /* R e g i s t e r */ /******************************************************************************/ int XrdCmsManTree::Register() { int nID; // Add this server to the tree table. Register is called only once and there // can be no more than MTMax connections to a manager. Hence, we dispense with // error checking (how optimistic :-) // myMutex.Lock(); tmInfo[maxTMI].Status= Active; nID = maxTMI; maxTMI++; myMutex.UnLock(); return nID; } /******************************************************************************/ /* T r y i n g */ /******************************************************************************/ // This method arranges server connections to a manager to form a minimal B-Tree // free of phantom arcs. The rule is simple, either all connections are to the // root of tree or there is only one connection to an interior node. Because // node discovery is non-determinstic, we must make sure that all root nodes // are tried so as to discover the full set of supervisor nodes we can contact. // This method returns True if the caller may continue at the indicated level // and False if the caller should restart at the root node. // int XrdCmsManTree::Trying(int nID, int lvl) { int i; // Set the current status of the connection // myMutex.Lock(); tmInfo[nID].Level = lvl; // Rule 0: If we aborted tell the client to just stop doing this // if (myStatus == Aborted) return -1; // Rule 1: If we are already connected at level >0 then the caller must wait // if (myStatus == Connected && conLevel > 0) {Pause(nID); return (lvl == tmInfo[nID].Level); } // Rule 2: If the caller is trying level 0 then any waiting threads must be // allowed to continue but forced to level 0. This allows us to discover // all the supervisors connected to the root. // if (!lvl) {if (numWaiting) {for (i = 0; i < maxTMI; i++) if (i != nID && tmInfo[i].Status == Waiting) {tmInfo[i].Level = 0; Redrive(i);} } myMutex.UnLock(); return 1; } // Rule 3: If the caller is trying at a non-zero level (interior node) and // someone else is trying at a non-zero level, then the caller must // wait. // for (i = 0; i < maxTMI; i++) if (i != nID && tmInfo[i].Status == Active && tmInfo[i].Level) break; if (i < maxTMI) Pause(nID); else myMutex.UnLock(); // The caller may continue. Indicate whether the caller must restart at the // root node. If the caller may continue trying to connect to an interior // node then it's the only thread trying to do so. // return (lvl == tmInfo[nID].Level); }