
On Wed, Jun 28, 2017 at 3:09 PM, Erik Bray <erik.m.bray@gmail.com> wrote:
On Wed, Jun 28, 2017 at 2:26 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 28 June 2017 at 21:40, Erik Bray <erik.m.bray@gmail.com> wrote:
My colleague's contention is that given
lock = threading.Lock()
this is simply *wrong*:
lock.acquire() try: do_something() finally: lock.release()
whereas this is okay:
with lock: do_something()
Technically both are slightly racy with respect to async signals (e.g. KeyboardInterrupt), but the with statement form is less exposed to the problem (since it does more of its work in single opcodes).
Nathaniel Smith posted a good write-up of the technical details to the issue tracker based on his work with trio: https://bugs.python.org/issue29988
Interesting; thanks for pointing this out. Part of me felt like this has to have come up before but my searching didn't bring this up somehow (and even then it's only a couple months old itself).
I didn't think about the possible race condition before WITH_CLEANUP_START, but obviously that's a possibility as well. Anyways since this is already acknowledged as a real bug I guess any further followup can happen on the issue tracker.
On second thought, maybe there is a case to made w.r.t. making a documentation change about the semantics of the `with` statement: The old-style syntax cannot make any guarantees about atomicity w.r.t. async events. That is, there's no way syntactically in Python to declare that no exception will be raised between "lock.acquire()" and the setup of the "try/finally" blocks. However, if issue-29988 were *fixed* somehow (and I'm not convinced it can't be fixed in the limited case of `with` statements) then there really would be a major semantic difference of the `with` statement in that it does support this invariant. Then the question is whether that difference be made a requirement of the language (probably too onerous a requirement?), or just a feature of CPython (which should still be documented one way or the other IMO). Erik