forked from kernelslacker/trinity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
locks.c
123 lines (107 loc) · 2.45 KB
/
locks.c
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
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "debug.h"
#include "locks.h"
#include "log.h"
#include "pids.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 (pid_alive(pid) == -1) {
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 something bad happened, like main crashed,
* we don't want to spin forever, so just get out.
*/
if ((shm->exit_reason != STILL_RUNNING) &&
(shm->exit_reason != EXIT_REACHED_COUNT)) {
_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);
}