2121#include <libcrun/cgroup.h>
2222#include <libcrun/cgroup-systemd.h>
2323#include <sys/types.h>
24+ #include <sys/epoll.h>
2425#include <unistd.h>
2526#include <string.h>
27+ #include <fcntl.h>
2628
2729typedef int (* test ) ();
2830
@@ -629,6 +631,73 @@ test_crun_ensure_directory ()
629631 return 0 ;
630632}
631633
634+ static int
635+ test_channel_fd_pair_no_busy_loop_on_blocked_output ()
636+ {
637+ /* When the output pipe is blocked, channel_fd_pair_process must not register
638+ EPOLLIN on the input fd. Verify by writing new data to the input AFTER
639+ processing and checking that epoll_wait does not fire. */
640+ libcrun_error_t err = NULL ;
641+ cleanup_close int infd0 = -1 , infd1 = -1 , outfd0 = -1 , outfd1 = -1 ;
642+ cleanup_close int epollfd = -1 ;
643+ cleanup_channel_fd_pair struct channel_fd_pair * channel = NULL ;
644+ struct epoll_event ev ;
645+ char buf [4096 ];
646+ int input_pipe [2 ], output_pipe [2 ];
647+ int ret ;
648+
649+ if (pipe (input_pipe ) < 0 || pipe (output_pipe ) < 0 )
650+ return -1 ;
651+
652+ infd0 = input_pipe [0 ];
653+ infd1 = input_pipe [1 ];
654+ outfd0 = output_pipe [0 ];
655+ outfd1 = output_pipe [1 ];
656+
657+ fcntl (input_pipe [0 ], F_SETFL , O_NONBLOCK );
658+ fcntl (input_pipe [1 ], F_SETFL , O_NONBLOCK );
659+ fcntl (output_pipe [0 ], F_SETFL , O_NONBLOCK );
660+ fcntl (output_pipe [1 ], F_SETFL , O_NONBLOCK );
661+
662+ /* Fill the output pipe so writes return EAGAIN. */
663+ memset (buf , 'X' , sizeof (buf ));
664+ while (write (output_pipe [1 ], buf , sizeof (buf )) > 0 )
665+ ;
666+
667+ /* Seed some input data so channel_fd_pair_process has work to do. */
668+ if (write (input_pipe [1 ], "A" , 1 ) != 1 )
669+ return -1 ;
670+
671+ epollfd = epoll_create1 (EPOLL_CLOEXEC );
672+ if (epollfd < 0 )
673+ return -1 ;
674+
675+ channel = channel_fd_pair_new (input_pipe [0 ], output_pipe [1 ], 4096 );
676+
677+ ret = channel_fd_pair_process (channel , epollfd , & err );
678+ if (ret < 0 )
679+ {
680+ crun_error_release (& err );
681+ return -1 ;
682+ }
683+
684+ /* Write new data so that an edge-triggered EPOLLIN (if registered) fires. */
685+ if (write (input_pipe [1 ], "B" , 1 ) != 1 )
686+ return -1 ;
687+
688+ /* With the fix EPOLLIN is not registered, so epoll_wait must time out.
689+ Without the fix EPOLLIN|EPOLLET is registered and the new data triggers
690+ an immediate wake-up. */
691+ ret = epoll_wait (epollfd , & ev , 1 , 0 );
692+ if (ret > 0 && ev .data .fd == input_pipe [0 ])
693+ {
694+ fprintf (stderr , "epoll fired on input fd while output is blocked\n" );
695+ return -1 ;
696+ }
697+
698+ return 0 ;
699+ }
700+
632701static void
633702run_and_print_test_result (const char * name , int id , test t )
634703{
@@ -652,9 +721,9 @@ main ()
652721{
653722 int id = 1 ;
654723#ifdef HAVE_SYSTEMD
655- printf ("1..16 \n" );
724+ printf ("1..17 \n" );
656725#else
657- printf ("1..13 \n" );
726+ printf ("1..14 \n" );
658727#endif
659728 RUN_TEST (test_crun_path_exists );
660729 RUN_TEST (test_write_read_file );
@@ -669,6 +738,7 @@ main ()
669738 RUN_TEST (test_str_join_array );
670739 RUN_TEST (test_get_current_timestamp );
671740 RUN_TEST (test_crun_ensure_directory );
741+ RUN_TEST (test_channel_fd_pair_no_busy_loop_on_blocked_output );
672742#ifdef HAVE_SYSTEMD
673743 RUN_TEST (test_parse_sd_array );
674744 RUN_TEST (test_get_scope_path );
0 commit comments