2020/12/07

2020 Perl Advent Calendar - Day 7

<< First | < Prev | Next >

So far in this advent series we've been following the progression from the original posts from 2013. In the next few posts we'll take things in a slightly different order. There will be a few new things to see about async/await syntax, but before we move onto that I want to continue the subject from the past couple of days, and cover the remaining the situations in which asynchronous programming with async/await follows the same shape and style as regular synchronous programming.

The original day 15 post introduced the Future::Utils::repeat helper function, for taking a single block of future-returning code, and calling it repeatedly until some condition is satisfied. The resulting code does behave somewhat like a regular perl while loop, though the syntax notation of it looks nothing like it. Using async/await, conversely, this sort of behaviour can simply be written with a regular while loop. Recalling that an await expression can be placed anywhere inside an async sub, we can simply wait for an intermediate result inside the body of the loop, before continuing.

This example from a module that communicates with a microcontroller needs to wait for the chip to not be busy. It can simply await a read of the current status and look for the busy bit inside a while loop.

use Future::AsyncAwait;

async sub await_nvm_not_busy
{
    my $self = shift;
    
    my $timeout = 50;
    while(--$timeout) {
        (await $self->load_nvm_status) & NVMCTRL_STATUS_FBUSY
            or last;
        
        await Future::IO->sleep(0.01);
    }
}

In fact, we can even put the await expression inside the loop condition itself. A different chip communication module for a radio transmitter uses this to wait for the chip to enter transmit state.

use Future::AsyncAwait;

async sub start_tx
{
    my $self = shift;
    
    await $self->command(CMD_STX);
    1 until (await $self->read_marcstate) eq "TX";
}

In both of these cases, we have to put the await expression in parentheses because of operator precedence, but other than that the logic reads just as short and tidy as it would for synchronous code.

Day 16 of the original series introduced another form of the Future::Utils::repeat helper for iterating over a given list of items. As before, this isn't necessary when using async/await syntax because we can just put an await expression somewhere inside a foreach loop to achieve the same effect.

For example, this somewhat-simplified example from a module that communicates with memory chips uses a regular foreach loop to iterate over a range of addresses to read.

use feature 'signature';
use Future::AsyncAwait;

async sub read_eeprom(%opts)
{
    my $start = $opts{start} // 0;
    my $stop  = $opts{stop} // MAXADDR;
    
    my $bytes = "";
    
    foreach my $addr ($start .. $stop-1) {
        await $self->set_address($addr);
        $bytes .= await $self->read_byte();
    }
    
    return $bytes;
}

Now we have a good overview of what code shapes are identical with async/await synta we'll next take a look at some new abilities we can gain from the ability to run things concurrently. While we first saw a few glimpses of that back on Day 3 this time, there are still a number of new things left to see.

<< First | < Prev | Next >

No comments:

Post a Comment