40 #include <sys/types.h>
86 #define SHMINFO(x) ((ShmInfo *)shmBase)->x
88 #define SHMADDR(type, offs) (type *)(shmBase + offs)
90 #define SHMOFFS(addr) (char *)addr - shmBase
92 #define ITEM_KEY(x) (char *)x + sizeof(MemItem) + keyPos
94 #define ITEM_VAL(x) (char *)x + sizeof(MemItem)
96 #define ITEM_VOF(x) (char *)x + sizeof(MemItem) - shmBase
98 int PageMask = ~(sysconf(_SC_PAGESIZE)-1);
99 int PageSize = sysconf(_SC_PAGESIZE);
113 EnumJar(
int xfd,
int bsz)
114 : buff(new char[bsz]), fd(xfd), iNum(0) {}
115 ~EnumJar() {
if (fd >= 0)
close(fd);
116 if (buff)
delete [] buff;
125 FileHelper(
XrdSsiShMam *mp) : autoClose(false), shMamP(mp) {}
126 ~FileHelper() {
if (autoClose)
127 {
int rc = errno; shMamP->Detach(); errno = rc;}
136 pthread_rwlock_t *mtxP;
141 {
if (isrw) pthread_rwlock_wrlock(mtx);
142 else pthread_rwlock_rdlock(mtx);
146 ~MutexHelper() {
if (mtxP) pthread_rwlock_unlock(mtxP);}
156 #if ( defined(__linux__) || defined(__GNU__) ) && defined(O_CLOEXEC) && defined(F_DUPFD_CLOEXEC)
157 inline int ShMam_Dup(
int oldfd)
158 {
return fcntl(oldfd, F_DUPFD_CLOEXEC, 0);}
160 inline int ShMam_Open(
const char *path,
int flags)
161 {
return open(path, flags|O_CLOEXEC);}
163 inline int ShMam_Open(
const char *path,
int flags, mode_t mode)
164 {
return open(path, flags|O_CLOEXEC, mode);}
166 inline int ShMam_Dup(
int oldfd)
167 {
int newfd = dup(oldfd);
168 if (newfd >= 0)
fcntl(newfd, F_SETFD, FD_CLOEXEC);
172 inline int ShMam_Open(
const char *path,
int flags)
173 {
int newfd =
open(path, flags);
174 if (newfd >= 0)
fcntl(newfd, F_SETFD, FD_CLOEXEC);
178 inline int ShMam_Open(
const char *path,
int flags, mode_t mode)
179 {
int newfd =
open(path, flags, mode);
180 if (newfd >= 0)
fcntl(newfd, F_SETFD, FD_CLOEXEC);
185 inline bool ShMam_Flush(
int fd)
187 #if _POSIX_SYNCHRONIZED_IO > 0
190 return fsync(fd) == 0;
243 pthread_mutex_init(&lkMutex, NULL);
244 pthread_rwlock_init(&myMutex, NULL);
252 int hash,
bool replace)
254 XLockHelper lockInfo(
this,
RWLock);
255 MemItem *theItem, *prvItem, *newItem;
256 int hEnt, kLen, iOff, retEno = 0;
260 if (!shmSize) {errno = ENOTCONN;
return false;}
261 if (!isRW) {errno = EROFS;
return false;}
266 if (kLen > maxKLen) {errno = ENAMETOOLONG;
return false;}
275 if (lockRW && !lockInfo.FLock())
return false;
279 hEnt = Find(theItem, prvItem, key, hash);
287 if (!replace) {errno = EEXIST;
return false;}
299 if (!(newItem = NewItem())) {errno = ENOSPC;
return false;}
303 newItem->hash = hash;
319 if (hEnt)
Atomic_SET(newItem->next, theItem->next);
320 else {hEnt = (
unsigned int)hash % shmSlots;
321 if (hEnt == 0) hEnt = 1;
326 if (prvItem) Atomic_SET_STRICT(prvItem->next, iOff);
328 Atomic_SET_STRICT(shmIndex[hEnt],iOff);
329 if (syncOn) Updated(
SHMOFFS(&shmIndex[hEnt]));
337 if (prvItem) Updated(
SHMOFFS(prvItem));
352 FileHelper fileHelp(
this);
354 struct stat Stat1, Stat2;
356 union {
int *intP;
Atomic(
int) *antP;} xntP;
362 mMode = PROT_READ|PROT_WRITE;
373 if (tout < 0) tout = 0x7fffffff;
374 while((shmFD = ShMam_Open(
shmPath, oMode)) < 0 && tout >= 0)
375 {
if (errno != ENOENT)
return false;
383 if (tout <= 0) {errno = ETIMEDOUT;
return false;}
384 fileHelp.autoClose =
true;
388 if (!lockInfo.FLock())
return false;
392 if (
fstat(shmFD, &Stat1))
return false;
396 shmBase = (
char *)mmap(0, Stat1.st_size, mMode, MAP_SHARED, shmFD, 0);
397 if (shmBase == MAP_FAILED)
return false;
398 shmSize = Stat1.st_size;
408 {errno = EDOM;
return false;}
425 || Stat1.st_dev != Stat2.st_dev || Stat1.st_ino != Stat2.st_ino)
426 {errno = EAGAIN;
return false;}
427 accMode = Stat2.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
432 fileHelp.autoClose =
false;
442 static const int minInfoSz = 256;
443 static const int okMode = S_IRWXU|S_IRWXG|S_IROTH;
444 static const int crMode = S_IRWXU|S_IRWXG|S_IROTH;
445 FileHelper fileHelp(
this);
447 int n, maxEnts, totSz, indexSz;
448 union {
int *intP; Atomic(
int) *antP;} xntP;
453 {errno = EINVAL;
return false;}
454 if (parms.
mode & ~okMode || ((parms.
mode & crMode) != crMode))
455 {errno = EACCES;
return false;}
459 reUse = (parms.
reUse <= 0 ? false :
true);
460 multW = (parms.
multW <= 0 ? false :
true);
464 memset(&theInfo, 0,
sizeof(theInfo));
468 shmInfoSz = (
sizeof(ShmInfo)+minInfoSz-1)/minInfoSz*minInfoSz;
469 theInfo.lowFree = theInfo.infoSz = shmInfoSz;
474 theInfo.itemSz = shmItemSz;
479 totSz = shmItemSz * maxEnts;
480 totSz = (totSz+7)/8*8;
484 indexSz = parms.
indexSz*
sizeof(int);
485 indexSz = (indexSz+7)/8*8;
489 totSz = totSz + indexSz + shmInfoSz;
498 theInfo.index = totSz-indexSz;
501 theInfo.highUse = theInfo.index;
502 theInfo.reUse = reUse;
503 theInfo.multW = multW;
504 theInfo.keyPos = keyPos =
shmTypeSz +
sizeof(MemItem);
505 theInfo.maxKeys = maxEnts;
506 theInfo.maxKeySz = maxKLen = parms.
maxKLen;
508 strncpy(theInfo.typeID,
shmType,
sizeof(theInfo.typeID)-1);
509 strncpy(theInfo.myName,
shmImpl,
sizeof(theInfo.myName)-1);
514 shmTemp = (
char *)malloc(n+8);
515 sprintf(shmTemp,
"%s.new",
shmPath);
519 if ((shmFD = ShMam_Open(shmTemp, O_RDWR|O_CREAT, parms.
mode)) < 0)
521 accMode = parms.
mode;
522 fileHelp.autoClose =
true;
526 if (!Lock(
true,
true)) {errno = EADDRINUSE;
return false;}
534 shmBase = (
char *)mmap(0, totSz, PROT_READ|PROT_WRITE, MAP_SHARED, shmFD, 0);
535 if (shmBase == MAP_FAILED)
return false;
541 memcpy(shmBase, &theInfo,
sizeof(theInfo));
549 lockRO = lockRW =
false;
550 fileHelp.autoClose =
false;
560 XLockHelper lockInfo(
this,
RWLock);
561 MemItem *theItem, *prvItem;
566 if (!shmSize) {errno = ENOTCONN;
return false;}
567 if (!isRW) {errno = EROFS;
return false;}
576 if (lockRW && !lockInfo.FLock())
return false;
580 if (!(hEnt = Find(theItem, prvItem, key, hash)))
581 {
if (data) {errno = ENOENT;
return false;}
592 iOff = theItem->next;
594 if (prvItem) Atomic_SET_STRICT(prvItem->next, iOff);
595 else {
if (!iOff)
SHMINFO(slotsUsed)--;
596 Atomic_SET_STRICT(shmIndex[hEnt],iOff);
605 if (prvItem) Updated(
SHMOFFS(prvItem));
606 else Updated(
SHMOFFS(&shmIndex[hEnt]));
622 if (shmFD >= 0) {
close(shmFD); shmFD = -1;}
623 if (shmSize) {munmap(shmBase, shmSize); shmSize = 0;}
624 if (shmTemp) {free(shmTemp); shmTemp = 0;}
634 EnumJar *theJar = (EnumJar *)jar;
638 if (theJar) {
delete theJar; jar = 0;}
646 XLockHelper lockInfo(
this,
ROLock);
647 EnumJar *theJar = (EnumJar *)jar;
650 int rc, newFD, fence, iOff, hash = 0;
654 if (!shmSize) {errno = ENOTCONN;
return false;}
662 if ((newFD = ShMam_Dup(shmFD)) < 0)
return false;
663 theJar =
new EnumJar(newFD, shmItemSz);
665 }
else if (theJar->iNum < 0)
673 if (lockRO && !lockInfo.FLock())
674 {rc = errno;
Enumerate(jar); errno = rc;
return false;}
679 iTest = (
static_cast<long long>(theJar->iNum) * shmItemSz) + shmInfoSz;
681 if (iTest < fence) iOff =
static_cast<int>(iTest);
686 theItem = (MemItem *)(theJar->buff);
688 {rc =
pread(theJar->fd, theJar->buff, shmItemSz, iOff);
689 if (rc < 0)
return false;
690 if (rc != shmItemSz)
break;
691 if ((hash = theItem->hash))
break;
697 if (!hash) {
Enumerate(jar); errno = ENOENT;
return false;}
706 theJar->iNum = (iOff - shmInfoSz)/shmItemSz + 1;
716 MutexHelper mtHelp(&myMutex,
RWLock);
720 if (!shmSize) {errno = ENOTCONN;
return false;}
721 if (!shmTemp) {errno = ENOPROTOOPT;
return false;}
722 if (!isRW) {errno = EROFS;
return false;}
727 return ExportIt(
false);
734 bool XrdSsiShMam::ExportIt(
bool fLocked)
747 if ((oldFD = ShMam_Open(
shmPath, O_RDWR)) < 0)
748 {
if (errno != ENOENT)
return false;}
750 {
do {rc = flock(oldFD, LOCK_EX);}
while(rc < 0 && errno == EINTR);
751 if (rc)
return false;
757 free(shmTemp); shmTemp = 0;
764 {
int vnum;
bool noGo =
false;
765 if (
pread(oldFD, &vnum,
sizeof(vnum), 0) == (ssize_t)
sizeof(vnum))
767 if (
pwrite(oldFD, &vnum,
sizeof(vnum), 0) != (ssize_t)
sizeof(vnum))
770 if (noGo) std::cerr <<
"SsiShMam: Unable to update version for " <<
shmPath
789 int XrdSsiShMam::Find(XrdSsiShMam::MemItem *&theItem,
790 XrdSsiShMam::MemItem *&prvItem,
791 const char *key,
int &hash)
797 if (!hash) hash = HashVal(key);
801 hEnt = (
unsigned int)hash % shmSlots;
802 if (hEnt == 0) hEnt = 1;
803 iOff = Atomic_GET_STRICT(shmIndex[hEnt]);
809 {theItem =
SHMADDR(MemItem, iOff);
810 if (hash == theItem->hash && !strcmp(key,
ITEM_KEY(theItem)))
813 iOff = Atomic_GET_STRICT(theItem->next);
825 bool XrdSsiShMam::Flush()
831 #if _POSIX_SYNCHRONIZED_IO > 0
834 rc =
fsync(shmFD) == 0;
841 std::cerr <<
"ShMam: msync() failed; " <<
XrdSysE2T(errno) <<std::endl;
856 XLockHelper lockInfo(
this,
ROLock);
857 MemItem *theItem, *prvItem;
862 if (!shmSize) {errno = ENOTCONN;
return false;}
871 if (lockRO && !lockInfo.FLock())
return false;
875 if (!(hEnt = Find(theItem, prvItem, key, hash)))
876 {errno = ENOENT;
return false;}
891 int XrdSsiShMam::HashVal(
const char *key)
894 int hval, klen = strlen(key);
898 crc = crc32(0L, Z_NULL, 0);
902 crc = crc32(crc, (
const Bytef *)key, klen);
907 hval =
static_cast<int>(crc);
908 return (hval ? hval : 1);
917 bool XrdSsiShMam::Lock(
bool xrw,
bool nowait)
919 int rc, act = (xrw ? LOCK_EX : LOCK_SH);
923 if (shmFD < 0) {errno = EBADF;
return false;}
931 if (xrw) lkCount = 1;
932 else {pthread_mutex_lock(&lkMutex);
933 if (lkCount++) {pthread_mutex_unlock(&lkMutex);
return true;}
938 if (nowait) act |= LOCK_NB;
942 do {rc = flock(shmFD, act);}
while(rc < 0 && errno == EINTR);
947 if (rc) {
if (xrw) lkCount = 0;
953 if (!xrw) pthread_mutex_unlock(&lkMutex);
963 MutexHelper mtHelp(&myMutex,
ROLock);
967 if (!shmSize) {errno = ENOTCONN;
return 0;}
969 if (!strcmp(vname,
"atomics"))
975 if (!strcmp(vname,
"hash"))
976 {
if (!buff || blen < (
int)(
sizeof(
int)+1)) {errno = EMSGSIZE;
return -1;}
977 memcpy(buff, &
SHMINFO(hashID),
sizeof(
int)); buff[
sizeof(int)] = 0;
980 if (!strcmp(vname,
"impl"))
981 {
int n = strlen(
SHMINFO(myName));
982 if (!buff || blen < n) {errno = EMSGSIZE;
return -1;}
986 if (!strcmp(vname,
"flockro"))
return lockRO;
987 if (!strcmp(vname,
"flockrw"))
return lockRW;
988 if (!strcmp(vname,
"indexsz"))
return shmSlots;
989 if (!strcmp(vname,
"indexused"))
return SHMINFO(slotsUsed);
990 if (!strcmp(vname,
"keys"))
return SHMINFO(itemCount);
991 if (!strcmp(vname,
"keysfree"))
994 if (!strcmp(vname,
"maxkeylen"))
return SHMINFO(maxKeySz);
995 if (!strcmp(vname,
"multw"))
return multW;
996 if (!strcmp(vname,
"reuse"))
return reUse;
997 if (!strcmp(vname,
"type"))
998 {
int n = strlen(
SHMINFO(typeID));
999 if (!buff || blen < n) {errno = EMSGSIZE;
return -1;}
1000 strcpy(buff,
SHMINFO(typeID));
1003 if (!strcmp(vname,
"typesz"))
return SHMINFO(typeSz);
1015 XrdSsiShMam::MemItem *XrdSsiShMam::NewItem()
1022 if (reUse &&
SHMINFO(freeItem))
1024 itemP =
SHMADDR(MemItem, iOff);
1025 SHMINFO(freeItem) = itemP->next;
1028 int newFree =
SHMINFO(lowFree) + shmItemSz;
1029 if (newFree >
SHMINFO(highUse)) itemP = 0;
1030 else {iOff =
SHMINFO(lowFree);
1031 itemP =
SHMADDR(MemItem, iOff);
1045 bool XrdSsiShMam::ReMap(LockType iHave)
1054 {pthread_rwlock_unlock(&myMutex);
1055 pthread_rwlock_wrlock(&myMutex);
1062 if (verNum ==
SHMINFO(verNum))
return false;
1075 if (!newMap.Attach(timeOut, isRW))
return false;
1089 XLockHelper lockInfo(
this,
RWLock);
1094 int fence, iOff, hash;
1098 if (!shmSize) {errno = ENOTCONN;
return false;}
1099 if (!isRW) {errno = EROFS;
return false;}
1104 {errno = EINVAL;
return false;}
1108 if (shmTemp) {errno = EPERM;
return false;}
1116 if (!lockInfo.FLock())
return false;
1140 parms.
mode = accMode;
1141 if (!newMap.
Create(parms))
return false;
1151 {theItem =
SHMADDR(MemItem, iOff);
1152 if ((hash = theItem->hash))
1155 if (!newMap.
AddItem(val, 0, key, hash,
true))
return false;
1166 if (!newMap.ExportIt(
true))
return false;
1178 void XrdSsiShMam::RetItem(MemItem *iP)
1190 {iP->next =
SHMINFO(freeItem);
1200 void XrdSsiShMam::SetLocking(
bool isrw)
1206 #ifdef NEED_ATOMIC_MUTEX
1207 lockRO = lockRW =
true;
1211 lockRO = reUse =
SHMINFO(reUse);
1217 lockRW = reUse || multW;
1225 void XrdSsiShMam::Snooze(
int sec)
1227 struct timespec naptime, waketime;
1231 naptime.tv_sec = sec;
1232 naptime.tv_nsec = 0;
1236 while(nanosleep(&naptime, &waketime) && EINTR == errno)
1237 {naptime.tv_sec = waketime.tv_sec;
1238 naptime.tv_nsec = waketime.tv_nsec;
1255 shmFD = newMap.shmFD;
1257 shmSize = newMap.shmSize;
1259 shmBase = newMap.shmBase;
1261 shmIndex = newMap.shmIndex;
1262 newMap.shmIndex = 0;
1263 lockRO = newMap.lockRO;
1264 lockRW = newMap.lockRW;
1265 reUse = newMap.reUse;
1266 multW = newMap.multW;
1267 verNum = newMap.verNum;
1276 MutexHelper mtHelp(&myMutex,
RWLock);
1280 if (!shmSize) {errno = ENOTCONN;
return false;}
1281 if (!isRW) {errno = EROFS;
return false;}
1286 if (!Flush())
return false;
1300 MutexHelper mtHelp(&myMutex,
RWLock);
1304 if (!shmSize) {errno = ENOTCONN;
return false;}
1305 if (!isRW) {errno = EROFS;
return false;}
1306 if (syncqsz < 0) {errno = EINVAL;
return false;}
1310 if (syncOn && !Flush())
return false;
1322 MutexHelper mtHelp(&myMutex,
RWLock);
1326 if (!shmSize) {errno = ENOTCONN;
return false;}
1327 if (!isRW) {errno = EROFS;
return false;}
1331 if (syncOn && !Flush())
return false;
1336 syncOpt = (syncdo ? MS_SYNC : MS_ASYNC);
1346 void XrdSsiShMam::UnLock(
bool isrw)
1352 if (shmFD < 0)
return;
1359 if (isrw) lkCount = 0;
1360 else {pthread_mutex_lock(&lkMutex);
1362 if (lkCount) {pthread_mutex_unlock(&lkMutex);
return;}
1367 do {rc = flock(shmFD, LOCK_UN);}
while(rc < 0 && errno == EINTR);
1372 if (!isrw) pthread_mutex_unlock(&lkMutex);
1379 void XrdSsiShMam::Updated(
int mOff)
1384 {
if (!syncBase) {syncBase =
true; syncQWR++;}
1387 {syncLast = (mOff &
PageMask); syncQWR++;}
1392 if (syncQWR >= syncQSZ) {ShMam_Flush(shmFD); syncQWR = 0;}
1397 void XrdSsiShMam::Updated(
int mOff,
int mLen)
1400 int memE = mOff + mLen;
1405 if (memB != syncLast)
1407 if (memB != (memE &
PageMask)) syncQWR++;
1413 if (syncQWR >= syncQSZ) {ShMam_Flush(shmFD); syncQWR = 0;}
ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)
int stat(const char *path, struct stat *buf)
int ftruncate(int fildes, off_t offset)
int open(const char *path, int oflag,...)
ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset)
int fstat(int fildes, struct stat *buf)
int fcntl(int fd, int cmd,...)
int rename(const char *oldpath, const char *newpath)
int fdatasync(int fildes)
#define SHMADDR(type, offs)
const char * XrdSysE2T(int errcode)
XrdSsiShMam(XrdSsiShMat::NewParms &parms)
bool Resize(XrdSsiShMat::CRZParms &parms)
bool Create(XrdSsiShMat::CRZParms &parms)
void Detach()
Detach the map from the shared memory.
bool AddItem(void *newdata, void *olddata, const char *key, int hash, bool replace=false)
bool DelItem(void *data, const char *key, int hash)
int Info(const char *vname, char *buff=0, int blen=0)
bool GetItem(void *data, const char *key, int hash)
bool Attach(int tout, bool isrw=false)
bool Enumerate(void *&jar, char *&key, void *&val)
const char * typeID
The name of the type associated with the key.
const char * impl
Implementation name.
const char * path
The path to the backing file for the table.
int hashID
The hash being used (0 means the default)
int typeSz
Size of the type in bytes.
static const int PageSize
static const int PageMask
int maxKeys
Maximum number of keys-value pairs expected in table.
int maxKLen
The maximum acceptable key length.
int mode
Filemode for the newly created file.
int indexSz
Number of four byte hash table entries to create.