fd 12 is closed via remove_device() but not removed from poll's fdset. Therefore poll continues to return with the POLLNVAL code but it is never handled. The POLLNVAL should be handled by adding G_IO_NVAL to the g_io_add_watch_full() event mask and then also included in the error condition at the top of event_io().
It looks like this might indeed be the case:
read(12, "\335\27\ 10H\0\0\ 0\0\374L\ 1\0\0\0\ 0\0\0\0\ 0\0\1\0\ 0\0", 1024) = 24 1\3\3\0\ 0\27\0\ 0\0\204\ 0\0\0\1\ 1o\0#\0\ 0\0/org/ f"..., 2048) = 923 1\1\0\0\ 0\0\27\ 0\0\0\10\ 0\0\0\5\ 1u\0\27\ 0\0\0", 24}, {"", 0}], 2) = 24
read(12, 0x6115a0, 1024) = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=7, events=POLLIN}, {fd=8, events=POLLIN}, {fd=9, events=POLLIN}, {fd=10, events=POLLIN}, {fd=11, events=POLLIN}, {fd=6, events=POLLIN}, {fd=3, events=POLLIN, revents=POLLIN}, {fd=12, events=POLLIN}], 10, -1) = 1
read(3, "l\1\0\
read(3, 0x60cca0, 2048) = -1 EAGAIN (Resource temporarily unavailable)
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=7, events=POLLIN}, {fd=8, events=POLLIN}, {fd=9, events=POLLIN}, {fd=10, events=POLLIN}, {fd=11, events=POLLIN}, {fd=6, events=POLLIN}, {fd=12, events=POLLIN}, {fd=3, events=POLLIN}], 10, 0) = 0
close(12) = 0
writev(3, [{"l\2\
poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=7, events=POLLIN}, {fd=8, events=POLLIN}, {fd=9, events=POLLIN}, {fd=10, events=POLLIN}, {fd=11, events=POLLIN}, {fd=6, events=POLLIN}, {fd=12, events=POLLIN, revents=POLLNVAL}, {fd=3, events=POLLIN}], 10, -1) = 1
fd 12 is closed via remove_device() but not removed from poll's fdset. Therefore poll continues to return with the POLLNVAL code but it is never handled. The POLLNVAL should be handled by adding G_IO_NVAL to the g_io_add_ watch_full( ) event mask and then also included in the error condition at the top of event_io().