[haiku-development] Re: correct way to acquire_sem_etc()

  • From: Ingo Weinhold <ingo_weinhold@xxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Thu, 30 Oct 2008 00:22:05 +0100

On 2008-10-29 at 23:23:33 [+0100], François Revol <revol@xxxxxxx> wrote:
> >
> > could someone help me with an acquire_sem_etc() problem? I am unsure
> > about
> > the B_CAN_INTERRUPT flag and handling B_INTERRUPTED as the error
> > code.
> 
> userland acquires are always interruptible.
> Usually you don't want to be interruptible when you implement locking
> primitives, to avoid screwing up the state.
> 
> > When exactly do I want acquire_sem_etc() to be interruptable, and who
> > can
> > interrupt me?
> 
> For things like read() hooks in drivers, when waiting for a buffer,
> it's desirable to be interruptible, so someone can actually hit CTRL-C
> in a terminal and get the desired behaviour.
> In general I think anything that waits for an event with a long timeout
> should be interruptible.
> 
> Anyone who can send unmasked signals to the current thread can
> interrupt.

The suspend_thread() + resume_thread() combo does also interrupt, so does 
debug_thread(). There might be other situations -- grep through the kernel 
code for thread_interrupt(), if you're interested.

> Sometimes, even in locking primitives, it's done as
> while (acquire_sem_etc(s, B_CAN_INTERRUPT, ...) == B_INTERRUPTED);
> 
> This might be to eat signals and avoid delaying them instead, though
> I'm not sure.
> I've seen that in the NFS addon code for ex.
> It can't be to let the thread handle the signal, as it won't be done
> until it actually exit from the syscall...

If that is done in kernel code, it's buggy. Signals are only handled right 
before leaving the kernel, that is this would become a busy loop.

> > As for the specific problem I am trying to fix: In the usb_hid
> > driver,
> > Michael and I use a USB interrupt transfer callback semaphore. In
> > other
> > words, the thread that does ioctl() on the driver schedules an USB
> > interrupt transfer, then it blocks on a semaphore which has been
> > created
> > like this:

If this is guaranteed to be done in a short time (I'm talking ms here), the 
acquire_sem_etc() probably doesn't need to be interruptable. If it can 
potentially take a long time (even if that's only in cases of hardware 
errors or the like), it really should be interruptable and the ioctl() 
should return immediately when that happens. One potential problem is that 
there's probably some kind of cleanup needed in such a case. Also the 
operation might still be performed successfully after all, so the caller 
might thus get out of sync with its view of the device.

A kind of middle-ground is the flag B_KILL_CAN_INTERRUPT. It works like 
B_CAN_INTERRUPT, iff the thread has received a kill signal. I.e. neither 
SIGWINCH or Ctrl-C wouldn't interrupt it, but "kill -9" or 
ProcessController Kill would. That doesn't change the need for cleanup (if 
any), but at least one doesn't need to worry about the caller getting a 
wrong picture of the situation, since it will never see userland again.

BTW, if you make the ioctl() interruptable you might want to consider 
making it automatically restartable.

CU, Ingo

Other related posts: