OP2: Beating Dynamic Partial Order Reduction


The basic model-checking algorithm is trying to enumerate all possible global system states. There are, however, many irrelevant states that can be skipped while ensuring that we learn all relevant states. For example, any two independent events are worth exploring in one order only. DPOR is reducing the state-space by capturing the dependencies between different threads/processes during the runtime. The main weakness of DPOR thus lies in the “dependent events” definition. If all events depend on each other, DPOR will not be able to reduce the search space. Thus, our goal is to create a simple application which will have a lot of dependencies and (because we want to beat DPOR) these dependencies can be removed by domain-specific knowledge. One such example might be an application which is performing different tasks in parallel and it is accessing global statistics.
main() {
	read_all_tasks();
	for i = 1 to n {
		new thread(process(i))
	}
	join_threads()
	print(counter + “ tasks processed successfully”)
}

process(i) {
	run(tasks[i])
	if (!error) {
		lock(counter); counter = counter + 1; unlock(counter)
	}
}
DPOR will find dependencies between all tasks in our example. Thus the technique won’t be able to reduce the search space even if the running of the tasks itself is independent. This result of DPOR can be easily beaten by simple domain knowledge: additions are commutative and thus there is no point in checking different orderings of increments. But this is only ``enhancement’’ of DPOR technique. To really show another POR technique let us remove the “print” line from the main algorithm and do complex operations with the counter:
lock(counter); counter= counter*counter + i; unlock(counter)
If the calculation of the new value does not create any branches in the code and the resulting value is never used, we may argue that the value of the counter is irrelevant. Thus, even if there are many dependencies between accesses to this counter, it would be enough to explore only one ordering. My POR technique is thus “if the manipulations to the shared object cannot affect the execution (no branching) and the value is never stored to other variables/objects, it is sufficient to explore any one ordering of these accesses”.