/******************************************************************************/
/* */
/* 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;
}