archie/prospero/lib/ardp/flocks.c
2024-05-27 16:13:40 +02:00

222 lines
5.8 KiB
C

/* Copyright (c) 1993, by Pandora Systems */
/* Author: Mitra <mitra@path.net> */
/* Allocation code copied and adapted from:
lib/pfs/flalloc.c in the Prospero Alpha.5.2a release. */
#include <pfs.h>
#include <plog.h>
#include "flocks.h"
#include "mitra_macros.h"
#ifdef PFS_THREADS
extern p_th_mutex p_th_mutexFILES;
#endif
/* FileLocks are currently unused by dirsrv.c, and not linked into the ardp
library. So dirsrv will not report on them in replies to a STATUS message.
*/
static FILELOCK lfree = NULL; /* Free filelocks */
/* These are global variables which will be read by dirsrv.c
Too bad C doesn't have better methods for structuring such global data. */
int filelock_count = 0;
int filelock_max = 0;
int filelock_open = 0;
int filelock_open_max = 0;
int filelock_sepwaits = 0;
int filelock_secwaits = 0;
FILELOCK filelock_locked = NULL;
/************* Standard routines to alloc, free and copy *************/
/*
* filelock_alloc - allocate and initialize FILELOCK structure
*
* returns a pointer to an initialized structure of type
* FILELOCK. If it is unable to allocate such a structure, it
* signals out_of_memory();
*/
FILELOCK
filelock_alloc()
{
FILELOCK fl;
TH_STRUC_ALLOC(filelock,FILELOCK,fl);
fl->name = NULL;
fl->readers = 0;
return(fl);
}
/*
* filelock_free - free a FILELOCK structure
*
* filelock_free takes a pointer to a FILELOCK structure and adds it to
* the free list for later reuse.
*/
void
filelock_free(FILELOCK fl)
{
stfree(fl->name); fl->name = NULL;
fl->readers = 0;
TH_STRUC_FREE(filelock, FILELOCK, fl);
}
/*
* filelock_lfree - free a linked list of FILELOCK structures.
*
* filelock_lfree takes a pointer to a filelock structure frees it and
* any linked
* FILELOCK structures. It is used to free an entire list of FILELOCK
* structures.
*/
void
filelock_lfree(fl)
FILELOCK fl;
{
TH_STRUC_LFREE(FILELOCK,fl,filelock_free);
}
static void
filelock_unreaders(FILELOCK flock)
{
/* Assumes p_th_mutexFILES is locked already */
assert(flock->readers > 0);
if (!(--flock->readers)) {
EXTRACT_ITEM(flock,filelock_locked);
filelock_free(flock);
}
}
static void
filelock_unwriters(FILELOCK flock)
{
/* Assumes p_th_mutexFILES is locked already */
assert(flock->readers == -1);
EXTRACT_ITEM(flock, filelock_locked);
filelock_free(flock);
}
void
filelock_freespares()
/* This is used for filelocks to free up space in the child */
{
TH_FREESPARES(filelock,FILELOCK);
}
void
filelock_release(const char *filename, int readonly)
{
FILELOCK flock;
p_th_mutex_lock(p_th_mutexFILES);
flock = filelock_locked;
FIND_FNCTN_LIST(flock, name, filename, stequal);
assert(flock); /* it better be locked */
if (readonly) {
filelock_unreaders(flock); /* May destroy flock */
} else {
filelock_unwriters(flock); /* May destroy flock */
}
filelock_open--;
p_th_mutex_unlock(p_th_mutexFILES);
}
int
locked_fclose_A(FILE *afile, const char *filename, int readonly)
{
int retval;
/* Assumes already obtained filelock for filename */
retval = fclose(afile);
/* At least on solaris, this can return an error for no apparent reason */
#if 0
assert(!retval);
#endif
filelock_release(filename, readonly);
return(retval);
}
void
filelock_obtain(const char *filename, int readonly)
{
FILELOCK(flock);
int haswaited = 0;
for (;;) {
p_th_mutex_lock(p_th_mutexFILES);
flock = filelock_locked;
FIND_FNCTN_LIST(flock, name, filename,stequal);
if (!flock) { /* didnt find a matching lock */
flock = filelock_alloc();
flock->name = stcopy(filename);
flock->readers = 0;
APPEND_ITEM(flock,filelock_locked);
}
/* Found, or created a matching lock */
if (readonly) {
if (flock->readers >= 0) {
flock->readers++;
break;
}
/* Drops thru here, if we want it readonly, but someone else writing*/
} else {
if (flock->readers == 0) {
flock->readers = -1; /* Special code for writer */
break;
}
/* Drops thru here, if we want to write, but someone else
is reading/writing */
}
/* At this point we cant lock it, so unlock mutex, wait, and try again*/
p_th_mutex_unlock(p_th_mutexFILES);
plog(L_QUEUE_INFO,NOREQ, "Waiting for filelock for %s", filename, 0);
if (!haswaited) filelock_sepwaits++;
filelock_secwaits++;
sleep(1); /* Maybe too long */
} /*for*/
if (++filelock_open > filelock_open_max) {
filelock_open_max = filelock_open;
}
/* break is always done with mutex locked */
p_th_mutex_unlock(p_th_mutexFILES);
}
FILE *
locked_fopen(const char *filename, const char *mode)
{
FILELOCK flock;
FILE *retval;
int readonly = stequal(mode,"r");
filelock_obtain(filename,readonly); /* Will wait till available */
if (!(retval = fopen(filename,mode))) {
filelock_release(filename, readonly);
}
return retval;
}
/* Suuitable sequence for creating a file via a temporary
filelock_obtain(filename,FALSE);
if (!(afile = locked_fopen(tmpfilename, "w")) )
filelock_release(filename,FALSE);
written stuff to the file - retval set if fails
return(locked_fclose_and_rename(afile, tmpfilename, filenam,retvale));
*/
int
locked_fclose_and_rename(
FILE *afile, /* Open "w" temporary file */
const char *tmpfilename, /* Name of temp file */
const char *filename, /* Name desired */
int retval) /* FALSE if ok to rename, TRUE cleanup*/
{
if (fflush(afile) || ferror(afile)) { retval = PFAILURE; }
if (locked_fclose_A(afile, tmpfilename, FALSE)) { retval = PFAILURE; }
if (!retval) { /* Dont attempt to rename if failed */
if (rename(tmpfilename, filename)) { retval = PFAILURE; }
}
unlink(tmpfilename);
filelock_release(filename,FALSE);
return(retval);
}