This file contains an early draft of an explanation of how to use the incoherence barriers; but in the process of writing it, I realized that the only kind of barrier that could be used in any but the most restricted circumstances was, in fact, identical to the kind of barrier that would work in almost every circumstance... So the long, involved explanation became unnecessary. But I think it would eventually be nice to have a file which helps people think about about reordering by compilers and memory hierarchy reordering in SMP systems, and this explanation might be useful as part of that. Thus we keep it around. /* In order to operate on any tagged_t values directly, C code (local to the Minor implementation) needs to first mark itself "incoherent", meaning that garbage collection must not take place, then operate on the values, and then mark itself "coherent" again. If some other thread requests a collection while we were incoherent, it will set our "collection_waiting" flag, which we must check each time we become coherent again. The functions on this page take care of entering and leaving coherent sections. To use these functions correctly, you must tell the C compiler not to rearrange your code so that the tagged_t manipulation takes place outside the incoherence barriers. Here's what I mean: (This basically amounts to an explanation of what 'volatile' really means; if you understand 'volatile' already, you can skim this.) Normally, C compilers are free to reorder statements --- including statements that modify variables or data structures --- in any way that doesn't affect the behavior of the program *in the absence of signal handlers*. For example, suppose you have two global 'int' variables x and y, and you write: x = 3; y = 4; And further suppose that both x and y will always be zero when we begin executing these statements. If these statements appear in your program right next to each other, as they do here, the compiler has the freedom to (for example) exchange them, so that y becomes 4 before x becomes 3. Since neither statement uses the value assigned by the other, swapping them can't affect the behavior of the program. Since the reordering is invisible, it's a permitted optimization. However, if your program includes a signal handler that reads x and y, then rearranging the assignments could, in fact, affect the behavior of the program. If the statements are executed as written, the signal handler will never see y == 4 unless x == 3. But if the compiler exchanges the two assignments, then the signal handler might see x == 3 while y is still zero. One could prohibit the compiler from rearranging the program in a way that signal handlers could see. Since signal handlers can only see variables in memory, I think this amounts to simply prohibiting the compiler from rearranging stores. But the committees writing the C standards felt that this was too restrictive, so compilers are permitted to reorder statements as they like. They can also delete code entirely: x = 3; // this could be optimized away x = 4; And so on. However, these rules applied everywhere make signal handlers really hard to use: you don't want to make assumptions about how clever your compiler was, so it's extremely difficult to be sure what values the variables your signal handler uses will have, and where in the main-line code they will be visible. For example, suppose you have a loop like this: interrupted = 0; while (! interrupted) do some work; and you have a signal handler that sets the 'interrupted' variable to 1 when the user hits C-c. Since there are no assignments to 'interrupted' in the code, the compiler is free to transform this code into: interrupted = 0; while (1) do some work; And things get more subtle. So, in the case at hand, if you write code like this: mn__thread_self->incoherent++; work with some tagged_t values directly; mn__thread_self->incoherent--; and the middle statement doesn't actually refer to the thread's 'incoherent' flag, then the compiler is free to transform this into: work with some tagged_t values directly; After all, the increment and the decrement cancel each other out, and nobody sees their effect. And even if the middle statement did use the incoherent flag, the compiler could always say: work with some tagged_t (mn__thread_self->incoherent + 1) values directly; and eliminate the increment and decrement. Clearly what we want is a way to tighten up the rules in those places that need it, while still letting the compiler rearrange code as it pleases elsewhere. So, the modified rule is that, where a program accesses or modifies objects whose type has the 'volatile' qualifier, the compiler may only reorder and rearrange those operations within the nearest enclosing sequence points (statement boundaries; function calls; &&, ||, and comma operators; etc.). So, if we declare mn__thread_self->incoherent to be volatile, does that help? mn__thread_self->incoherent++; work with some tagged_t values directly; mn__thread_self->incoherent--; The compiler is no longer allowed to eliminate the increments and decrements, but it is still allowed to rearrange other code relative to them, like so: mn__thread_self->incoherent++; mn__thread_self->incoherent--; work with some tagged_t values directly; In some cases, the work will involve accessing the compiler may only reorder and delete operations on objects whose type has the 'volatile' qualifier within sequence points. (Things like function calls, statement boundaries, the comma operator, and the && and || operators all introduce sequence points.) that operations on objects of that type may only be reordered and within sequence points, but (What makes things vastly simpler is that we only need to coordinate with our own signal handler, not with other threads. Only our signal handler can pass our state off to the collecting thread, and the nasty synchronization happens there.) */