1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "debug.h"
#include "exit.h"
#include "locks.h"
#include "pids.h"
#include "shm.h"
#include "trinity.h"
#include "utils.h"
/*
* Check that the processes holding locks are still alive.
*/
static bool check_lock(lock_t *_lock)
{
pid_t pid;
/* We don't care about unlocked or locking-in-progress */
if (_lock->lock != LOCKED)
return FALSE;
/* First the easy case. If it's held by a dead pid, release it. */
pid = _lock->owner;
/* if we're in the process of unlocking, it can show up as LOCKED
* but with no owner. Just bail, we'll try again next time around.
*/
if (pid == 0)
return FALSE;
if (pid_alive(pid) == FALSE) {
if (errno != ESRCH)
return TRUE;
debugf("Found a lock held by dead pid %d. Freeing.\n", pid);
unlock(_lock);
return TRUE;
}
return FALSE;
}
/* returns TRUE if something is awry */
bool check_all_locks(void)
{
unsigned int i;
bool ret = FALSE;
check_lock(&shm->syscalltable_lock);
for_each_child(i)
ret |= check_lock(&shm->children[i]->syscall.lock);
return ret;
}
static void __lock(lock_t *_lock)
{
_lock->lock = LOCKING;
_lock->owner = getpid();
_lock->lock = LOCKED;
}
bool trylock(lock_t *_lock)
{
if (_lock->lock == UNLOCKED) {
__lock(_lock);
return TRUE;
}
return FALSE;
}
void lock(lock_t *_lock)
{
pid_t pid = getpid();
while (_lock->lock != UNLOCKED) {
if (_lock->owner == pid) {
debugf("lol, already have lock!\n");
show_backtrace();
panic(EXIT_LOCKING_CATASTROPHE);
_exit(EXIT_FAILURE);
}
/* This is pretty horrible. But if we call lock()
* from stuck_syscall_info(), and a child is hogging a lock
* (or worse, a dead child), we'll deadlock, because main won't
* ever get back, and subsequently check_lock().
* So we add an extra explicit check here.
*/
if (pid == mainpid) {
check_lock(_lock);
} else {
/* Ok, we're a child pid. If we reached the limit, just exit */
if (shm->exit_reason == EXIT_REACHED_COUNT)
_exit(EXIT_SUCCESS);
/* if something bad happened, like main crashed,
* we don't want to spin forever, so just get out.
*/
if (shm->exit_reason != STILL_RUNNING)
_exit(EXIT_FAILURE);
}
usleep(1);
}
__lock(_lock);
}
void unlock(lock_t *_lock)
{
asm volatile("" ::: "memory");
_lock->owner = 0;
_lock->lock = UNLOCKED;
}
/*
* Release a lock we already hold.
*
* This function should be used sparingly. It's pretty much never something
* that you'll need, just for rare occasions like when we return from a
* signal handler with a lock held.
*/
void bust_lock(lock_t *_lock)
{
if (_lock->lock == UNLOCKED)
return;
if (getpid() != _lock->owner)
return;
unlock(_lock);
}
|