Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #75878 > unrolled thread

Specifying `blocking` and `timeout` when acquiring lock as a context manager

Started bycool-RR <ram.rachum@gmail.com>
First post2014-08-08 04:51 -0700
Last post2014-08-09 08:57 +1000
Articles 9 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  Specifying `blocking` and `timeout` when acquiring lock as a context manager cool-RR <ram.rachum@gmail.com> - 2014-08-08 04:51 -0700
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager Ethan Furman <ethan@stoneleaf.us> - 2014-08-08 06:25 -0700
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager "Neil D. Cerutti" <neilc@norwich.edu> - 2014-08-08 12:05 -0400
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager Chris Angelico <rosuav@gmail.com> - 2014-08-09 02:16 +1000
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager "Neil D. Cerutti" <neilc@norwich.edu> - 2014-08-08 14:35 -0400
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager "Neil D. Cerutti" <neilc@norwich.edu> - 2014-08-08 14:57 -0400
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager Chris Kaynor <ckaynor@zindagigames.com> - 2014-08-08 12:07 -0700
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager Ian Kelly <ian.g.kelly@gmail.com> - 2014-08-08 13:21 -0600
    Re: Specifying `blocking` and `timeout` when acquiring lock as a context manager Chris Angelico <rosuav@gmail.com> - 2014-08-09 08:57 +1000

#75878 — Specifying `blocking` and `timeout` when acquiring lock as a context manager

Fromcool-RR <ram.rachum@gmail.com>
Date2014-08-08 04:51 -0700
SubjectSpecifying `blocking` and `timeout` when acquiring lock as a context manager
Message-ID<ff17939d-84cc-4f86-a5b9-9056d76dc9c0@googlegroups.com>
Hi all,

If I want to acquire a `threading.Lock` using the context manager protocol, is it possible to specify the `blocking` and `timeout` arguments that `acquire` would usually take?

Thanks,
Ram.

[toc] | [next] | [standalone]


#75881

FromEthan Furman <ethan@stoneleaf.us>
Date2014-08-08 06:25 -0700
Message-ID<mailman.12747.1407504346.18130.python-list@python.org>
In reply to#75878
On 08/08/2014 04:51 AM, cool-RR wrote:
>
> If I want to acquire a `threading.Lock` using the context manager protocol,
>  is it possible to specify the `blocking` and `timeout` arguments that
>  `acquire` would usually take?

Not that I know of, but why would you want to?  There's no built-in 'if' with a 'with' block -- how would your code know 
whether it ran or not?

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#75897

From"Neil D. Cerutti" <neilc@norwich.edu>
Date2014-08-08 12:05 -0400
Message-ID<mailman.12755.1407513979.18130.python-list@python.org>
In reply to#75878
On 8/8/2014 9:25 AM, Ethan Furman wrote:
> On 08/08/2014 04:51 AM, cool-RR wrote:
>>
>> If I want to acquire a `threading.Lock` using the context manager
>> protocol,
>>  is it possible to specify the `blocking` and `timeout` arguments that
>>  `acquire` would usually take?
>
> Not that I know of, but why would you want to?  There's no built-in 'if'
> with a 'with' block -- how would your code know whether it ran or not?

Perhaps defer release, a la a common Go pattern:

with contextlib.ExitStack() as stack:
     acquired = lock.acquire(blocking=False)
     if acquired:
         stack.callback(lock.release)
         do_stuff

-- 
Neil Cerutti

[toc] | [prev] | [next] | [standalone]


#75898

FromChris Angelico <rosuav@gmail.com>
Date2014-08-09 02:16 +1000
Message-ID<mailman.12756.1407514617.18130.python-list@python.org>
In reply to#75878
On Sat, Aug 9, 2014 at 2:05 AM, Neil D. Cerutti <neilc@norwich.edu> wrote:
> Perhaps defer release, a la a common Go pattern:
>
> with contextlib.ExitStack() as stack:
>     acquired = lock.acquire(blocking=False)
>     if acquired:
>         stack.callback(lock.release)
>         do_stuff

There's a race condition in that - an unexpected exception could
happen between those two. Are you able to set the callback to be a
"release if acquired" atomic operation?

ChrisA

[toc] | [prev] | [next] | [standalone]


#75901

From"Neil D. Cerutti" <neilc@norwich.edu>
Date2014-08-08 14:35 -0400
Message-ID<mailman.12758.1407522945.18130.python-list@python.org>
In reply to#75878
On 8/8/2014 12:16 PM, Chris Angelico wrote:
> On Sat, Aug 9, 2014 at 2:05 AM, Neil D. Cerutti <neilc@norwich.edu> wrote:
>> Perhaps defer release, a la a common Go pattern:
>>
>> with contextlib.ExitStack() as stack:
>>      acquired = lock.acquire(blocking=False)
>>      if acquired:
>>          stack.callback(lock.release)
>>          do_stuff
>
> There's a race condition in that - an unexpected exception could
> happen between those two. Are you able to set the callback to be a
> "release if acquired" atomic operation?

Doesn't any natural looking use of blocking=False suffer from the same 
race condition? What's the correct way to use it?

