From: Jorge on
Hello,

I have a forked process that enters a loop like this:

while (RUN) {
//do nothing but burn the CPU
}

it also has a handler set for SIGALRM that just flips RUN to 0.

Later on the parent sends it a kill(child, SIGALRM), and, the problem
is that the sent SIGALRM signal is never handled -I don't know why-
unless I put a sleep(0); inside the above loop.

Why is this so ?

Thanks in advance,
--
Jorge.
From: Rainer Weikusat on
Jorge <jorge(a)jorgechamorro.com> writes:
> I have a forked process that enters a loop like this:
>
> while (RUN) {
> //do nothing but burn the CPU
> }
>
> it also has a handler set for SIGALRM that just flips RUN to 0.
>
> Later on the parent sends it a kill(child, SIGALRM), and, the problem
> is that the sent SIGALRM signal is never handled -I don't know why-
> unless I put a sleep(0); inside the above loop.
>
> Why is this so ?

In absence of the code, one has to rely on guesswork and my guess
would be 'the code in the loop doesn't touch RUN' and you haven't
declared to be volatile, hence, the compiler transformed the actually
executed code into something working like the pseudo-code below:

if (RUN) while (1) burn_cpu();
From: Jorge on
On Feb 8, 4:55 pm, Rainer Weikusat <rweiku...(a)mssgmbh.com> wrote:
> Jorge <jo...(a)jorgechamorro.com> writes:
> > I have a forked process that enters a loop like this:
>
> > while (RUN) {
> >   //do nothing but burn the CPU
> > }
>
> > it also has a handler set for SIGALRM that just flips RUN to 0.
>
> > Later on the parent sends it a kill(child, SIGALRM), and, the problem
> > is that the sent SIGALRM signal is never handled -I don't know why-
> > unless I put a sleep(0); inside the above loop.
>
> > Why is this so ?
>
> In absence of the code, one has to rely on guesswork and my guess
> would be 'the code in the loop doesn't touch RUN' and you haven't
> declared to be volatile, hence, the compiler transformed the actually
> executed code into something working like the pseudo-code below:
>
>         if (RUN) while (1) burn_cpu();

Thanks. Here's the source. Removing the sleep(0) makes the loop an
infinite loop... What do you think ?

#define kMaxProcesos 8

int signalCtr= 0;
int RUN= 1;
pid_t children[kMaxProcesos];

void signalHandler (int status) {
signalCtr++;
}

void alarmHandler (int status) {
RUN= 0;
}

extern int main (int argc, char* argv[]) {

long processNr= 0;
int parentProc= getpid();

signal(SIGCHLD, signalHandler);
signal(SIGALRM, alarmHandler);

while (processNr < kMaxProcesos) {
if(children[processNr] = fork()) {
//parent
processNr++;
} else if (parentProc != getpid()) {
//child
fprintf(stdout, "PID (%i) %i: Hola !\r\n", processNr, getpid());
while (RUN) {
sleep(0); //****** COMENTAR ESTA LINEA
}
fprintf(stdout, "PID (%i) %i: Adiós !\r\n", processNr,
getpid());
return 0;
} else {
fprintf(stdout, "Pasa algo muy muy raro(%i)\r\n", processNr);
}
}

int n;
processNr= kMaxProcesos;
while (processNr--) {
n= signalCtr;
kill(children[processNr], SIGALRM);
while (n == signalCtr) {
sleep(0); //****** COMENTAR ESTA LINEA
}
}

fprintf(stdout, "BYE: %i fork()s, %i signals\r\n", kMaxProcesos,
signalCtr);
return 0;
}

Thanks in advance,
--
Jorge.
From: Rainer Weikusat on
Jorge <jorge(a)jorgechamorro.com> writes:
> On Feb 8, 4:55�pm, Rainer Weikusat <rweiku...(a)mssgmbh.com> wrote:
>> Jorge <jo...(a)jorgechamorro.com> writes:
>> > I have a forked process that enters a loop like this:
>>
>> > while (RUN) {
>> > � //do nothing but burn the CPU
>> > }
>>
>> > it also has a handler set for SIGALRM that just flips RUN to 0.
>>
>> > Later on the parent sends it a kill(child, SIGALRM), and, the problem
>> > is that the sent SIGALRM signal is never handled -I don't know why-
>> > unless I put a sleep(0); inside the above loop.
>>
>> > Why is this so ?
>>
>> In absence of the code, one has to rely on guesswork and my guess
>> would be 'the code in the loop doesn't touch RUN' and you haven't
>> declared to be volatile, hence, the compiler transformed the actually
>> executed code into something working like the pseudo-code below:
>>
>> � � � � if (RUN) while (1) burn_cpu();
>
> Thanks. Here's the source. Removing the sleep(0) makes the loop an
> infinite loop... What do you think ?

