4 ms·
Externally canceling a task at a location other than a known stopping point is used as an example here, but in most environments doing this is a known-bad desig
by kevingadd 3y ago
Externally canceling a task at a location other than a known stopping point is used as an example here, but in most environments doing this is a known-bad design decision, since the terminated thread-or-task might have been holding a mutex, and now that mutex is stuck closed forever. .NET has been closing the door on this primitive for years (https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/thread-abort-obsolete https://learn.microsoft.com/en-us/dotnet/core/compatibility/...)
- verdagon 3y agoIt makes me wonder if there are any language constructs that can make this a reasonable feature. One idea I've been tossing around is having the ability to roll back any changes to mutex-guarded data if an exception drops a mutex guard. It should be possible with the right language constructs and bookkeeping. Perhaps there are other mechanisms out there too. I feel like the ability to destroy another thread isnt inherently bad, just... bad with today's languages. Just a feeling though.
- cratermoon 3y agoThe Go folks will repeat the aphorism, "Do not communicate by sharing memory; instead, share memory by communicating."[1]. The author directly violates the intention of the designers of Go by talking about shared file handles and other data structures, i.e. memory. The word "channel" doesn't appear a single time in the article, even though goroutines without channels to communicate with each other should never be sharing data. Channels are the synchronization primitive in Go. 1. https://go.dev/blog/codelab-share https://go.dev/blog/codelab-share
- bb88 3y agoA file descriptor is nothing but a pointer. It's really just an int. Usually maintained by the OS. Instead of sharing the file descriptor across a goroutine (bro, like WTF), let one go routine manage the file descriptor itself.
- jayd16 3y agoChannels would help but the author is saying they're naively shared through a closure and that's a problem, no?
- EdiX 3y agoIt seems to me that what you are describing is usually called software transactional memory. It has its own set of problems (bad performance with high granularity and livelocks, although you can probably avoid livelocks if you only care about using it for abnormal terminations) but it doesn't fully resolve the problem here. Yes, not leaving memory in an invalid state goes a long way but any form of IPC is potentially problematic: consider what happens if the thread is writing to a socket borrowed from a pool, or to a disk file. Not impossible to deal with but everything you do needs to be designed with cancellation-at-any-point in mind, it doesn't seem worth it to me.
- jayd16 3y agoSQL comes to mind.
- jayd16 3y agoIt's not so much that exceptions are bad though. Cancellation tokens throwing is not obsolete. Mutex and such can be cleaned up in finally blocks in .NET. It's just that being able to place the exception in a predictable place has benefits.
- bb88 3y agoPython/C++ has finally: blocks as well. They're not nearly as good as the `with` statement (or context handler) in python.
- mrkeen 3y agoHaskell does this. Threads can cancel other threads. ResourceT and bracket are two ways for a thread to register clean-up code in the event that they are cancelled.
- djha-skin 3y agoThat mutex gets cleaned up because thread interruption is implemented as an exception, so the `finally` block would be able to take care of the mutex.
- kevingadd 3y agothis is only true if the mutex is guarded by a finally block. your thread could be calling anything, including user-space code written in C that holds a mutex.