//go:build linux && !android
// +build linux,!android

package tio

import (
	"os"

	"golang.org/x/sys/unix"
)

// blockOn parks the calling goroutine until fd is ready (events is POLLIN for
// reads, POLLOUT for writes) or shutdownFd signals teardown. It builds the
// pollfd array on the stack every call, so concurrent callers on the same
// Queue never share Revents storage: the previous shared-array implementation
// was a genuine Go data race when two writers parked in poll(2) at once (the
// kernel writing Revents while another goroutine zeroed it). Level-triggered
// events kept it from deadlocking, but it was still a race.
//
// Poll(2) is looped over EINTR. err is checked before the Revents bits are
// trusted, since a failed poll may leave them bogus. Returns os.ErrClosed when
// shutdown was signaled (POLLIN on shutdownFd) or either fd reported a problem
// condition (POLLHUP|POLLNVAL|POLLERR).
func blockOn(fd, shutdownFd int32, events int16) error {
	const problemFlags = unix.POLLHUP | unix.POLLNVAL | unix.POLLERR
	pfds := [2]unix.PollFd{
		{Fd: fd, Events: events},
		{Fd: shutdownFd, Events: unix.POLLIN},
	}
	var err error
	for {
		_, err = unix.Poll(pfds[:], -1)
		if err != unix.EINTR {
			break
		}
	}
	tunEvents := pfds[0].Revents
	shutdownEvents := pfds[1].Revents
	// Check err before trusting the potentially bogus bits we just got.
	if err != nil {
		return err
	}
	if shutdownEvents&(unix.POLLIN|problemFlags) != 0 {
		return os.ErrClosed
	}
	if tunEvents&problemFlags != 0 {
		return os.ErrClosed
	}
	return nil
}
