int server_rwlock_r2wlock(server_rwlock_t *rwlock);
This function attempts to atomically convert a held "read lock" to a "write lock".
Returns 0 on error, 1 on success, 2 on success but race lost
The caller _must_ hold the read-lock before calling this, otherwise bad things will happen.
This function will block until "write lock" status can be acquired (all other readers have gone away), and for the duration of the conversion all other "write lock" attempts will be blocked making this atomic.
Use this function for elevating your lock level after you have done your reading and determined you must modify. If you must ensure that the state of what is protected cannot be modified between your lengthy reading process and now needed write process, this function is how to do it.
There is however still an opportunity for modifications to ocurr between your server_rwlock_rlock() and successful return of server_rwlock_r2wlock(). This is due to the potential for multiple threads holding the "read lock" entering server_rwlock_r2wlock() simultaneously. One will return without contention for the transcending state, others will be blocked until the winner gives up the write lock. This means the others cannot assume the protected data has not changed.
As a result, when you use server_rwlock_r2wlock() you must test the return value to see if it is 1 or 2. When it returns 2 the "race was lost" and though you now have acquired the "write lock" someone else had it somewhere during your transition from "read lock" to "write lock".
This means that in the worse-case scenario you must deal with it as if you were using posix reader-writer threads, and as in the example above redo your search. However, when the returned value is 1, you were uncontended and enjoy improved efficiency... which is presumably the common case.
2007-12-06