Here's another attempt at context managing:

@contextlib.contextmanager
def release_if_acquired(lock, blocking=True, timeout=-1):
     acquired = lock.acquire(blocking, timeout)
     if acquired:
         yield acquired
         lock.release()
     else:
         yield acquired

with release_if_acquired(lock, blocking=False) as acquired:
     if acquired:
         do_stuff

-- 
Neil Cerutti

[toc] | [prev] | [next] | [standalone]


#75903

From"Neil D. Cerutti" <neilc@norwich.edu>
Date2014-08-08 14:57 -0400
Message-ID<mailman.12759.1407524326.18130.python-list@python.org>
In reply to#75878
On 8/8/2014 2:35 PM, Neil D. Cerutti wrote:
> Here's another attempt at context managing:


> @contextlib.contextmanager
> def release_if_acquired(lock, blocking=True, timeout=-1):
>      acquired = lock.acquire(blocking, timeout)
>      if acquired:
>          yield acquired
>          lock.release()
>      else:
>          yield acquired

I should not have used a temporary.

@contextlib.contextmanager
def release_if_acquired(lock, blocking=True, timeout=-1):
      if lock.acquire(blocking, timeout)
          yield True
          lock.release()
      else:
          yield False

-- 
Neil Cerutti

[toc] | [prev] | [next] | [standalone]


#75904

FromChris Kaynor <ckaynor@zindagigames.com>
Date2014-08-08 12:07 -0700
Message-ID<mailman.12760.1407525364.18130.python-list@python.org>
In reply to#75878

[Multipart message — attachments visible in raw view] — view raw

On Fri, Aug 8, 2014 at 11:35 AM, Neil D. Cerutti <neilc@norwich.edu> wrote:

> On 8/8/2014 12:16 PM, Chris Angelico wrote:
>
>> On Sat, Aug 9, 2014 at 2:05 AM, Neil D. Cerutti <neilc@norwich.edu>
>> wrote:
>>
>>> Perhaps defer release, a la a common Go pattern:
>>>
>>> with contextlib.ExitStack() as stack:
>>>      acquired = lock.acquire(blocking=False)
>>>      if acquired:
>>>          stack.callback(lock.release)
>>>          do_stuff
>>>
>>
>> There's a race condition in that - an unexpected exception could
>> happen between those two. Are you able to set the callback to be a
>> "release if acquired" atomic operation?
>>
>
> Doesn't any natural looking use of blocking=False suffer from the same
> race condition? What's the correct way to use it?
>
> Here's another attempt at context managing:
>
> @contextlib.contextmanager
> def release_if_acquired(lock, blocking=True, timeout=-1):
>     acquired = lock.acquire(blocking, timeout)
>     if acquired:
>         yield acquired
>         lock.release()
>     else:
>         yield acquired
>

What I'd probably do is:
@contextlib.contextmanager
def release_if_acquired(lock, blocking=True, timeout=-1):
    acquired = lock.acquire(blocking, timeout)
    try:
        yield acquired
    finally:
        if acquired:
            lock.release()

However, there is still the chance that a interrupt signal (ctrl+c) could
prevent the lock from being released, but I think the only 100% solution
would be to write the code in C where it cannot be interrupted within
Python. The OS could still interrupt or kill the thread, but in that case,
I don't think there is anything you can do...


> with release_if_acquired(lock, blocking=False) as acquired:
>     if acquired:
>
>         do_stuff
>
>

[toc] | [prev] | [next] | [standalone]


#75905

FromIan Kelly <ian.g.kelly@gmail.com>
Date2014-08-08 13:21 -0600
Message-ID<mailman.12761.1407525749.18130.python-list@python.org>
In reply to#75878
On Fri, Aug 8, 2014 at 7:25 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
> On 08/08/2014 04:51 AM, cool-RR wrote:
>>
>>
>> If I want to acquire a `threading.Lock` using the context manager
>> protocol,
>>  is it possible to specify the `blocking` and `timeout` arguments that
>>  `acquire` would usually take?
>
>
> Not that I know of, but why would you want to?  There's no built-in 'if'
> with a 'with' block -- how would your code know whether it ran or not?

@contextmanager
def locking(lock, blocking=False, timeout=-1):
  try:
    yield lock.acquire(blocking, timeout)
  finally:
    lock.release()

with locking(lock, timeout=5) as acquired:
  if acquired:
    print('yay!')

[toc] | [prev] | [next] | [standalone]


#75913

FromChris Angelico <rosuav@gmail.com>
Date2014-08-09 08:57 +1000
Message-ID<mailman.12769.1407538648.18130.python-list@python.org>
In reply to#75878
On Sat, Aug 9, 2014 at 4:35 AM, Neil D. Cerutti <neilc@norwich.edu> wrote:
> Doesn't any natural looking use of blocking=False suffer from the same race
> condition? What's the correct way to use it?

Actually, I don't know. I try to avoid any form of thread locking
where possible, and I don't remember the last time I used
blocking=False at all. Generally, in those rare occasions when I want
to lock, I want to lock reliably, and it's around something so narrow
that the simple option will be the best. So you're quite probably
right.

ChrisA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web