"benign" data race: stop request flag, write once, read periodically,
{ edited by mod to shorten lines to ~70 characters. -mod }
I have a question about a common code pattern that is cooperative task
(or thread) cancellation. Here's a crude definition off the cuff:
a task is cancellable if the controller of the task and the cancellable
task have a shared boolean flag which is used to signal or notify that
the task is to be canceled. The controller code exists in a separate
thread. If the controller code wants the task to stop, it sets the stop
flag to true. It is not a requirement for the task to notice the
request right away, or ever. It is acceptable behavior for a stop
request to be issued before the task completes execution, and for the
task to report successful, non-stopped execution. The requirement is
merely that the task make a best-effort attempt to notice the stop
request in a timely manner and to stop, interrupt, and cancel its
execution when it notices a stop request.
Consider the following pseudo C++ code which is an example of this
pattern.
//----
#include <thread>
class MyTask
{
public:
MyTask() : stopFlag(false) {}
bool stopFlag;
void operator() ()
{
for (;;)
{
if (stopFlag)
return;
//do some (bounded) work, then check the stopFlag again
}
}
};
int main()
{
MyTask task;
std::thread mythread(task);
task.stopFlag = true;
mythread.join();
}
//----
It's just an example of basic userspace cooperative task / thread
cancellation.
I know this is a formal race condition according to the C++11 standard,
and I know that the simplest fix would be to change stopFlag to this:
std::atomic<bool> stopFlag;
I also remember some other dedicated standard type for just this
purpose or something.
I also know that I could change stopFlag to this:
std::atomic<bool> stopFlag;
and I could get away with making the load and store both
std::memory_order_relaxed.
My question concerns common implementations. It would be nice ammo to
convince my colleagues and friends that all race conditions are harmful,
and there is no such thing as a benign race condition. I am aware of
the excellent paper "How to miscompile programs with "benign" data
races" by Hans-J. Boehm. However, it doesn't cover this very simple
example, and it would be nice if someone had an example of plausible
code in this pattern plus a plausible implementation which would
produce actual undesired behavior.
I also have more than enough ammo to make the case that changing the
read and write into atomic std::memory_order_relaxed will pose
basically zero overhead and will solve any possible lingering doubts
about real-world real-implementation correctness.
Finally, I recall seeing a real academic paper which covered this very
specific example and actually gave plausible user code and a plausible
reason why the implementation would break even this. However, I did
not save the paper, and I do not remember enough to find this paper.
(I spent the last several hours trying.) I am posting this to ask if
anyone knows of this paper and can point me towards it. I would be
most grateful if someone could post a link or enough information to
find this paper. I hope the paper is not a figment of my imagination -
it might be.
Thanks for your time!
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]