// Timer IDs are implemented using a free-list; // there's a vector initialized with: // X[i] = i + 1 // and nextFreeTimerId starts with 1. // // Allocating a timer ID involves taking the ID from // X[nextFreeTimerId] // updating nextFreeTimerId to this value and returning the old value // // When the timer ID is allocated, its cell in the vector is unused (it's a // free list). As an added protection, we use the cell to store an invalid // (negative) value that we can later check for integrity. // // (continues below). int QAbstractEventDispatcherPrivate::allocateTimerId() { int timerId, newTimerId; int at, *b; do { timerId = nextFreeTimerId; //.loadAcquire(); // ### FIXME Proper memory ordering semantics // which bucket are we looking in? int which = timerId & TimerIdMask; int bucket = bucketOffset(which); at = bucketIndex(bucket, which); b = timerIds[bucket]; if (!b) { // allocate a new bucket b = allocateBucket(bucket); if (!timerIds[bucket].testAndSetRelease(0, b)) { // another thread won the race to allocate the bucket delete [] b; b = timerIds[bucket]; } } newTimerId = prepareNewValueWithSerialNumber(timerId, b[at]); } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); b[at] = -timerId; return timerId; }
int QAbstractEventDispatcherPrivate::allocateTimerId() { int timerId, newTimerId; do { timerId = nextFreeTimerId; // which bucket are we looking in? int which = timerId & 0x00ffffff; int bucket = bucketOffset(which); int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; if (!b) { // allocate a new bucket b = allocateBucket(bucket); if (!timerIds[bucket].testAndSetRelease(0, b)) { // another thread won the race to allocate the bucket delete [] b; b = timerIds[bucket]; } } newTimerId = b[at]; } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); return timerId; }
void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) { int which = timerId & 0x00ffffff; int bucket = bucketOffset(which); int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; int freeId, newTimerId; do { freeId = nextFreeTimerId; b[at] = freeId & 0x00ffffff; newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); }
// Releasing a timer ID requires putting the current ID back in the vector; // we do it by setting: // X[timerId] = nextFreeTimerId; // then we update nextFreeTimerId to the timer we've just released // // The extra code in allocateTimerId and releaseTimerId are ABA prevention // and bucket memory. The buckets are simply to make sure we allocate only // the necessary number of timers. See above. // // ABA prevention simply adds a value to 7 of the top 8 bits when resetting // nextFreeTimerId. void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) { int which = timerId & TimerIdMask; int bucket = bucketOffset(which); int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; Q_ASSERT_X(timerId == -b[at], "QAbstractEventDispatcher::releaseTimerId", "Internal error: timer ID not found"); int freeId, newTimerId; do { freeId = nextFreeTimerId;//.loadAcquire(); // ### FIXME Proper memory ordering semantics b[at] = freeId & TimerIdMask; newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); }