[Python-Dev] Signals, threads, blocking C functions
Gustavo Carneiro
gjcarneiro at gmail.com
Wed Sep 13 15:17:03 CEST 2006
On 9/12/06, Adam Olsen <rhamph at gmail.com> wrote:
> On 9/12/06, Gustavo Carneiro <gjcarneiro at gmail.com> wrote:
> > On 9/12/06, Adam Olsen <rhamph at gmail.com> wrote:
> > > My previous mention of using a *single* flag may survive corruption
> > > simply because we can tolerate false positives. Signal handlers would
> > > write 0xFFFFFFFF, the poll loop would check if *any* bit is set. If
> > > so, write 0x0, read off the fd, then loop around and check it again.
> > > If the start of the read() acts as a write-barrier it SHOULD guarantee
> > > we don't miss any positive writes.
> >
> > Why write 0xFFFFFFFF? Why can't the variable be of a "volatile
> > char" type? Assuming sizeof(char) == 1, please don't tell me
> > architecture XPTO will write the value 4 bits at a time! :P
>
> Nope. It'll write 32 bits, then break that up into 8 bits :)
> Although, at the moment I can't fathom what harm that would cause...
Hmm... it means that to write those 8 bits the processor / compiler
may need to 1. read 32 bits from memory to a register, 2. modify 8
bits of the register, 3. write back those 32 bits. Shouldn't affect
our case, but maybe it's better to use sig_atomic_t in any case.
> For the record, all volatile does is prevent compiler reordering
> across sequence points.
It makes the compiler aware the value may change any time, outside
the current context/function, so it doesn't assume a constant value
and always re-reads it from memory instead of assuming a value from a
register is correct.
> > static volatile char signal_flag;
> > static int signal_pipe_r, signal_pipe_w;
> >
> > PyErr_CheckSignals()
> > {
> > if (signal_flag) {
> > char signum;
> > signal_flag = 0;
> > while (read(signal_pipe_r, &signum, 1) == 1)
> > process_signal(signum);
> > }
> > }
>
> I'd prefer this to be a "while (signal_flag)" instead, although it
> should technically work either way.
I guess we can use while instead of if.
>
> > static void
> > signal_handler(int signum)
> > {
> > char signum_c = signum;
> > signal_flag = 1;
> > write(signal_pipe_w, &signum_c, 1);
> > }
>
> This is wrong. PyErr_CheckSignals could check and clear signal_flag
> before you reach the write() call. "signal_flag = 1" should come
> after.
Yes, good catch.
I don't understand the memory barrier concern in your other email.
I know little on the subject, but from what I could find out memory
barriers are used to avoid reordering of multiple read and write
operations. However, in this case we have a single value at stake,
there's nothing to reorder.
Except perhaps that "signal_flag = 0" could be delayed... If it is
delayed until after the while (read (...)...) loop below we could get
in trouble. I see your point now... :|
But I think that a system call has to act as memory barrier,
forcefully, because the CPU has to jump into kernelspace, a completely
different context, it _has_ to flush pending memory operations sooner
or later.
Round two:
static volatile sig_atomic_t signal_flag;
static int signal_pipe_r, signal_pipe_w;
PyErr_CheckSignals()
{
while (signal_flag) {
char signum;
signal_flag = 0;
while (read(signal_pipe_r, &signum, 1) == 1)
process_signal(signum);
}
}
static void
signal_handler(int signum)
{
char signum_c = signum;
write(signal_pipe_w, &signum_c, 1);
signal_flag = 1;
}
--
Gustavo J. A. M. Carneiro
"The universe is always one step beyond logic."
More information about the Python-Dev
mailing list