/******************************************************************************/ /* */ /* X r d S s i S e s s R e a l . c c */ /* */ /* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */ /* 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 #include #include #include #include #include #include #include "XrdSsi/XrdSsiAtomics.hh" #include "XrdSsi/XrdSsiRequest.hh" #include "XrdSsi/XrdSsiRRAgent.hh" #include "XrdSsi/XrdSsiRRInfo.hh" #include "XrdSsi/XrdSsiScale.hh" #include "XrdSsi/XrdSsiServReal.hh" #include "XrdSsi/XrdSsiSessReal.hh" #include "XrdSsi/XrdSsiTaskReal.hh" #include "XrdSsi/XrdSsiTrace.hh" #include "XrdSsi/XrdSsiUtils.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysHeaders.hh" #include "Xrd/XrdScheduler.hh" using namespace XrdSsi; /******************************************************************************/ /* L o c a l D e f i n e s */ /******************************************************************************/ #define SINGLETON(dlvar, theitem)\ theitem ->dlvar .next == theitem #define INSERT(dlvar, curitem, newitem) \ newitem ->dlvar .next = curitem; \ newitem ->dlvar .prev = curitem ->dlvar .prev; \ curitem ->dlvar .prev-> dlvar .next = newitem; \ curitem ->dlvar .prev = newitem #define REMOVE(dlbase, dlvar, curitem) \ if (dlbase == curitem) dlbase = (SINGLETON(dlvar,curitem) \ ? 0 : curitem ->dlvar .next);\ curitem ->dlvar .prev-> dlvar .next = curitem ->dlvar .next;\ curitem ->dlvar .next-> dlvar .prev = curitem ->dlvar .prev;\ curitem ->dlvar .next = curitem;\ curitem ->dlvar .prev = curitem /******************************************************************************/ /* L o c a l S t a t i c s */ /******************************************************************************/ namespace { std::string dsProperty("DataServer"); const char *tident = 0; } /******************************************************************************/ /* G l o b a l s */ /******************************************************************************/ namespace XrdSsi { extern XrdScheduler *schedP; extern XrdSysError Log; extern XrdSsiScale sidScale; } /******************************************************************************/ /* L o c a l C l a s s e s */ /******************************************************************************/ namespace { class CleanUp : public XrdJob { public: void DoIt() {sessP->Lock(); sessP->Unprovision(); delete this; } CleanUp(XrdSsiSessReal *sP) : sessP(sP) {} ~CleanUp() {} private: XrdSsiSessReal *sessP; }; } /******************************************************************************/ /* D e s t r u c t o r */ /******************************************************************************/ XrdSsiSessReal::~XrdSsiSessReal() { XrdSsiTaskReal *tP; if (resKey) free(resKey); if (sessName) free(sessName); if (sessNode) free(sessNode); while((tP = freeTask)) {freeTask = tP->attList.next; delete tP;} } /******************************************************************************/ /* I n i t S e s s i o n */ /******************************************************************************/ void XrdSsiSessReal::InitSession(XrdSsiServReal *servP, const char *sName, int uent, bool hold) { requestP = 0; uEnt = uent; attBase = 0; freeTask = 0; myService = servP; nextTID = 0; alocLeft = XrdSsiRRInfo::idMax; isHeld = hold; inOpen = false; noReuse = false; if (resKey) {free(resKey); resKey = 0;} if (sessName) free(sessName); sessName = (sName ? strdup(sName) : 0); if (sessNode) free(sessNode); sessNode = 0; } /******************************************************************************/ /* Private: N e w T a s k */ /******************************************************************************/ // Must be called with sessMutex locked! XrdSsiTaskReal *XrdSsiSessReal::NewTask(XrdSsiRequest *reqP) { EPNAME("NewTask"); XrdSsiTaskReal *ptP, *tP; // Allocate a task object for this request // if ((tP = freeTask)) freeTask = tP->attList.next; else {if (!alocLeft || !(tP = new XrdSsiTaskReal(this))) {XrdSsiUtils::RetErr(*reqP, "Too many active requests.", EMLINK); return 0; } alocLeft--; } // We always set a new task ID to avoid ID collisions. his is good for over // 194 days if we have 1 request/second. In practice. this will work for a // couple of years before wrapping. By then the ID's should be free. // tP->SetTaskID(nextTID++); nextTID &= XrdSsiRRInfo::idMax; // Initialize the task and return its pointer // tP->Init(reqP, reqP->GetTimeOut()); DEBUG("Task=" <GetTimeOut()); // If there was an error, scuttle the request. Note that errors will be returned // on a separate thread to avoid hangs here. // if (!epStatus.IsOK()) {std::string eTxt; int eNum = XrdSsiUtils::GetErr(epStatus, eTxt); XrdSsiUtils::RetErr(*reqP, eTxt.c_str(), eNum); XrdSsi::sidScale.retEnt(uEnt); return false; } // Queue a new task and indicate our state // NewTask(reqP); inOpen = true; return true; } /******************************************************************************/ /* Private: R e l T a s k */ /******************************************************************************/ void XrdSsiSessReal::RelTask(XrdSsiTaskReal *tP) // sessMutex locked! { EPNAME("RelTask"); // Do some debugging here // DEBUG((isHeld ? "Recycling" : "Deleting")<<" task="<Recycle(this, false); } else { if (sessName) {free(sessName); sessName = 0;} if (sessNode) {free(sessNode); sessNode = 0;} sessMutex.UnLock(); myService->Recycle(this, !noReuse); } } /******************************************************************************/ /* T a s k F i n i s h e d */ /******************************************************************************/ void XrdSsiSessReal::TaskFinished(XrdSsiTaskReal *tP) { // Lock our mutex // sessMutex.Lock(); // Remove task from the task list if it's in it // if (tP == attBase || tP->attList.next != tP) {REMOVE(attBase, attList, tP);} // Clear any pending task events and decrease active count // tP->ClrEvent(); // Return the request entry number // XrdSsi::sidScale.retEnt(uEnt); // Place the task on the free list. If we can shutdown, then unprovision which // will drive a shutdown. The returns without the sessMutex, otherwise we must // unlock it before we return. // RelTask(tP); if (!isHeld && !attBase) Unprovision(); else sessMutex.UnLock(); } /******************************************************************************/ /* U n H o l d */ /******************************************************************************/ void XrdSsiSessReal::UnHold(bool cleanup) { XrdSsiMutexMon sessMon(sessMutex); // Immediately stopo reuse of this object // if (isHeld && resKey && myService) myService->StopReuse(resKey); // Turn off the hold flag and if we have no attached tasks, schedule shutdown // isHeld = false; if (cleanup && !attBase) XrdSsi::schedP->Schedule(new CleanUp(this)); } /******************************************************************************/ /* Private: U n p r o v i s i o n */ /******************************************************************************/ // Called with sessMutex locked and returns with it unlocked void XrdSsiSessReal::Unprovision() // Called with sessMutex locked! { EPNAME("Unprovision"); XrdCl::XRootDStatus uStat; // Clear any pending events // DEBUG("Closing " <IsOK(); // If we have no requests then we may want to simply shoutdown. // Note that shutdown and unprovision unlock the sessMutex. // if (!tP) {if (isHeld) {sessMutex.UnLock(); return false; } if (!status->IsOK()) Shutdown(*status, false); else {if (!isHeld) Unprovision(); else sessMutex.UnLock(); } return false; } // We are here because the open finally completed. If the open failed, then // schedule an error for all pending tasks. The Finish() call on each will // drive the cleanup of this session. // if (!status->IsOK()) {XrdSsiErrInfo eInfo; XrdSsiUtils::SetErr(*status, eInfo); do {tP->SchedError(&eInfo); tP = tP->attList.next;} while(tP != attBase); sessMutex.UnLock(); return false; } // Obtain the endpoint name // std::string currNode; if (epFile.GetProperty(dsProperty, currNode)) {if (sessNode) free(sessNode); sessNode = strdup(currNode.c_str()); } else sessNode = strdup("Unknown!"); // Execute each pending request. Make sure not to reference the task object // chain pointer after invoking SendRequest() as it may become invalid. // ztP = attBase; do {ntP = tP->attList.next; if (!tP->SendRequest(sessNode)) noReuse = true; tP = ntP; } while(tP != ztP); // We are done, field the next event // sessMutex.UnLock(); return true; }