[Picolibc] Using atomics for fgetc/ungetc in tinystdio

Keith Packard keithp at keithp.com
Mon Jul 6 13:42:01 PDT 2020


Tinystdio is fully re-entrant for output as it doesn't have any internal
mutable state in that path. On the other hand, input has to support
ungetc, which requires some state.

The current tinystdio code has a one-character 'unget' buffer and a flag
indicating that this buffer is full. Making that re-entrant would
require locking, which means a bunch of OS support that would be nice to
avoid.

Because of padding requirements on 32-bit hosts, there are two unused
bytes in the FILE structure. So, I changed the design to use a 16-bit
value for the unget buffer and then define it as full when the value is
non-zero. OR'ing in a bit in the high byte when storing the unget value
means we can continue to use zero as the 'empty' flag. This uses the
same amount of RAM and shrinks the implementation of fgets and ungetc a
bit.

With this single 16-bit value, we can use atomics to implement fgetc and
ungetc:

    int unget(int c, FILE *file)
    {
        ...
        if (!atomic_compare_exchange_weak(&file->unget, &zero, c | 0x100))
            return EOF;
        return (unsigned char) c;
    }


    int fgetc(FILE *file)
    {
        uint16_t unget;
        unget = atomic_exchange(&file->unget, &zero);
        if (unget)
            return unget;
        ...
    }

The problem is that few of our target platforms actually support atomics
directly. RISC-V, ARM and Xtensa all use a helper function for these,
__atomic_compare_exchange_2. That function requires OS support, which
may not be present in all environments.

So, what I'm thinking of doing is to add non-atomic implementations of
the two required interfaces and then to allow applications using
picolibc to override those with real versions if they like:


    bool
    __libc_atomic_compare_exchange_2(uint16_t *p, uint16_t d, uint16_t v);
    
    	If *p == d, Then *p = v; return true;
    	Else return false
    
    uint16_t
    __libc_atomic_exchange_2(uint16_t *p, uint16_t v);
    
    	o = *p;
    	*p = v;
    	return o;

Suggestions for better names are welcome, along with other comments and
tests on different platforms. This code is all on the 'atomic-stdio' branch.

-- 
-keith
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://keithp.com/pipermail/picolibc/attachments/20200706/761bd7ae/attachment.sig>


More information about the Picolibc mailing list