Search code examples
clinuxsignalsasync-safe

The usage of sig_atomic_t in linux signal mask function


I was recently studying the book named Advanced Linux Programming and I ran into this question: The book said you should use sig_atomic_t variable type to be sure that if you set a global flag or counter in a signal handler function, context switch does not occur between the arithmetic operations(i.e. ++) and saving those into a register.

My question is: What can happen if we don't use sig_atomic_t and just use another type and context switch occurs? I mean the program will just return and save it later for example. Can someone give me a scenario which it will make our code unstable or buggy?


Solution

  • The risk you run in the scenario you describe (read from memory to register, update register, write to memory and a context switch happens between any of those operations) is that you could lose an update made in the other context.

    For example:

    main context:
      read i (=10) from memory to register R1
      add 5 to R1
        <interrupt. Switch to interrupt context>
        read i (=10) from memory to register R1
        add 10 to R1
        write R1 to i in memory (i = 20)
        <end of interrupt. Back to main context>
      write R1 to i in memory (i = 15)
    

    As you can see, the update from the interrupt has been lost.

    An even bigger problem would occur if your type requires multiple operations to write it to memory and the interrupt occurs in the middle of a write operation.

    For example:

    main context:
      read first half of i (=10) from memory to register R1
      read second half of i (=10) from memory to register R2
      add 5 to R1/R2 pair
      write R1 to first half of i in memory
        <interrupt. Switch to interrupt context>
        read first half of i (= ??) from memory to register R1
        read second half of i (= ??) from memory to register R2
        add 10 to R1/R2 pair
        write R1 to first half of i in memory
        write R2 to second half of i in memory
        <end of interrupt. Back to main context>
      write R2 to second half of i in memory
    

    Here, there is no telling what value i will end up with.

    With sig_atomic_t, this second problem can't occur, because the type is guaranteed to use atomic read/write operations.