Live data from Hacker News

Async and Finaliser Deadlocks

tratt.net

11–20 of 22 posts

Re: Async and Finaliser Deadlocks

#11
post #5

Earlier quoted context omitted.

I think it says if your async code holds locks you’re gonna have a bad time. Async and optimistic locks probably should go hand in hand. I would think finalizers and async code magnify problems that are already there.

If you use a single-threaded executor then you don't need locks in your async code. Well, you might use external locks, but not thread synchronization primitives. When I write async code I use a single-threaded multi-process pattern. Look ma'! No locks! Well, that's not very fair. The best async code I've written was embarrassingly parallel, no-sync-needed, read-only stuff. If I was writing an RDBMS I would very much…

You do have to be careful that all of your data updates are transitive, or you have to hold all of the updates until you can apply them in sequential order. One of my favorite tricks there is to use a throttling or limiting library, start all of the tasks, and then run a for loop to await each answer in order. You still have front-of-line issues but you can make as much forward progress as can be made.

Re: Async and Finaliser Deadlocks

#13
post #5

Earlier quoted context omitted.

I think it says if your async code holds locks you’re gonna have a bad time. Async and optimistic locks probably should go hand in hand. I would think finalizers and async code magnify problems that are already there.

If you use a single-threaded executor then you don't need locks in your async code. Well, you might use external locks, but not thread synchronization primitives. When I write async code I use a single-threaded multi-process pattern. Look ma'! No locks! Well, that's not very fair. The best async code I've written was embarrassingly parallel, no-sync-needed, read-only stuff. If I was writing an RDBMS I would very much…

that isn’t the panacea you describe it to be. you just happen to write a lot of code where writing it that way doesn’t result in consistency problems.

Re: Async and Finaliser Deadlocks

#14
A __del__ that does any kind of real work is asking for trouble. Use it to print a diagnostic reminding you to call .close() or .join() or use a with statement, and nothing else. For example:

    def close(self):
        self._closed = True
        self.do_interesting_finalisation_stuff()
    def __del__(self):
        if not self._closed:
            print("Programming error! Forgot to .close()", self)
If you do anything the slightest bit more interesting than that in your __del__, then you are likely to regret it.

Every time I've written a __del__ that did more, it has been trouble and I've ended up whittling it down to a simple diagnostic. With one notable exception: A __del__ that put a termination notification into a queue.Queue which a different thread was listening to. That one worked great: If the other thread was still alive and listening, then it would get the message. If not, then the message would just get garbage-collected with the Queue, but message would be redundant anyway, so that would be fine.

Re: Async and Finaliser Deadlocks

#16

A __del__ that does any kind of real work is asking for trouble. Use it to print a diagnostic reminding you to call .close() or .join() or use a with statement, and nothing else. For example: def close(self): self._closed = True self.do_interesting_finalisation_stuff() def __del__(self): if not self._closed: print("Programming error! Forgot to .close()", self) If you do anything the slightest bit more interesting tha…

Yep, a __del__ in the redis client code caused almost random deadlocks at my job for several years. Manual intervention was required to restart stuck Celery jobs. Took me about 2-3 weeks to find the culprit (had to deploy python interpreter compiled with debug info into production, wait for deadlock to happen again, attach with gdb and find where it happens). One of the most difficult production issues I had to solve in my life (because it happened randomly and it was impossible to even remotely guess what is causing it).

Re: Async and Finaliser Deadlocks

#17
Before drawing any „higher order conclusion“ …

Did anyone assure that this code is using a recursive mutex???

Because, well, lol, a second lock on a non-recursive mutex would look like what is reported here.

KISS and gn8

Re: Async and Finaliser Deadlocks

#18
post #17

Before drawing any „higher order conclusion“ … Did anyone assure that this code is using a recursive mutex??? Because, well, lol, a second lock on a non-recursive mutex would look like what is reported here. KISS and gn8

But who am I anyway? Everyone is free to do what they want.

Everyone is free to overcompensate, remunerate, juxtapose, aggravate, frustrate, vandalize, …

Re: Async and Finaliser Deadlocks

#19

A __del__ that does any kind of real work is asking for trouble. Use it to print a diagnostic reminding you to call .close() or .join() or use a with statement, and nothing else. For example: def close(self): self._closed = True self.do_interesting_finalisation_stuff() def __del__(self): if not self._closed: print("Programming error! Forgot to .close()", self) If you do anything the slightest bit more interesting tha…

One helpful rule is: if you use `__del__`, it should be on a separate class which doesn't contain any methods or data except the native handle.

You can't call inappropriate functions if you don't have any way to reach them!

Re: Async and Finaliser Deadlocks

#20
The article missunderstands the futurelock problem. It assumes that the mutex is a "normal" blocking mutex and that the future blocks. This is not the case: The future does cooperate and returns the control back to the `select!` call. The problem is that the `select!` call does not have access to the future that holds the lock, so it cannot make progress.
Post reply on HN