aboutsummaryrefslogtreecommitdiff
path: root/src/libthread/rendez.c
blob: 62b825b5eeaa12a6f491c20359b13a6390d2910a (plain)
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
#include "threadimpl.h"

Rgrp _threadrgrp;
static int isdirty;
int _threadhighnrendez;
int _threadnrendez;
static int nrendez;

static ulong
finish(Thread *t, ulong val)
{
	ulong ret;

	ret = t->rendval;
	t->rendval = val;
	while(t->state == Running)
		sleep(0);
	lock(&t->proc->lock);
	if(t->state == Rendezvous){	/* not always true: might be Dead */
		t->state = Ready;
		_threadready(t);
	}
	unlock(&t->proc->lock);
	return ret;
}

ulong
_threadrendezvous(ulong tag, ulong val)
{
	ulong ret;
	Thread *t, **l;

	lock(&_threadrgrp.lock);
_threadnrendez++;
	l = &_threadrgrp.hash[tag%nelem(_threadrgrp.hash)];
	for(t=*l; t; l=&t->rendhash, t=*l){
		if(t->rendtag==tag){
			_threaddebug(DBGREND, "Rendezvous with thread %d.%d", t->proc->pid, t->id);
			*l = t->rendhash;
			ret = finish(t, val);
			--nrendez;
			unlock(&_threadrgrp.lock);
			return ret;
		}
	}

	/* Going to sleep here. */
	t = _threadgetproc()->thread;
	t->rendbreak = 0;
	t->inrendez = 1;
	t->rendtag = tag;
	t->rendval = val;
	t->rendhash = *l;
	*l = t;
	t->nextstate = Rendezvous;
	++nrendez;
	if(nrendez > _threadhighnrendez)
		_threadhighnrendez = nrendez;
	_threaddebug(DBGREND, "Rendezvous for tag %lud", t->rendtag);
	unlock(&_threadrgrp.lock);
	_sched();
	t->inrendez = 0;
	_threaddebug(DBGREND, "Woke after rendezvous; val is %lud", t->rendval);
	return t->rendval;
}

/*
 * This is called while holding _threadpq.lock and p->lock,
 * so we can't lock _threadrgrp.lock.  Instead our caller has 
 * to call _threadbreakrendez after dropping those locks.
 */
void
_threadflagrendez(Thread *t)
{
	t->rendbreak = 1;
	isdirty = 1;
}

void
_threadbreakrendez(void)
{
	int i;
	Thread *t, **l;

	if(isdirty == 0)
		return;
	lock(&_threadrgrp.lock);
	if(isdirty == 0){
		unlock(&_threadrgrp.lock);
		return;
	}
	isdirty = 0;
	for(i=0; i<nelem(_threadrgrp.hash); i++){
		l = &_threadrgrp.hash[i];
		for(t=*l; t; t=*l){
			if(t->rendbreak){
				*l = t->rendhash;
				finish(t, ~0);
			}else
				 l=&t->rendhash;
		}
	}
	unlock(&_threadrgrp.lock);
}