#ifndef __XRDOUCCACHE_HH__
#define __XRDOUCCACHE_HH__
/******************************************************************************/
/* */
/* X r d O u c C a c h e . h h */
/* */
/* (c) 2011 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 "XrdOuc/XrdOucCacheStats.hh"
#include "XrdOuc/XrdOucIOVec.hh"
/* The classes defined here can be used to implement a general cache for
data from an arbitrary source (e.g. files, sockets, etc); as follows:
1. Create an instance of XrdOucCacheIO. This object is used to actually
bring in missing data into the cache or write out dirty cache pages.
There can be many instances of this class, as needed. However, make sure
that there is a 1-to-1 unique correspondence between data and its CacheIO
object. Violating this may cause the same data to be cached multiple
times and if the cache is writable the data may be inconsistent!
2. Create an instance of XrdOucCache. You can specify various cache
handling parameters (see the class definition). You can also define
additional instances if you want more than one cache. The specific cache
you create will be defined by an implementation that derives from these
classes. For instance, an implementation of a memory cache is defined
in "XrdOucCacheDram.hh".
3. Use the Attach() method in XrdOucCache to attach your XrdOucCacheIO
object with a cache instance. The method returns a remanufactured
XrdOucCacheIO object that interposes the cache in front of the original
XrdOucCacheIO. This allows you to transparently use the cache.
4. When finished using the remanufactured XrdOucCacheIO object, use its
Detach() method to remove the association from the cache. Other actions
are defined by the actual implementation. For instance XrdOucCacheDram
also releases any assigned cache pages, writes out any dirty pages, and
may optionally delete the object when all references have been removed.
5. You may delete cache instances as well. Just be sure that no associations
still exist using the XrdOucCache::isAttached() method. Otherwise, the
cache destructor will wait until all attached objects are detached.
Example:
class physIO : public XrdOucCacheIO {...}; // Define required methods
class xCache : public XrdOucCache {...}; // The cache implementation
XrdOucCache::Parms myParms; // Set any desired parameters
XrdOucCache *myCache;
XrdOucCacheIO *cacheIO;
xCache theCache; // Implementation instance
myCache = theCache.Create(myParms); // Create a cache instance
cacheIO = myCache->Attach(physIO); // Interpose the cache
// Use cacheIO (fronted by myCache) instead of physIO. When done...
delete cacheIO->Detach(); // Deletes cacheIO and physIO
*/
/******************************************************************************/
/* X r d O u c C a c h e I O C B */
/******************************************************************************/
//-----------------------------------------------------------------------------
//! The XrdOucCacheIOCB defines a callback object that must be used to handle
//! asynchronous I/O operations.
//-----------------------------------------------------------------------------
class XrdOucCacheIOCB
{
public:
//------------------------------------------------------------------------------
//! Handle result from a previous async operation.
//!
//! @param result is result from a previous operation. Successful results are
//! always values >= 0 while errors are negative values and are
//! always '-errno' indicate the reason for the error.
//------------------------------------------------------------------------------
virtual
void Done(int result) = 0;
XrdOucCacheIOCB() {}
virtual ~XrdOucCacheIOCB() {}
};
/******************************************************************************/
/* C l a s s X r d O u c C a c h e I O */
/******************************************************************************/
/* The XrdOucCacheIO object is responsible for interacting with the original
data source/target. It can be used with or without a front-end cache.
Six abstract methods are provided FSize(), Path(), Read(), Sync(), Trunc(),
and Write(). You must provide implementations for each as described below.
Four additional virtual methods are pre-defined: Base(), Detach(), and
Preread() (2x). Normally, there is no need to over-ride these methods.
Finally, each object carries with it a XrdOucCacheStats object.
*/
class XrdOucCacheIO
{
public:
// FSize() returns the current size of the file associated with this object.
// Success: size of the file in bytes.
// Failure: -errno associated with the error.
virtual
long long FSize() = 0;
// Path() returns the path name associated with this object.
//
virtual
const char *Path() = 0;
// Read() places Length bytes in Buffer from a data source at Offset.
// When fronted by a cache, the cache is inspected first.
// Success: actual number of bytes placed in Buffer.
// Failure: -errno associated with the error.
virtual
int Read (char *Buffer, long long Offset, int Length) = 0;
// ReadV() Performs a vector of read requests. When fronted by a cache,
// the cache is inspected first. By batching requests, it provides
// us the ability to skip expensive network round trips.
// If any reads fail or return short, the entire operation should
// fail.
// Success: actual number of bytes read.
// Failure: -errno associated with the read error.
virtual
int ReadV(const XrdOucIOVec *readV, int n)
{int nbytes = 0, curCount = 0;
for (int i=0; iDetach()" if you want to make sure you
// delete the underlying object as well. Alternatively, use the optADB
// option when attaching a CacheIO object to a cache. This will delete
// underlying object and always return 0 to avoid a double delete.
// When not fronted by a cache, Detach() always returns itself. This
// makes its use consistent whether or not a cache is employed.
//
virtual XrdOucCacheIO *Detach() {return this;}
// ioActive() returns true if there is any ongoing IO operation. The function is
// used in XrdPosixXrootd::Close() to check if destruction od PosixFile
// has to be done in a separate task.
virtual bool ioActive() { return false; }
// Preread() places Length bytes into the cache from a data source at Offset.
// When there is no cache or the associated cache does not support or
// allow pre-reads, it's a no-op. Cache placement limits do not apply.
// To maximize parallelism, Peread() should called *after* obtaining
// the wanted bytes using Read(). If the cache implementation supports
// automatic prereads; you can setup parameters on how this should be
// done using the next the next structure and method. The following
// options can be specified:
//
static const int SingleUse = 0x0001; // Mark pages for single use
virtual
void Preread (long long Offset, int Length, int Opts=0)
{
(void)Offset; (void)Length; (void)Opts;
}
// The following structure describes automatic preread parameters. These can be
// set at any time for each XrdOucCacheIO object. It can also be specified when
// creating a cache to establish the defaults (see XrdOucCache::Create()).
// Generally, an implementation that supports prereads should disable small
// prereads when minPages or loBound is set to zero; and should disable large
// prereads when maxiRead or maxPages is set to zero. Refer to the actual
// derived class implementation on how the cache handles prereads.
//
struct aprParms
{int Trigger; // preread if (rdln < Trigger) (0 -> pagesize+1)
int prRecalc; // Recalc pr efficiency every prRecalc bytes (0->50M)
int Reserve4;
short minPages; // If rdln/pgsz < min, preread minPages (0->off)
signed
char minPerf; // Minimum auto preread performance required (0->n/a)
char Reserve1;
aprParms() : Trigger(0), prRecalc(0), Reserve4(0),
minPages(0), minPerf(90), Reserve1(0)
{}
};
virtual
void Preread(aprParms &Parms) { (void)Parms; }
// Here is where the stats about cache and I/O usage reside. There
// is a summary object in the associated cache as well.
//
XrdOucCacheStats Statistics;
virtual ~XrdOucCacheIO() {} // Always use Detach() instead of direct delete!
};
/******************************************************************************/
/* C l a s s X r d O u c C a c h e */
/******************************************************************************/
/* The XrdOucCache class is used to define an instance of a cache. There can
be many such instances. Each instance is associated with one or more
XrdOucCacheIO objects. Use the Attach() method in this class to create
such associations.
*/
class XrdOucCache
{
public:
/* Attach() must be called to obtain a new XrdOucCacheIO object that fronts an
existing XrdOucCacheIO object with this cache.
Upon success a pointer to a new XrdOucCacheIO object is returned
and must be used to read and write data with the cache interposed.
Upon failure, the original XrdOucCacheIO object is returned with
errno set. You can continue using the object without any cache.
The following Attach() options are available and, when specified,
override the default options associated with the cache, except for
optRW, optNEW, and optWIN which are valid only for a r/w cache.
*/
static const int optADB = 0x1000; // Automatically delete underlying CacheIO
static const int optFIS = 0x0001; // File is Structured (e.g. root file)
static const int optFIU = 0x0002; // File is Unstructured (e.g. unix file)
static const int optRW = 0x0004; // File is read/write (o/w read/only)
static const int optNEW = 0x0014; // File is new -> optRW (o/w read to write)
static const int optWIN = 0x0024; // File is new -> optRW use write-in cache
virtual
XrdOucCacheIO *Attach(XrdOucCacheIO *ioP, int Options=0) = 0;
/* isAttached()
Returns the number of CacheIO objects attached to this cache.
Hence, 0 (false) if none and true otherwise.
*/
virtual
int isAttached() {return 0;}
/* You must first create an instance of a cache using the Create() method.
The Parms structure is used to pass parameters about the cache and should
be filled in with values meaningful to the type of cache being created.
The fields below, while oriented toward a memory cache, are sufficiently
generic to apply to almost any kind of cache. Refer to the actual
implementation in the derived class to see how these values are used.
*/
struct Parms
{long long CacheSize; // Size of cache in bytes (default 100MB)
int PageSize; // Size of each page in bytes (default 32KB)
int Max2Cache; // Largest read to cache (default PageSize)
int MaxFiles; // Maximum number of files (default 256 or 8K)
int Options; // Options as defined below (default r/o cache)
short minPages; // Minum number of pages (default 256)
short Reserve1; // Reserved for future use
int Reserve2; // Reserved for future use
Parms() : CacheSize(104857600), PageSize(32768),
Max2Cache(0), MaxFiles(0), Options(0),
minPages(0), Reserve1(0), Reserve2(0) {}
};
// Valid option values in Parms::Options
//
static const int
isServer = 0x0010; // This is server application (as opposed to a user app).
// Appropriate internal optimizations will be used.
static const int
isStructured = 0x0020; // Optimize for structured files (e.g. root).
static const int
canPreRead = 0x0040; // Enable pre-read operations (o/w ignored)
static const int
logStats = 0x0080; // Display statistics upon detach
static const int
Serialized = 0x0004; // Caller ensures MRSW semantics
static const int
ioMTSafe = 0x0008; // CacheIO object is MT-safe
static const int
Debug = 0x0003; // Produce some debug messages (levels 0, 1, 2, or 3)
/* Create() Creates an instance of a cache using the specified parameters.
You must pass the cache parms and optionally any automatic
pre-read parameters that will be used as future defaults.
Upon success, returns a pointer to the cache. Otherwise, a null
pointer is returned with errno set to indicate the problem.
*/
virtual
XrdOucCache *Create(Parms &Params, XrdOucCacheIO::aprParms *aprP=0) = 0;
// Propagate Unlink client request from posix layer to cache.
virtual
int Unlink(const char* /*path*/) { return 0; }
// Propagate Rmdir client request from posix layer to cache.
virtual
int Rmdir(const char* /*path*/) { return 0; }
// Propagate Rename client request from posix layer to cache.
virtual
int Rename(const char* /*path*/, const char* /*newPath*/) { return 0; }
// Propagate Truncate client request from posix layer to cache.
virtual
int Truncate(const char* /*path*/, off_t /*size*/) { return 0; }
/* The following holds statistics for the cache itself. It is updated as
associated cacheIO objects are deleted and their statistics are added.
*/
XrdOucCacheStats Stats;
XrdOucCache() {}
virtual ~XrdOucCache() {}
};
/******************************************************************************/
/* C r e a t i n g C a c h e P l u g - I n s */
/******************************************************************************/
/* You can create a cache plug-in for those parts of the xrootd system that
allow a dynamically selectable cache implementation (e.g. the proxy server
plug-in supports cache plug-ins via the pss.cachelib directive).
Your plug-in must exist in a shared library and have the following extern C
function defined:
extern "C"
{
XrdOucCache *XrdOucGetCache(XrdSysLogger *Logger, // Where messages go
const char *Config, // Config file used
const char *Parms); // Optional parm string
}
When Logger is null, you should use cerr to output messages. Otherwise,
tie an instance XrdSysError to the passed logger.
When Config is null, no configuration file is present. Otherwise, you need
additional configuration information you should get it
from that file in order to support single configuration.
When Parms is null, no parameter string was specified.
The call should return an instance of an XrdOucCache object upon success and
a null pointer otherwise. The instance is used to create actual caches using
the object's Create() method.
*/
#endif