xref: /plan9-contrib/sys/src/cmd/fossil/bwatch.c (revision d7aba6c3b511bc618cf0c53345848188fc02611a)
15e96a66cSDavid du Colombier #include "stdinc.h"
25e96a66cSDavid du Colombier #include "dat.h"
35e96a66cSDavid du Colombier #include "fns.h"
45e96a66cSDavid du Colombier #include "error.h"
55e96a66cSDavid du Colombier 
65e96a66cSDavid du Colombier /*
75e96a66cSDavid du Colombier  * Lock watcher.  Check that locking of blocks is always down.
85e96a66cSDavid du Colombier  *
95e96a66cSDavid du Colombier  * This is REALLY slow, and it won't work when the blocks aren't
105e96a66cSDavid du Colombier  * arranged in a tree (e.g., after the first snapshot).  But it's great
115e96a66cSDavid du Colombier  * for debugging.
125e96a66cSDavid du Colombier  */
135e96a66cSDavid du Colombier enum
145e96a66cSDavid du Colombier {
155e96a66cSDavid du Colombier 	MaxLock = 16,
165e96a66cSDavid du Colombier 	HashSize = 1009,
175e96a66cSDavid du Colombier };
185e96a66cSDavid du Colombier 
195e96a66cSDavid du Colombier /*
205e96a66cSDavid du Colombier  * Thread-specific watch state.
215e96a66cSDavid du Colombier  */
225e96a66cSDavid du Colombier typedef struct WThread WThread;
235e96a66cSDavid du Colombier struct WThread
245e96a66cSDavid du Colombier {
255e96a66cSDavid du Colombier 	Block *b[MaxLock];	/* blocks currently held */
265e96a66cSDavid du Colombier 	uint nb;
275e96a66cSDavid du Colombier 	uint pid;
285e96a66cSDavid du Colombier };
295e96a66cSDavid du Colombier 
305e96a66cSDavid du Colombier typedef struct WMap WMap;
315e96a66cSDavid du Colombier typedef struct WEntry WEntry;
325e96a66cSDavid du Colombier 
335e96a66cSDavid du Colombier struct WEntry
345e96a66cSDavid du Colombier {
355e96a66cSDavid du Colombier 	uchar c[VtScoreSize];
365e96a66cSDavid du Colombier 	uchar p[VtScoreSize];
375e96a66cSDavid du Colombier 	int off;
385e96a66cSDavid du Colombier 
395e96a66cSDavid du Colombier 	WEntry *cprev;
405e96a66cSDavid du Colombier 	WEntry *cnext;
415e96a66cSDavid du Colombier 	WEntry *pprev;
425e96a66cSDavid du Colombier 	WEntry *pnext;
435e96a66cSDavid du Colombier };
445e96a66cSDavid du Colombier 
455e96a66cSDavid du Colombier struct WMap
465e96a66cSDavid du Colombier {
47*d7aba6c3SDavid du Colombier 	QLock lk;
485e96a66cSDavid du Colombier 
495e96a66cSDavid du Colombier 	WEntry *hchild[HashSize];
505e96a66cSDavid du Colombier 	WEntry *hparent[HashSize];
515e96a66cSDavid du Colombier };
525e96a66cSDavid du Colombier 
535e96a66cSDavid du Colombier static WMap map;
545e96a66cSDavid du Colombier static void **wp;
555e96a66cSDavid du Colombier static uint blockSize;
565e96a66cSDavid du Colombier static WEntry *pool;
575e96a66cSDavid du Colombier uint bwatchDisabled;
585e96a66cSDavid du Colombier 
595e96a66cSDavid du Colombier static uint
hash(uchar score[VtScoreSize])605e96a66cSDavid du Colombier hash(uchar score[VtScoreSize])
615e96a66cSDavid du Colombier {
625e96a66cSDavid du Colombier 	uint i, h;
635e96a66cSDavid du Colombier 
645e96a66cSDavid du Colombier 	h = 0;
655e96a66cSDavid du Colombier 	for(i=0; i<VtScoreSize; i++)
665e96a66cSDavid du Colombier 		h = h*37 + score[i];
675e96a66cSDavid du Colombier 	return h%HashSize;
685e96a66cSDavid du Colombier }
695e96a66cSDavid du Colombier 
705e96a66cSDavid du Colombier #include <pool.h>
715e96a66cSDavid du Colombier static void
freeWEntry(WEntry * e)725e96a66cSDavid du Colombier freeWEntry(WEntry *e)
735e96a66cSDavid du Colombier {
745e96a66cSDavid du Colombier 	memset(e, 0, sizeof(WEntry));
755e96a66cSDavid du Colombier 	e->pnext = pool;
765e96a66cSDavid du Colombier 	pool = e;
775e96a66cSDavid du Colombier }
785e96a66cSDavid du Colombier 
795e96a66cSDavid du Colombier static WEntry*
allocWEntry(void)805e96a66cSDavid du Colombier allocWEntry(void)
815e96a66cSDavid du Colombier {
825e96a66cSDavid du Colombier 	int i;
835e96a66cSDavid du Colombier 	WEntry *w;
845e96a66cSDavid du Colombier 
855e96a66cSDavid du Colombier 	w = pool;
865e96a66cSDavid du Colombier 	if(w == nil){
87*d7aba6c3SDavid du Colombier 		w = vtmallocz(1024*sizeof(WEntry));
885e96a66cSDavid du Colombier 		for(i=0; i<1024; i++)
895e96a66cSDavid du Colombier 			freeWEntry(&w[i]);
905e96a66cSDavid du Colombier 		w = pool;
915e96a66cSDavid du Colombier 	}
925e96a66cSDavid du Colombier 	pool = w->pnext;
935e96a66cSDavid du Colombier 	memset(w, 0, sizeof(WEntry));
945e96a66cSDavid du Colombier 	return w;
955e96a66cSDavid du Colombier }
965e96a66cSDavid du Colombier 
975e96a66cSDavid du Colombier /*
985e96a66cSDavid du Colombier  * remove all dependencies with score as a parent
995e96a66cSDavid du Colombier  */
1005e96a66cSDavid du Colombier static void
_bwatchResetParent(uchar * score)1015e96a66cSDavid du Colombier _bwatchResetParent(uchar *score)
1025e96a66cSDavid du Colombier {
1035e96a66cSDavid du Colombier 	WEntry *w, *next;
1045e96a66cSDavid du Colombier 	uint h;
1055e96a66cSDavid du Colombier 
1065e96a66cSDavid du Colombier 	h = hash(score);
1075e96a66cSDavid du Colombier 	for(w=map.hparent[h]; w; w=next){
1085e96a66cSDavid du Colombier 		next = w->pnext;
1095e96a66cSDavid du Colombier 		if(memcmp(w->p, score, VtScoreSize) == 0){
1105e96a66cSDavid du Colombier 			if(w->pnext)
1115e96a66cSDavid du Colombier 				w->pnext->pprev = w->pprev;
1125e96a66cSDavid du Colombier 			if(w->pprev)
1135e96a66cSDavid du Colombier 				w->pprev->pnext = w->pnext;
1145e96a66cSDavid du Colombier 			else
1155e96a66cSDavid du Colombier 				map.hparent[h] = w->pnext;
1165e96a66cSDavid du Colombier 			if(w->cnext)
1175e96a66cSDavid du Colombier 				w->cnext->cprev = w->cprev;
1185e96a66cSDavid du Colombier 			if(w->cprev)
1195e96a66cSDavid du Colombier 				w->cprev->cnext = w->cnext;
1205e96a66cSDavid du Colombier 			else
1215e96a66cSDavid du Colombier 				map.hchild[hash(w->c)] = w->cnext;
1225e96a66cSDavid du Colombier 			freeWEntry(w);
1235e96a66cSDavid du Colombier 		}
1245e96a66cSDavid du Colombier 	}
1255e96a66cSDavid du Colombier }
1265e96a66cSDavid du Colombier /*
1275e96a66cSDavid du Colombier  * and child
1285e96a66cSDavid du Colombier  */
1295e96a66cSDavid du Colombier static void
_bwatchResetChild(uchar * score)1305e96a66cSDavid du Colombier _bwatchResetChild(uchar *score)
1315e96a66cSDavid du Colombier {
1325e96a66cSDavid du Colombier 	WEntry *w, *next;
1335e96a66cSDavid du Colombier 	uint h;
1345e96a66cSDavid du Colombier 
1355e96a66cSDavid du Colombier 	h = hash(score);
1365e96a66cSDavid du Colombier 	for(w=map.hchild[h]; w; w=next){
1375e96a66cSDavid du Colombier 		next = w->cnext;
1385e96a66cSDavid du Colombier 		if(memcmp(w->c, score, VtScoreSize) == 0){
1395e96a66cSDavid du Colombier 			if(w->pnext)
1405e96a66cSDavid du Colombier 				w->pnext->pprev = w->pprev;
1415e96a66cSDavid du Colombier 			if(w->pprev)
1425e96a66cSDavid du Colombier 				w->pprev->pnext = w->pnext;
1435e96a66cSDavid du Colombier 			else
1445e96a66cSDavid du Colombier 				map.hparent[hash(w->p)] = w->pnext;
1455e96a66cSDavid du Colombier 			if(w->cnext)
1465e96a66cSDavid du Colombier 				w->cnext->cprev = w->cprev;
1475e96a66cSDavid du Colombier 			if(w->cprev)
1485e96a66cSDavid du Colombier 				w->cprev->cnext = w->cnext;
1495e96a66cSDavid du Colombier 			else
1505e96a66cSDavid du Colombier 				map.hchild[h] = w->cnext;
1515e96a66cSDavid du Colombier 			freeWEntry(w);
1525e96a66cSDavid du Colombier 		}
1535e96a66cSDavid du Colombier 	}
1545e96a66cSDavid du Colombier }
1555e96a66cSDavid du Colombier 
1565e96a66cSDavid du Colombier static uchar*
parent(uchar c[VtScoreSize],int * off)1575e96a66cSDavid du Colombier parent(uchar c[VtScoreSize], int *off)
1585e96a66cSDavid du Colombier {
1595e96a66cSDavid du Colombier 	WEntry *w;
1605e96a66cSDavid du Colombier 	uint h;
1615e96a66cSDavid du Colombier 
1625e96a66cSDavid du Colombier 	h = hash(c);
1635e96a66cSDavid du Colombier 	for(w=map.hchild[h]; w; w=w->cnext)
1645e96a66cSDavid du Colombier 		if(memcmp(w->c, c, VtScoreSize) == 0){
1655e96a66cSDavid du Colombier 			*off = w->off;
1665e96a66cSDavid du Colombier 			return w->p;
1675e96a66cSDavid du Colombier 		}
1685e96a66cSDavid du Colombier 	return nil;
1695e96a66cSDavid du Colombier }
1705e96a66cSDavid du Colombier 
1715e96a66cSDavid du Colombier static void
addChild(uchar p[VtEntrySize],uchar c[VtEntrySize],int off)1725e96a66cSDavid du Colombier addChild(uchar p[VtEntrySize], uchar c[VtEntrySize], int off)
1735e96a66cSDavid du Colombier {
1745e96a66cSDavid du Colombier 	uint h;
1755e96a66cSDavid du Colombier 	WEntry *w;
1765e96a66cSDavid du Colombier 
1775e96a66cSDavid du Colombier 	w = allocWEntry();
1785e96a66cSDavid du Colombier 	memmove(w->p, p, VtScoreSize);
1795e96a66cSDavid du Colombier 	memmove(w->c, c, VtScoreSize);
1805e96a66cSDavid du Colombier 	w->off = off;
1815e96a66cSDavid du Colombier 
1825e96a66cSDavid du Colombier 	h = hash(p);
1835e96a66cSDavid du Colombier 	w->pnext = map.hparent[h];
1845e96a66cSDavid du Colombier 	if(w->pnext)
1855e96a66cSDavid du Colombier 		w->pnext->pprev = w;
1865e96a66cSDavid du Colombier 	map.hparent[h] = w;
1875e96a66cSDavid du Colombier 
1885e96a66cSDavid du Colombier 	h = hash(c);
1895e96a66cSDavid du Colombier 	w->cnext = map.hchild[h];
1905e96a66cSDavid du Colombier 	if(w->cnext)
1915e96a66cSDavid du Colombier 		w->cnext->cprev = w;
1925e96a66cSDavid du Colombier 	map.hchild[h] = w;
1935e96a66cSDavid du Colombier }
1945e96a66cSDavid du Colombier 
1955e96a66cSDavid du Colombier void
bwatchReset(uchar score[VtScoreSize])1965e96a66cSDavid du Colombier bwatchReset(uchar score[VtScoreSize])
1975e96a66cSDavid du Colombier {
198*d7aba6c3SDavid du Colombier 	qlock(&map.lk);
1995e96a66cSDavid du Colombier 	_bwatchResetParent(score);
2005e96a66cSDavid du Colombier 	_bwatchResetChild(score);
201*d7aba6c3SDavid du Colombier 	qunlock(&map.lk);
2025e96a66cSDavid du Colombier }
2035e96a66cSDavid du Colombier 
2045e96a66cSDavid du Colombier void
bwatchInit(void)2055e96a66cSDavid du Colombier bwatchInit(void)
2065e96a66cSDavid du Colombier {
2075e96a66cSDavid du Colombier 	wp = privalloc();
2085e96a66cSDavid du Colombier 	*wp = nil;
2095e96a66cSDavid du Colombier }
2105e96a66cSDavid du Colombier 
2115e96a66cSDavid du Colombier void
bwatchSetBlockSize(uint bs)2125e96a66cSDavid du Colombier bwatchSetBlockSize(uint bs)
2135e96a66cSDavid du Colombier {
2145e96a66cSDavid du Colombier 	blockSize = bs;
2155e96a66cSDavid du Colombier }
2165e96a66cSDavid du Colombier 
2175e96a66cSDavid du Colombier static WThread*
getWThread(void)2185e96a66cSDavid du Colombier getWThread(void)
2195e96a66cSDavid du Colombier {
2205e96a66cSDavid du Colombier 	WThread *w;
2215e96a66cSDavid du Colombier 
2225e96a66cSDavid du Colombier 	w = *wp;
2235e96a66cSDavid du Colombier 	if(w == nil || w->pid != getpid()){
224*d7aba6c3SDavid du Colombier 		w = vtmallocz(sizeof(WThread));
2255e96a66cSDavid du Colombier 		*wp = w;
2265e96a66cSDavid du Colombier 		w->pid = getpid();
2275e96a66cSDavid du Colombier 	}
2285e96a66cSDavid du Colombier 	return w;
2295e96a66cSDavid du Colombier }
2305e96a66cSDavid du Colombier 
2315e96a66cSDavid du Colombier /*
2325e96a66cSDavid du Colombier  * Derive dependencies from the contents of b.
2335e96a66cSDavid du Colombier  */
2345e96a66cSDavid du Colombier void
bwatchDependency(Block * b)2355e96a66cSDavid du Colombier bwatchDependency(Block *b)
2365e96a66cSDavid du Colombier {
2375e96a66cSDavid du Colombier 	int i, epb, ppb;
2385e96a66cSDavid du Colombier 	Entry e;
2395e96a66cSDavid du Colombier 
2405e96a66cSDavid du Colombier 	if(bwatchDisabled)
2415e96a66cSDavid du Colombier 		return;
2425e96a66cSDavid du Colombier 
243*d7aba6c3SDavid du Colombier 	qlock(&map.lk);
2445e96a66cSDavid du Colombier 	_bwatchResetParent(b->score);
2455e96a66cSDavid du Colombier 
2465e96a66cSDavid du Colombier 	switch(b->l.type){
2475e96a66cSDavid du Colombier 	case BtData:
2485e96a66cSDavid du Colombier 		break;
2495e96a66cSDavid du Colombier 
2505e96a66cSDavid du Colombier 	case BtDir:
2515e96a66cSDavid du Colombier 		epb = blockSize / VtEntrySize;
2525e96a66cSDavid du Colombier 		for(i=0; i<epb; i++){
2535e96a66cSDavid du Colombier 			entryUnpack(&e, b->data, i);
2545e96a66cSDavid du Colombier 			if(!(e.flags & VtEntryActive))
2555e96a66cSDavid du Colombier 				continue;
2565e96a66cSDavid du Colombier 			addChild(b->score, e.score, i);
2575e96a66cSDavid du Colombier 		}
2585e96a66cSDavid du Colombier 		break;
2595e96a66cSDavid du Colombier 
2605e96a66cSDavid du Colombier 	default:
2615e96a66cSDavid du Colombier 		ppb = blockSize / VtScoreSize;
2625e96a66cSDavid du Colombier 		for(i=0; i<ppb; i++)
2635e96a66cSDavid du Colombier 			addChild(b->score, b->data+i*VtScoreSize, i);
2645e96a66cSDavid du Colombier 		break;
2655e96a66cSDavid du Colombier 	}
266*d7aba6c3SDavid du Colombier 	qunlock(&map.lk);
2675e96a66cSDavid du Colombier }
2685e96a66cSDavid du Colombier 
2695e96a66cSDavid du Colombier static int
depth(uchar * s)2705e96a66cSDavid du Colombier depth(uchar *s)
2715e96a66cSDavid du Colombier {
2725e96a66cSDavid du Colombier 	int d, x;
2735e96a66cSDavid du Colombier 
2745e96a66cSDavid du Colombier 	d = -1;
2755e96a66cSDavid du Colombier 	while(s){
2765e96a66cSDavid du Colombier 		d++;
2775e96a66cSDavid du Colombier 		s = parent(s, &x);
2785e96a66cSDavid du Colombier 	}
2795e96a66cSDavid du Colombier 	return d;
2805e96a66cSDavid du Colombier }
2815e96a66cSDavid du Colombier 
2825e96a66cSDavid du Colombier static int
lockConflicts(uchar xhave[VtScoreSize],uchar xwant[VtScoreSize])2835e96a66cSDavid du Colombier lockConflicts(uchar xhave[VtScoreSize], uchar xwant[VtScoreSize])
2845e96a66cSDavid du Colombier {
2855e96a66cSDavid du Colombier 	uchar *have, *want;
2865e96a66cSDavid du Colombier 	int havedepth, wantdepth, havepos, wantpos;
2875e96a66cSDavid du Colombier 
2885e96a66cSDavid du Colombier 	have = xhave;
2895e96a66cSDavid du Colombier 	want = xwant;
2905e96a66cSDavid du Colombier 
2915e96a66cSDavid du Colombier 	havedepth = depth(have);
2925e96a66cSDavid du Colombier 	wantdepth = depth(want);
2935e96a66cSDavid du Colombier 
2945e96a66cSDavid du Colombier 	/*
2955e96a66cSDavid du Colombier 	 * walk one or the other up until they're both
2965e96a66cSDavid du Colombier  	 * at the same level.
2975e96a66cSDavid du Colombier 	 */
2985e96a66cSDavid du Colombier 	havepos = -1;
2995e96a66cSDavid du Colombier 	wantpos = -1;
3005e96a66cSDavid du Colombier 	have = xhave;
3015e96a66cSDavid du Colombier 	want = xwant;
3025e96a66cSDavid du Colombier 	while(wantdepth > havedepth){
3035e96a66cSDavid du Colombier 		wantdepth--;
3045e96a66cSDavid du Colombier 		want = parent(want, &wantpos);
3055e96a66cSDavid du Colombier 	}
3065e96a66cSDavid du Colombier 	while(havedepth > wantdepth){
3075e96a66cSDavid du Colombier 		havedepth--;
3085e96a66cSDavid du Colombier 		have = parent(have, &havepos);
3095e96a66cSDavid du Colombier 	}
3105e96a66cSDavid du Colombier 
3115e96a66cSDavid du Colombier 	/*
3125e96a66cSDavid du Colombier 	 * walk them up simultaneously until we reach
3135e96a66cSDavid du Colombier 	 * a common ancestor.
3145e96a66cSDavid du Colombier 	 */
3155e96a66cSDavid du Colombier 	while(have && want && memcmp(have, want, VtScoreSize) != 0){
3165e96a66cSDavid du Colombier 		have = parent(have, &havepos);
3175e96a66cSDavid du Colombier 		want = parent(want, &wantpos);
3185e96a66cSDavid du Colombier 	}
3195e96a66cSDavid du Colombier 
3205e96a66cSDavid du Colombier 	/*
3215e96a66cSDavid du Colombier 	 * not part of same tree.  happens mainly with
3225e96a66cSDavid du Colombier 	 * newly allocated blocks.
3235e96a66cSDavid du Colombier 	 */
3245e96a66cSDavid du Colombier 	if(!have || !want)
3255e96a66cSDavid du Colombier 		return 0;
3265e96a66cSDavid du Colombier 
3275e96a66cSDavid du Colombier 	/*
3285e96a66cSDavid du Colombier 	 * never walked want: means we want to lock
3295e96a66cSDavid du Colombier 	 * an ancestor of have.  no no.
3305e96a66cSDavid du Colombier 	 */
3315e96a66cSDavid du Colombier 	if(wantpos == -1)
3325e96a66cSDavid du Colombier 		return 1;
3335e96a66cSDavid du Colombier 
3345e96a66cSDavid du Colombier 	/*
3355e96a66cSDavid du Colombier 	 * never walked have: means we want to lock a
3365e96a66cSDavid du Colombier 	 * child of have.  that's okay.
3375e96a66cSDavid du Colombier 	 */
3385e96a66cSDavid du Colombier 	if(havepos == -1)
3395e96a66cSDavid du Colombier 		return 0;
3405e96a66cSDavid du Colombier 
3415e96a66cSDavid du Colombier 	/*
3425e96a66cSDavid du Colombier 	 * walked both: they're from different places in the tree.
3435e96a66cSDavid du Colombier 	 * require that the left one be locked before the right one.
3445e96a66cSDavid du Colombier 	 * (this is questionable, but it puts a total order on the block tree).
3455e96a66cSDavid du Colombier 	 */
3465e96a66cSDavid du Colombier 	return havepos < wantpos;
3475e96a66cSDavid du Colombier }
3485e96a66cSDavid du Colombier 
3495e96a66cSDavid du Colombier static void
stop(void)3505e96a66cSDavid du Colombier stop(void)
3515e96a66cSDavid du Colombier {
3525e96a66cSDavid du Colombier 	int fd;
3535e96a66cSDavid du Colombier 	char buf[32];
3545e96a66cSDavid du Colombier 
3555e96a66cSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/ctl", getpid());
3565e96a66cSDavid du Colombier 	fd = open(buf, OWRITE);
3575e96a66cSDavid du Colombier 	write(fd, "stop", 4);
3585e96a66cSDavid du Colombier 	close(fd);
3595e96a66cSDavid du Colombier }
3605e96a66cSDavid du Colombier 
3615e96a66cSDavid du Colombier /*
3625e96a66cSDavid du Colombier  * Check whether the calling thread can validly lock b.
3635e96a66cSDavid du Colombier  * That is, check that the calling thread doesn't hold
3645e96a66cSDavid du Colombier  * locks for any of b's children.
3655e96a66cSDavid du Colombier  */
3665e96a66cSDavid du Colombier void
bwatchLock(Block * b)3675e96a66cSDavid du Colombier bwatchLock(Block *b)
3685e96a66cSDavid du Colombier {
3695e96a66cSDavid du Colombier 	int i;
3705e96a66cSDavid du Colombier 	WThread *w;
3715e96a66cSDavid du Colombier 
3725e96a66cSDavid du Colombier 	if(bwatchDisabled)
3735e96a66cSDavid du Colombier 		return;
3745e96a66cSDavid du Colombier 
3755e96a66cSDavid du Colombier 	if(b->part != PartData)
3765e96a66cSDavid du Colombier 		return;
3775e96a66cSDavid du Colombier 
378*d7aba6c3SDavid du Colombier 	qlock(&map.lk);
3795e96a66cSDavid du Colombier 	w = getWThread();
3805e96a66cSDavid du Colombier 	for(i=0; i<w->nb; i++){
3815e96a66cSDavid du Colombier 		if(lockConflicts(w->b[i]->score, b->score)){
3825e96a66cSDavid du Colombier 			fprint(2, "%d: have block %V; shouldn't lock %V\n",
3835e96a66cSDavid du Colombier 				w->pid, w->b[i]->score, b->score);
3845e96a66cSDavid du Colombier 			stop();
3855e96a66cSDavid du Colombier 		}
3865e96a66cSDavid du Colombier 	}
387*d7aba6c3SDavid du Colombier 	qunlock(&map.lk);
3885e96a66cSDavid du Colombier 	if(w->nb >= MaxLock){
3895e96a66cSDavid du Colombier 		fprint(2, "%d: too many blocks held\n", w->pid);
3905e96a66cSDavid du Colombier 		stop();
3915e96a66cSDavid du Colombier 	}else
3925e96a66cSDavid du Colombier 		w->b[w->nb++] = b;
3935e96a66cSDavid du Colombier }
3945e96a66cSDavid du Colombier 
3955e96a66cSDavid du Colombier /*
3965e96a66cSDavid du Colombier  * Note that the calling thread is about to unlock b.
3975e96a66cSDavid du Colombier  */
3985e96a66cSDavid du Colombier void
bwatchUnlock(Block * b)3995e96a66cSDavid du Colombier bwatchUnlock(Block *b)
4005e96a66cSDavid du Colombier {
4015e96a66cSDavid du Colombier 	int i;
4025e96a66cSDavid du Colombier 	WThread *w;
4035e96a66cSDavid du Colombier 
4045e96a66cSDavid du Colombier 	if(bwatchDisabled)
4055e96a66cSDavid du Colombier 		return;
4065e96a66cSDavid du Colombier 
4075e96a66cSDavid du Colombier 	if(b->part != PartData)
4085e96a66cSDavid du Colombier 		return;
4095e96a66cSDavid du Colombier 
4105e96a66cSDavid du Colombier 	w = getWThread();
4115e96a66cSDavid du Colombier 	for(i=0; i<w->nb; i++)
4125e96a66cSDavid du Colombier 		if(w->b[i] == b)
4135e96a66cSDavid du Colombier 			break;
4145e96a66cSDavid du Colombier 	if(i>=w->nb){
4155e96a66cSDavid du Colombier 		fprint(2, "%d: unlock of unlocked block %V\n", w->pid, b->score);
4165e96a66cSDavid du Colombier 		stop();
4175e96a66cSDavid du Colombier 	}else
4185e96a66cSDavid du Colombier 		w->b[i] = w->b[--w->nb];
4195e96a66cSDavid du Colombier }
4205e96a66cSDavid du Colombier 
421