Remix.run Logo
pizlonator 3 days ago

At first I wondered if musl does it better, so I checked, and the version I have disables cancellation in the guts of `getaddrinfo`.

I've always thought APIs like `pthread_cancel` are too nasty to use. Glad to see well documented evidence of my crank opinion

pengaru 3 days ago | parent [-]

The asynchronous cancellation in particular is difficult to use correctly, but is also one of the most useful aspects of the api in situations where appropriate.

Imagine cpu-bound worker threads that do nothing but consume work via condition variables and spend long periods of time in hot compute-only loops working on said work... Instead of adding a conditional in the compute you're probably not interested in slowing down at all, you turn on async cancellation and pthread_cancel() the workers when you need to interrupt what's going on.

But it's worth noting pthread_cancel() is also rarely supported anywhere outside first-class pthreads-capable systems like modern linux. So if you have any intention of running elsewhere, forget about it. Thread cancellation support in general is actually somewhat rare IME.

epcoa 3 days ago | parent | next [-]

> But it's worth noting pthread_cancel() is also rarely supported anywhere outside first-class pthreads-capable systems like modern linux

Having written some of the implementation for a non x86 commercial Unix well over 30 years ago now (yeah, I know), pthread_cancel is not that rare. A carve out like “modern linux” is io_uring or even inotify and epoll. AIX and HP-UX, fuck even OSF/1 had pthread_cancel.

Windows has TerminateThread. Most RTOS have some kind of thread level task killing interface.

While they have different semantics than pthread_cancel, that doesn’t really affect the example you’re giving - they can all be used for the “cpu-bound worker”

lll-o-lll 3 days ago | parent | next [-]

I’m not familiar with pthread_cancel, but I am with TerminateThread. It’s not something that can be used safely: ever. Raymond Chen has written a few times about it, including the history.

> Originally, there was no Terminate­Thread function. The original designers felt strongly that no such function should exist because there was no safe way to terminate a thread, and there’s no point having a function that cannot be called safely. But people screamed that they needed the Terminate­Thread function, even though it wasn’t safe, so the operating system designers caved and added the function because people demanded it. Of course, those people who insisted that they needed Terminate­Thread now regret having been given it.

pizlonator 3 days ago | parent | prev [-]

pthreads has `pthread_kill`, which is like `TerminateThread`.

`pthread_cancel` is different

hedora 3 days ago | parent | prev | next [-]

Assuming it’s OK to take 10msec to cancel, that conditional can be a well-predicted branch and a read of a cached memory address every 10msec. On a 1GHz processor, that’s a one cycle instruction that’s run every 10 million cycles. Unless the conditional or the cached read is the straw that breaks the back of the cache, there’s no way it’ll be measurable.

achierius 3 days ago | parent [-]

How do you insert a branch "every 10ms" without some sort of hardware-provided interrupt?

If your code is running in a hot loop, you would have to insert that branch into the hot loop (even well-predicted branches add a few cycles, and can do things like break up decode groups) or have the hot loop bail out every once in a while to go execute the branch and code, which would mean tiling your interior hot loop and thus adding probably significant overhead that way.

Also, you say "cached memory address" but I can almost guarantee that unless you're doing that load a lot more frequently than once every 10 milliseconds the inner loop is going to knock that address out of l1 and probably l2 by the time you get back around to it.

hedora 2 days ago | parent [-]

You put the check outside the innermost loop. Put it up one or two loops instead, and reason that the check runs frequently enough and also infrequently enough.

Also, don’t you have to hit a pthread cancellation point for pthread_cancel to take effect?

Those are way more expensive than a branch, but if you want the exact behavior, you could do “if (done) { break; } else { pthread_??? }”

pengaru 2 days ago | parent [-]

> Also, don’t you have to hit a pthread cancellation point for pthread_cancel to take effect?

No, the whole point here is async cancellation - you don't test for it and you don't enter a cancellation point.

Excerpt from pthread_setcancelstate(3):

  >    Asynchronous cancelability
  >        Setting the cancelability  type  to  PTHREAD_CANCEL_ASYNCHRONOUS  is  rarely  useful.
  >        Since  the  thread  could be canceled at any time, it cannot safely reserve resources
  >        (e.g., allocating memory with malloc(3)), acquire mutexes, semaphores, or locks,  and
  >        so  on.   Reserving resources is unsafe because the application has no way of knowing
  >        what the state of these resources is when the thread is canceled; that is, did cance-
  >        lation occur before the resources were reserved, while they were reserved,  or  after
  >        they  were  released?   Furthermore,  some internal data structures (e.g., the linked
  >        list of free blocks managed by the malloc(3) family of functions) may be left  in  an
  >        inconsistent  state if cancelation occurs in the middle of the function call.  Conse-
  >        quently, clean-up handlers cease to be useful.
  >
  >        Functions that can be safely asynchronously  canceled  are  called  async-cancel-safe
  >        functions.   POSIX.1-2001  and  POSIX.1-2008  require  only  that  pthread_cancel(3),
  >        pthread_setcancelstate(), and pthread_setcanceltype() be async-cancel-safe.  In  gen-
  >        eral,  other  library  functions can't be safely called from an asynchronously cance-
  >        lable thread.
  >
  >        One of the few circumstances in which asynchronous cancelability  is  useful  is  for
  >        cancelation of a thread that is in a pure compute-bound loop.
sthustfo 3 days ago | parent | prev [-]

asynchronous cancellation (when compared to deferred) is only recommended in scenarios where the thread does not share any data, semaphore or conditional variables with other threads. The target thread tends to cleanup any data within the thread cleanup handlers via pthread_cleanup_pop(). If not, the entire application might end up going down. Async cancellation has a very narrow application scope imho.