[Async-sig] "read-write" synchronization

Chris Jerdonek chris.jerdonek at gmail.com
Sun Jun 25 23:34:50 EDT 2017


So here's one approach I'm thinking about for implementing
readers-writer synchronization. Does this seem reasonable as a
starting point, or am I missing something much simpler?

I know there are various things you can prioritize for (readers vs.
writers, etc), but I'm less concerned about those for now.

The global state is--

* reader_count: an integer count of the active (reading) readers
* writer_lock: an asyncio Lock object
* no_readers_event: an asyncio Event object signaling no active readers
* no_writer_event: an asyncio Event object signaling no active writer

Untested pseudo-code for a writer--

    async with writer_lock:
        no_writer_event.clear()
        # Wait for the readers to finish.
        await no_readers_event.wait()
        # Do the write.
        await write()

    # Awaken waiting readers.
    no_writer_event.set()

Untested pseudo-code for a reader--

    while True:
        await no_writer_event.wait()
        # Check the writer_lock again in case a new writer has
        # started writing.
        if not writer_lock.locked():
            # Then we can do the read.
            break

    reader_count += 1
    if reader_count == 1:
        no_readers_event.clear()
    # Do the read.
    await read()
    reader_count -= 1
    if reader_count == 0:
        # Awaken any waiting writer.
        no_readers_event.set()

One thing I'm not clear about is when the writer_lock is released and
the no_writer_event set, are there any guarantees about what coroutine
will be awakened first -- a writer waiting on the lock or the readers
waiting on the no_writer_event?

Similarly, is there a way to avoid having to have readers check the
writer_lock again when a reader waiting on no_writer_event is
awakened?

--Chris


On Sun, Jun 25, 2017 at 3:27 PM, Chris Jerdonek
<chris.jerdonek at gmail.com> wrote:
> On Sun, Jun 25, 2017 at 3:09 PM, Nathaniel Smith <njs at pobox.com> wrote:
>> On Sun, Jun 25, 2017 at 2:13 PM, Chris Jerdonek
>> <chris.jerdonek at gmail.com> wrote:
>>> I'm using asyncio, and the synchronization primitives that asyncio
>>> exposes are relatively simple [1]. Have options for async read-write
>>> synchronization already been discussed in any detail?
>>
>> As a general comment: I used to think rwlocks were a simple extension
>> to regular locks, but it turns out there's actually this huge increase
>> in design complexity. Do you want your lock to be read-biased,
>> write-biased, task-fair, phase-fair? Can you acquire a write lock if
>> you already hold one (i.e., are write locks reentrant)? What about
>> acquiring a read lock if you already hold the write lock? Can you
>> atomically upgrade/downgrade a lock? This makes it much harder to come
>> up with a one-size-fits-all design suitable for adding to something
>> like the python stdlib.
>
> I agree. And my point about asyncio's primitives wasn't a criticism or
> request that more be added. I was asking more if there has been any
> discussion of general approaches and patterns that take advantage of
> the event loop's single thread, etc.
>
> Maybe what I'll do is briefly write up the approach I have in mind,
> and people can let me know if I'm on the right track. :)
>
> --Chris


More information about the Async-sig mailing list