#define _POSIX_C_SOURCE 200809L #include /* close, write */ #include /* socketpair, recv */ #include /* memset */ #include #include /* V tomto příkladu budete opět programovat trojici podprogramů: * * 1. ‹meter_start› nastartuje asynchronní počítání bajtů, * 2. ‹meter_read› přečte aktuální stav počítadla, * 3. ‹meter_cleanup› vyčká na konec přenosu, uvolní zdroje a vrátí * finální hodnotu počítadla. */ /* Oproti první přípravě tohoto týdne bude přenos dat probíhat mezi * dvěma popisovači obousměrně. Podprogram ‹meter_start› bude mít 3 * parametry: * * 1. dvojici popisovačů ‹fd_1› a ‹fd_2› připojených proudových * socketů, mezi kterými bude obousměrně kopírovat (přeposílat) * data, * 2. ‹max_delay› který určuje, jaký může být nejvýše rozdíl mezi * ustálenou hodnotou ‹meter_read› a počtem bajtů skutečně * zapsaných (odeslaných) do socketů ‹fd_1› a ‹fd_2›, a zároveň * o kolik bajtů může být opožděný přenos mezi ‹fd_1› a ‹fd_2›. * * Podprogram ‹meter_start› vrátí volajícímu ukazatel ‹handle›, * který volající později předá podprogramu ‹meter_cleanup›, * případně ‹meter_read›. Samotné přeposílání dat bude provádět * asynchronně. * * Při uzavření spojení na libovolném z popisovačů ‹fd_1› nebo * ‹fd_2› uzavře i zbývající spojení. Můžete předpokládat, že * zápisy do ‹fd_1› a ‹fd_2› budou i v blokujícím režimu provedeny * obratem (nehrozí tedy uváznutí při zápisu, ani hladovění). */ void *meter_start( int fd_1, int fd_2, int max_delay ); /* Podprogram ‹meter_read› přečte aktuální hodnotu počítadla. * Neprobíhají-li souběžně žádné zápisy a celkový počet skutečně * zapsaných bajtů je ⟦n⟧, musí se výsledek opakovaného volání * ‹meter_read› ustálit na hodnotě ⟦k⟧, ⟦n - 2d ≤ k ≤ n⟧, kde ⟦d⟧ je * parametr ‹max_delay›, který byl předán podprogramu ‹meter_start›. * */ int meter_read( void *handle ); /* Podprogram ‹meter_cleanup› obdrží ukazatel ‹handle›, který byl * vrácen podprogramem ‹meter_start›, a uvolní veškeré zdroje s ním * spojené. Návratová hodnota ≥ 0 zaručuje, že všechna data byla * úspěšně přeposlána a zároveň určuje, kolik bajtů bylo přeneseno. * Jinak je výsledkem -1. */ int meter_cleanup( void *handle ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* sched_yield */ #include /* signal, SIGPIPE, SIG_IGN */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void fill( int fd, ssize_t bytes ) { char buf[ 512 ]; memset( buf, 'x', 512 ); ssize_t nwrote; while ( bytes > 0 && ( nwrote = write( fd, buf, bytes > 512 ? 512 : bytes ) ) > 0 ) bytes -= nwrote; assert( nwrote != -1 ); assert( bytes == 0 ); } static int drain( int fd, ssize_t bytes ) { char buf[ bytes ]; if ( recv( fd, buf, bytes, MSG_WAITALL ) == -1 ) return -1; else return 0; } int main( void ) { if ( signal( SIGPIPE, SIG_IGN ) == SIG_ERR ) err( 2, "signal" ); int fds_a[ 2 ], fds_b[ 2 ]; void *handle; if ( socketpair( AF_UNIX, SOCK_STREAM, 0, fds_a ) == -1 ) err( 1, "creating a socketpair" ); if ( socketpair( AF_UNIX, SOCK_STREAM, 0, fds_b ) == -1 ) err( 1, "creating a socketpair" ); handle = meter_start( fds_a[ 0 ], fds_b[ 0 ], 10 ); fill( fds_a[ 1 ], 15 ); fill( fds_b[ 1 ], 18 ); while ( meter_read( handle ) < 13 ) sched_yield(); assert( meter_read( handle ) <= 33 ); for ( int i = 0; i < 20; ++i ) { fill( fds_a[ 1 ], 15 ); assert( drain( fds_b[ 1 ], 15 ) == 0 ); } for ( int i = 0; i < 20; ++i ) { fill( fds_a[ 1 ], 18 ); fill( fds_b[ 1 ], 18 ); assert( drain( fds_b[ 1 ], 18 ) == 0 ); assert( drain( fds_a[ 1 ], 18 ) == 0 ); } while ( meter_read( handle ) < 13 + 20 * 15 + 20 * 36 ) sched_yield(); assert( meter_read( handle ) <= 33 + 20 * 15 + 20 * 36 ); close_or_warn( fds_a[ 1 ], "socket" ); close_or_warn( fds_b[ 1 ], "socket" ); assert( meter_cleanup( handle ) >= 13 + 20 * 15 + 20 * 36 ); return 0; }