Epoch calls are stored in a 4-bucket hash table. The 4-bucket hash table allows for calls to be stored for three epochs: the current epoch and two previous ones. The comments at the beginning of ck_epoch.c explain why this is necessary.
When there are active threads, ck_epoch_poll_deferred() current runs the epoch calls for the current global epoch + 1. Because of modulo arithmetic, this is equivalent to running the calls for epoch - 3. However, this means that ck_epoch_poll_deferred() is waiting unnecessarily long to run epoch calls.
Further, there could be races in incrementing the global epoch. Imagine all active threads have observed epoch n. CPU 0 sees this. It increments the global epoch to (n + 1). It runs the epoch calls for (n - 3). Now, CPU 1 checks. It sees that there are active threads which have not yet observed the new global epoch (n + 1). In this case, ck_epoch_poll_deferred() will return without running any epochs. In the worst case (CPU 1 continually losing the race), these epoch calls could be deferred indefinitely.
To fix this, always run any epoch calls for global epoch - 2. Further, if all active threads have observed the global epoch, run epoch calls for global epoch - 1.
The global epoch is only incremented when all active threads have observed it. Therefore, all active threads must always have observed global epoch - 1 or the current global epoch. Accordingly, it is safe to always run epoch calls for global epoch - 2.
Further, if all active threads have observed the global epoch, it is safe to run epoch calls for global epoch - 1.
While here, add a compile-time check to validate assumptions about the value of CK_EPOCH_LENGTH.