File: CondVar.sc

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (122 lines) | stat: -rw-r--r-- 3,700 bytes parent folder | download | duplicates (3)
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
CondVar {
	var waitingThreads;

	*new { ^super.newCopyArgs(Array.new) }

	wait { |predicate|
		if(predicate.isNil) {
			this.prWait
		} {
			// If the predicate is already true, we return immediately. If not,
			// we block until the predicate is true after being signalled.
			// The waiting thread may be signaled several times before the predicate
			// becomes true, hence the loop is necessary.
			while { predicate.value.not } { this.prWait }
		}
	}

	waitFor { |timeoutBeats, predicate|
		var endingBeats;

		// May throw if preconditions are not met.
		timeoutBeats = this.prConvertTimeoutBeatsToSafeValue(timeoutBeats);

		// This is a slightly more complex version of the body of `wait`. The
		// extra complexity comes from maintaining the timeout requirement.
		if(predicate.isNil) {
			// Interface requirement: immediately return if timeout is non-positive
			if(timeoutBeats <= 0) { ^false };
			^this.prWaitFor(timeoutBeats)
		} {
			endingBeats = thisThread.beats + timeoutBeats;

			// The waiting thread may be signaled several times before the predicate
			// becomes true, so the loop is necessary.
			while {
				predicate.value.not
			} {
				// We only get here when the predicate is not true, so we can return
				// false immediately if we are timed out.
				if(thisThread.beats >= endingBeats) { ^false };
				// If the timeout expires, return the value of the predicate; otherwise,
				// we were signalled, so go back through the loop.
				if(this.prWaitFor(endingBeats - thisThread.beats).not) { ^predicate.value };
			};

			^true
		}
	}

	signalOne {
		if (waitingThreads.isEmpty.not) {
			this.prWakeThread(waitingThreads.removeAt(0));
		}
	}

	signalAll {
		waitingThreads.do(this.prWakeThread(_));
		waitingThreads = Array.new;
	}

	// Also disables `copy`, since it redirects to `shallowCopy` on Object
	shallowCopy { this.shouldNotImplement(thisMethod) }
	deepCopy { this.shouldNotImplement(thisMethod) }

	prWait {
		this.prSleepThread(thisThread);
		\wait.yield
	}

	// Returns true iff we were woken via signal (and not timeout)
	prWaitFor { |timeoutBeats|
		// precondition: timeoutBeats is a Float or Integer, positive, and not inf
		var waitingThread = thisThread;
		var didNotTimeout = true;
		var timeoutThread = Routine {
			var wokenThread;

			timeoutBeats.wait;

			didNotTimeout = false;

			// If our waiting thread is signaled before this timeout fires
			// the timeout routine will be stopped after prWait below
			// and we won't get here.
			wokenThread = this.prRemoveWaitingThread(waitingThread);
			// if wokenThread is nil, we asssume a signal happened
			// immediately prior to timeout and the thread is already awake
			// no call to prWakeThread happens in this case
			if(wokenThread.notNil) { this.prWakeThread(wokenThread) };
		};

		timeoutThread.play(thisThread.clock);
		this.prWait;
		timeoutThread.stop;

		^didNotTimeout
	}

	prSleepThread { |t| waitingThreads = waitingThreads.add(t.threadPlayer) }
	prWakeThread { |t| t.clock.sched(0, t) }
	prRemoveWaitingThread { |t| ^waitingThreads.remove(t.threadPlayer) }

	// Precondition checks for `waitFor` timeout. May throw.
	prConvertTimeoutBeatsToSafeValue { |n|
		n = case
			{ n.class === Float or: { n.class === Integer } } { n }
			{ n.respondsTo(\asInteger) } { n.asInteger }
			{ n.respondsTo(\asFloat) } { n.asFloat }
			{ n };

		if(n.class !== Float and: { n.class !== Integer }) {
			Error("Timeout must be a Float or Integer, or convertible to one").throw
		};

		if(n.isNaN) { Error("Timeout must not be NaN").throw };

		// -inf is checked in negative-time check after this.
		if(n === inf) { Error("Timeout must not be inf; use `wait` instead").throw };

		^n
	}
}