I'd like a model of the golang RWMutex API semantics ( https://pkg.go.dev/sync#RWMutex ); in particular I want the blocking behavior for readers and writers.
Here's my model of the read write mutex:
typedef RWLock {
chan writeComplete = [0] of {bit};
chan allowWrite = [0] of {bit};
int readers;
bit writing;
int writeWaiters;
int readWaiters
}
/* RWLock actions */
inline acquire_read(lock) {
do
:: atomic {
if
:: lock.writing == 1 ->
lock.readWaiters++;
lock.writeComplete?0;
lock.readWaiters--;
break
:: else ->
lock.readers++;
break
fi
}
od
}
inline release_read(lock) {
atomic {
lock.readers--;
lock.readers == 0 ->
end: lock.writeComplete!0
}
}
inline acquire_write(lock) {
do
:: atomic {
if
:: lock.writing == 0 ->
lock.writing = 1;
break;
:: else ->
lock.writeWaiters++;
lock.allowWrite?0;
lock.writeWaiters--
fi
}
od
}
inline release_write(lock) {
atomic {
assert(lock.writing == 1);
lock.writing = 0
if
:: lock.writeWaiters > 0 ->
lock.allowWrite!0
:: else ->
skip
fi
if
:: lock.readWaiters > 0 ->
lock.writeComplete!0;
:: else ->
skip
fi
}
}
I'm not totally sure if this is correct. Ideally answer to my question would be in one of these categories:
- assure me that my model is correct
- tell me it's wrong and tell me how to fix it
- you don't know if it's correct but you can offer some guidance on how I can effectively test this using a promela model
So far I have a naive model that uses it like this:
( complete example here --> https://gist.github.com/david415/05a5e7ed332e90bd7fb78b1f8f0c72cb )
int counter = 0;
proctype Writer(RWLock lock) {
acquire_write(lock);
counter = counter + 1;
printf("Writer incremented counter to %d\n", counter);
end: release_write(lock);
}
proctype Reader(RWLock lock) {
int localCounter;
acquire_read(lock);
localCounter = counter;
printf("Reader read counter: %d\n", localCounter);
end: release_read(lock);
}
init {
RWLock myLock;
myLock.readers = 0;
myLock.writing = 0;
myLock.writeWaiters = 0;
myLock.readWaiters = 0
run Writer(myLock);
run Reader(myLock)
end: skip
}
Disclaimer: though my PhD was in Formal Methods, it has been 15 years since I did any models, and I never user Promela.
Here are my notes.
acquire_writeshould wait until all readers release the lock. You checklock.writing == 0, but what aboutlock.readers > 0?Try the following scenario:
As far as can see,
Writerproceeds, while the spec requires it to block.acquire_readdoesn't catch the following requirements:The model
acquire_readshould wait not only whenlock.writing == 1, but whenlock.writeWaiters > 0as well.Try the following scenario:
According to the spec, it should block. According to your model, it proceeds with
lock.readers++, doesn't it?The spec requires:
I can't find how the model formalizes it.
Try the following scenario: