Execution of the Sleep Instruction and Shared Variables Between ISR and Main

The potential problem with the sleep instruction is that an ISR can have executed right before the execution of the sleep instruction. If that ISR wrote to a flag which then needs to be handled in the main loop, it will not be handled until the next interrupt occurs and causes the device to wake up. The following code shows an example where this can happen:
volatile uint8_t shared_flag;
ISR(perip_vector){
    uint8_t i_flags = PERIP_INTFLAGS;
    shared_flag = 0x01;
    PERIP_INTFLAGS = i_flags;
}

main(){
    while(1){
        cli();
        if(shared_flag == 0x01){
            shared_flag &= ~0x01;
            sei();
            handle_event_etc();
        }
        sei();
        sleep();
    }
}

In this case, the shared_flag is protected from being altered by the ISR while it is being checked and cleared. However, if the ISR writes to the flag after the first sei(), inside the if statement, the sleep instruction will be executed without the shared_flag being checked again. As such, the events that need to happen in main() will not happen until the next time an interrupt occurs, waking the device from sleep.

To handle all flags inside the main loop, use the following code:
main(){
    while(1){
        cli();
        if(shared_flag == 0){
            sei();
            sleep();
        }
        sei();
        //handle shared_flags and run application
    }
}
This code ensures that no shared flags between ISRs and the main loop go unhandled before going to sleep. It does so because any instruction executing after the sei() instruction will be allowed to execute before a jump into a pending interrupt. The shared_flag variable can be shared between multiple ISRs. Note that any read-modify-write of the shared_flag inside the main loop will have to be protected by cli() and sei() as shown in the code below:
cli();
shared_flag &= ~0x01;
sei();

This makes the read-modify-write an atomic action, that is, it cannot be interrupted by any ISRs. However, this can be avoided by using General Purpose Input Output (GPIOR) registers as shared variables between ISRs. Registers GPIOR0 to GPIOR3 allow the compiler to compile any single-bit modification to one line of assembly code that will execute in a single CPU cycle. In comparison, using a read-modify-write of a uint8_t variable will take three cycles, or five cycles when taking the cli() and sei() instructions into account.