Mercurial > noffle
view src/lock.c @ 167:7ba337dafb2c noffle
[svn] * TODO,content.c,lock.c,server.c,server.h: Remove bug notice re:
need to do a Cont_write on Lock_closeDatabases in case of unwritten
content changes when releasing the lock. Update content to keep dirty
flag and avoid unnecessary writes, and lock to signal server to re-read
its group content info after the lock is released. Do NOT write content
info on Lock_close if dirty, as the placeholder article in unsubscribed
groups is currently done by adding it to the content when joining the
group and deliberately not saving it unless another content modification
takes place and thus causes the content to be saved.
author | bears |
---|---|
date | Sun, 25 Feb 2001 23:29:50 +0000 |
parents | 8ea6b5ddc5a5 |
children | c912e8288164 |
line wrap: on
line source
/* lock.c $Id: lock.c 260 2001-02-25 23:29:50Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include "lock.h" #include <errno.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #if TIME_WITH_SYS_TIME #include <sys/time.h> #include <time.h> #else #if HAVE_SYS_TIME_H #include <sys/time.h> #else #include <time.h> #endif #endif #include <unistd.h> #include "configfile.h" #include "content.h" #include "log.h" #include "database.h" #include "group.h" #include "request.h" #include "portable.h" #include "server.h" #include "util.h" struct Lock { const char *name; int lockFd; Str lockFile; Bool doLazyLocking; volatile Bool lazyClose; volatile Bool lazyLockBusy; }; static struct Lock globalLock = { "global", -1, "", TRUE, FALSE, FALSE }; static struct Lock fetchLock = { "fetch", -1, "", FALSE, FALSE, FALSE }; static sig_t oldHandler = NULL; /* Block/unblock SIGUSR1. */ static Bool blockSignal( Bool block ) { sigset_t sigs; sigemptyset(&sigs); sigaddset(&sigs, SIGUSR1); for(;;) { if ( sigprocmask( block ? SIG_BLOCK : SIG_UNBLOCK, &sigs, NULL ) != 0 ) { if ( errno != EINTR ) { Log_err( "Can't block/unblock signal" ); return FALSE; } } else return TRUE; } /* NOTREACHED */ } /* Check the global lock held. */ static Bool gotLock( struct Lock *lock ) { return ( lock->lockFd != -1 ); } static void lockWaitAlarm( int sig ) { UNUSED( sig ); return; } static Bool waitLock( struct Lock *lock, enum LockRequestWait wait ) { int fd; struct flock l; ASSERT( ! gotLock( lock ) ); Log_dbg( "Waiting for lock %s ...", lock->name ); if ( lock->lockFile[ 0 ] == '\0' ) snprintf( lock->lockFile, MAXCHAR, "%s/lock/%s", Cfg_spoolDir(), lock->name ); if ( ( fd = open( lock->lockFile, O_WRONLY | O_CREAT, 0644 ) ) < 0 ) { Log_err( "Cannot open %s (%s)", lock->lockFile, strerror( errno ) ); return FALSE; } l.l_type = F_WRLCK; l.l_start = 0; l.l_whence = SEEK_SET; l.l_len = 0; if ( wait == LOCK_WAIT ) { sig_t oldAlarmHandler; unsigned oldAlarm; oldAlarmHandler = Utl_installSignalHandler( SIGALRM, lockWaitAlarm ); oldAlarm = alarm( 1 ); for(;;) { alarm( 1 ); if ( fcntl( fd, F_SETLKW, &l ) < 0 ) if ( errno != EINTR ) { Utl_installSignalHandler( SIGALRM, oldAlarmHandler ); alarm( oldAlarm ); Log_err( "Cannot lock %s: %s", lock->lockFile, strerror( errno ) ); close( lock->lockFd ); return FALSE; } else { /* Send SIGUSR1 to the process holding the lock. */ if ( fcntl( fd, F_GETLK, &l) == 0 && l.l_type != F_UNLCK ) if ( kill( l.l_pid, SIGUSR1 ) < 0 ) Log_err( "Can't signal process %d: %s", l.l_pid, strerror( errno ) ); } else break; } Utl_installSignalHandler( SIGALRM, oldAlarmHandler ); alarm( oldAlarm ); } else { if ( fcntl( fd, F_SETLK, &l ) < 0 ) { close( lock->lockFd ); return FALSE; } } lock->lockFd = fd; Log_dbg( "Lock successful" ); return TRUE; } static void releaseLock( struct Lock *lock ) { struct flock l; ASSERT( gotLock( lock ) ); l.l_type = F_UNLCK; l.l_start = 0; l.l_whence = SEEK_SET; l.l_len = 0; if ( fcntl( lock->lockFd, F_SETLK, &l ) < 0 ) Log_err( "Cannot release %s: %s", lock->lockFile, strerror( errno ) ); close( lock->lockFd ); lock->lockFd = -1; Log_dbg( "Releasing lock" ); } static Bool openDatabases( void ) { globalLock.lazyClose = FALSE; if ( ! waitLock( &globalLock, LOCK_WAIT ) ) { Log_err( "Could not get write lock" ); return FALSE; } if ( ! Db_open() ) { Log_err( "Could not open database" ); releaseLock( &globalLock ); return FALSE; } if ( ! Grp_open() ) { Log_err( "Could not open groupinfo" ); Db_close(); releaseLock( &globalLock ); return FALSE; } if ( ! Req_open() ) { Log_err( "Could not initialize request database" ); Grp_close(); Db_close(); releaseLock( &globalLock ); return FALSE; } globalLock.lazyClose = globalLock.doLazyLocking; globalLock.lazyLockBusy = TRUE; return TRUE; } static void closeDatabases( void ) { Grp_close(); Db_close(); Req_close(); Server_flushCache(); releaseLock( &globalLock ); globalLock.lazyLockBusy = FALSE; globalLock.lazyClose = FALSE; } static void lockSignal( int sig ) { UNUSED( sig ); if ( globalLock.lazyLockBusy ) globalLock.lazyClose = FALSE; else closeDatabases(); return; } /* Open all databases and set global lock. */ Bool Lock_openDatabases( void ) { Bool res; /* First time - need to initialise signal handler? */ if ( oldHandler == NULL ) oldHandler = Utl_installSignalHandler( SIGUSR1, lockSignal ); if ( ! blockSignal( TRUE ) ) return FALSE; if ( ! globalLock.lazyClose ) res = openDatabases(); else res = TRUE; globalLock.lazyLockBusy = res; if ( ! blockSignal( FALSE ) ) return FALSE; return res; } /* Close all databases and release global lock. */ void Lock_closeDatabases( void ) { blockSignal( TRUE ); if ( ! globalLock.lazyClose ) closeDatabases(); else globalLock.lazyLockBusy = FALSE; blockSignal( FALSE ); } /* Sync al databases to disc. Maintain global lock status. */ void Lock_syncDatabases( void ) { Bool wasOpen; if ( gotLock( &globalLock ) ) { blockSignal( TRUE ); wasOpen = globalLock.lazyLockBusy; closeDatabases(); if ( wasOpen ) openDatabases(); blockSignal( FALSE ); } } /* Check the global lock held. */ Bool Lock_gotLock( void ) { return globalLock.lazyLockBusy; } /* Get fetch lock. */ Bool Lock_getFetchLock( enum LockRequestWait wait ) { return waitLock( &fetchLock, wait ); } /* Release fetch lock. */ void Lock_releaseFetchLock( void ) { releaseLock( &fetchLock ); } /* Check the fetch lock held. */ Bool Lock_fetchLock( void ) { return gotLock( &fetchLock ); }