[please see original for code]

Exactly what I originally wrote. Accessing an object (as in while
(RUN);) is considered to be free of side effects in the C-norm and
because of this, a compiler may generate code with fewer accesses
(that is, load operations) than what would be needed for a 'naive'
translation if it can 'prove' that the value cannot have changed. Your
code with the sleep commented out (I am only going to show this for
the first loop) looks like this:

------------------
if(children[processNr] = fork()) {
//parent
processNr++;
} else if (parentProc != getpid()) {
//child
fprintf(stdout, "PID (%i) %i: Hola !\r\n", processNr, getpid());
while (RUN);
fprintf(stdout, "PID (%i) %i: Adi�s !\r\n", processNr, getpid());
return 0;
------------------

And when optimizing, gcc translates this to (x86)

---------------------
8048510: e8 ab fe ff ff call 80483c0 <fork(a)plt>
8048515: 85 c0 test %eax,%eax
8048517: 89 04 9d a0 98 04 08 mov %eax,0x80498a0(,%ebx,4)
804851e: 75 e8 jne 8048508 <main+0x48>
----------------------

fork, store the pid in the processNr array, compare fork return value
with zero, jump to 8048508 when not zero

-----------------------
8048520: e8 4b fe ff ff call 8048370 <getpid(a)plt>
8048525: 39 c6 cmp %eax,%esi
8048527: 0f 85 8b 00 00 00 jne 80485b8 <main+0xf8>
----------------------

That's the (mostly redundant) parentProc check. If running in the
child, jump to 80485b8:

----------------------
80485b8: e8 b3 fd ff ff call 8048370 <getpid(a)plt>
80485bd: 89 5c 24 08 mov %ebx,0x8(%esp)
80485c1: c7 44 24 04 e0 86 04 movl $0x80486e0,0x4(%esp)
80485c8: 08
80485c9: 89 44 24 0c mov %eax,0xc(%esp)
80485cd: a1 80 98 04 08 mov 0x8049880,%eax
80485d2: 89 04 24 mov %eax,(%esp)
80485d5: e8 d6 fd ff ff call 80483b0 <fprintf(a)plt>
-----------------------

That's the 'Hola!'-fprintf in the child.

-----------------------
80485da: a1 64 98 04 08 mov 0x8049864,%eax
80485df: 85 c0 test %eax,%eax
80485e1: 75 2f jne 8048612 <main+0x152>
-----------------------

Load value of RUN into eax, jmp to 8048612 if not zero.

-----------------------
8048612: eb fe jmp 8048612 <main+0x152>
----------------------

For obvious reasons, this instruction will execute forever :-).

As I already wrote: you need to declare both signalCtr and RUN to be
volatile to inform the compiler that it must reload the value because
loading it is supposed to be a necessary side effect. Assuming that
RUN has been declared as

volatile int RUN = 1;

the loop is translated as

------------------------
80485e0: a1 64 98 04 08 mov 0x8049864,%eax
80485e5: 85 c0 test %eax,%eax
80485e7: 75 f7 jne 80485e0 <main+0x120>
------------------------

Load value, test if non-zero, if non-zero, jump to load

The reason the loop works with the sleep(0) is presumably because
whatever code gets executed because of the call might have changed the
value of RUN and hence, the compiler generates code to check this.
From: Jorge on
On Feb 8, 6:58 pm, Rainer Weikusat <rweiku...(a)mssgmbh.com> wrote:
> (...)
> Load value of RUN into eax, jmp to 8048612 if not zero.
>
> -----------------------
>  8048612:       eb fe                   jmp    8048612 <main+0x152>
>  ----------------------
>
>  For obvious reasons, this instruction will execute forever :-).
>
>  As I already wrote: you need to declare both signalCtr and RUN to be
>  volatile to inform the compiler that it must reload the value because
>  loading it is supposed to be a necessary side effect. Assuming that
>  RUN has been declared as
>
>         volatile int RUN = 1;
>
> the loop is translated as
>
> ------------------------
>  80485e0:       a1 64 98 04 08          mov    0x8049864,%eax
>  80485e5:       85 c0                   test   %eax,%eax
>  80485e7:       75 f7                   jne    80485e0 <main+0x120>
> ------------------------
>
> Load value, test if non-zero, if non-zero, jump to load
>
> The reason the loop works with the sleep(0) is presumably because
> whatever code gets executed because of the call might have changed the
> value of RUN and hence, the compiler generates code to check this.

Hmm, yes, I see. This is of great help. Thank you very very much !
Bye,
--
Jorge.