File: behavior.bs

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (265 lines) | stat: -rw-r--r-- 5,897 bytes parent folder | download
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
use ui;
use layout;
use progvis:program;
use core:geometry;

/**
 * Object containing callbacks that is called by the system as a response to different things the
 * program is doing. This is used to encapsulate the entire behavior of the front-end depending on
 * its current state.
 */
class Behavior on Ui {
	// Called when something went wrong in the running program.
	void onProgramError(ProgramError error) {}

	// Called when the user performed some action. If the string is recorded, the same sequence can
	// then be played back.
	void onUserAction(Str action) {}

	// Called when the program is restarted.
	void onRestart() {}

	// Called when this behavior is disposed.
	void onDispose() {}

	// Allow reloading this program?
	Str? allowReload() { null; }

	// Allow starting the model checker.
	Str? allowModelCheck() { null; }

	// Allow spawning another thread?
	Str? allowSpawnThread() { null; }

	// Allow changing "track reads/writes". If this returns "false", track memory is forced on.
	Bool allowTrackMemory() { true; }

	// Allow disabling leak checking.
	Bool allowNoLeaks() { true; }

	// Get the panel to show during rendering.
	ProblemPanel? panel() { null; }

	// Data used by the painter. In a separate object to avoid thread switches.
	BehaviorConfig config;
}


/**
 * Behavior config. Communicates with the painter efficiently.
 */
class BehaviorConfig on Render {
	init() {
		init {
			// Default is to allow stepping.
			step = true;
		}
	}

	// Return an action to mark.
	Str? mark() { data; }

	// Set the mark.
	assign mark(Str m) { data = m; }
	assign mark(Str? m) { data = m; }

	// Allow single-stepping threads?
	Bool allowStep() { step; }

	// Set allow step.
	assign allowStep(Bool v) { step = v; }

	// The data.
	private Str? data;

	// Data.
	private Bool step;
}


/**
 * Default callbacks for the main Progvis window.
 *
 * Simply displays any errors and ignores the user actions.
 */
class DefaultBehavior extends Behavior {
	protected MainWin window;

	init(MainWin window) {
		init { window = window; }
	}

	void onProgramError(ProgramError error) : override {
		if (error as ThreadError) {
			showMessage(window, "Thread ${error.threadId} crashed", "Thread ${error.threadId} crashed:\n" + error.message);
		} else if (error as DataRaceError) {
			showMessage(window, "Data race found", "You found data races:\n" + error.message);
		} else if (error as DeadlockError) {
			showMessage(window, "Deadlock found", error.message);
		} else if (error as LivelockError) {
			showMessage(window, "Livelock found", error.message);
		} else if (error as MemoryLeakError) {
			showMessage(window, "Memory leak found", error.message);
		} else if (error as NoError) {
			// Nothing needed, we just don't want to show "program error".
		} else {
			showMessage(window, "Program error: " + error.type, error.message);
		}
	}

}


/**
 * Behavior for playing back a solution.
 *
 * We inherit from default behavior to get default error messages.
 */
class ReplayBehavior extends DefaultBehavior {
	private Str[] steps;
	private Nat pos;

	// Auto step currently active.
	private Bool autoStep;

	// Stop the auto step?
	private Bool stopAuto;

	// Allow cancelling.
	private Bool allowCancel;

	init(MainWin window, Str solution) {
		init(window) {}

		Char sep = ";".begin.v;
		Str:Iter last = solution.begin();
		do {
			Str:Iter next = solution.find(sep, last);
			if (last != next)
				steps << solution.substr(last, next);
		} while (next != solution.end) {
			last = next + 1;
		}

		updateConfig();
	}

	init(MainWin window, check:Action[] steps) {
		init(window) {
			allowCancel = true;
		}

		for (x in steps)
			this.steps << x.toS;

		updateConfig();
	}

	void onProgramError(ProgramError error) : override {
		stop();
		super:onProgramError(error);
	}

	void onRestart() : override {
		stop();
		pos = 0;
		updateConfig();
	}

	void onDispose() : override {
		stop();
	}

	ProblemPanel? panel() {
		ProblemPanel p("Showing the problem");
		p.button("Restart", &window.onRestart());
		p.button("Next step", &this.onStep());
		p.button("Play/Pause", &this.onAuto());
		if (allowCancel)
			p.button("Close", &window.resetBehavior());
		p;
	}

	private void onStep() {
		if (!autoStep)
			applyStep();
	}

	private void onAuto() {
		if (autoStep) {
			stop();
		} else {
			stopAuto = false;
			(spawn autoStepFn()).detach();
		}
	}

	private void applyStep() {
		if (pos >= steps.count) {
			(spawn showMessage(window, "End", "No more steps are available. Press \"Restart\" to see it again, or explore more on your own.")).detach();
			return;
		}

		// Must update the mark first, otherwise we might show the wrong thing.
		pos++;
		updateConfig();
		if (!window.applyAction(steps[pos - 1])) {
			// Something was running, restore.
			pos--;
			updateConfig();
		}
	}

	private void stop() {
		stopAuto = true;
		while (autoStep)
			yield();
	}

	// Update the mark.
	private void updateConfig() {
		if (pos < steps.count) {
			config.mark = steps[pos];
			config.allowStep = false;
		} else {
			config.mark = null;
			config.allowStep = true;
		}
	}

	// Function running in another UThread.
	// We're constantly monitoring the "main" thread. It is a bit wasteful, but OK.
	private void autoStepFn() {
		Moment next;

		autoStep = true;
		try {
			while (!stopAuto) {
				if (next < Moment()) {
					next += 1 s;

					applyStep();
				}

				if (pos >= steps.count)
					break;

				sleep(10 ms);
			}
		} catch (Exception e) {
			print("Internal error: ${e}");
		}
		autoStep = false;
	}

	Bool allowTrackMemory() : override { false; }
	Str? allowReload() : override {
		if (allowCancel) {
			window.resetBehavior();
			return null;
		} else {
			return "You can not reload problems while showing a problem. Open a new problem instead.";
		}
	}
	Str? allowSpawnThread() { "You can not spawn new threads now. Please open a new program first."; }
}