diff --git a/unix/syscall_internal_solaris_test.go b/unix/syscall_internal_solaris_test.go index a92753a14..d3b587189 100644 --- a/unix/syscall_internal_solaris_test.go +++ b/unix/syscall_internal_solaris_test.go @@ -28,6 +28,40 @@ func (e *EventPort) checkInternals(t *testing.T, fds, paths, cookies, pending in } } +// getOneRetry wraps EventPort.GetOne which in turn wraps a syscall that can be +// interrupted causing us to receive EINTR. +// To prevent our tests from flaking, we retry the syscall until it works +// rather than get unexpected results in our tests. +func getOneRetry(t *testing.T, p *EventPort, timeout *Timespec) (e *PortEvent, err error) { + t.Helper() + for { + e, err = p.GetOne(timeout) + if err != EINTR { + break + } + } + return e, err +} + +// getRetry wraps EventPort.Get which in turn wraps a syscall that can be +// interrupted causing us to receive EINTR. +// To prevent our tests from flaking, we retry the syscall until it works +// rather than get unexpected results in our tests. +func getRetry(t *testing.T, p *EventPort, s []PortEvent, min int, timeout *Timespec) (n int, err error) { + t.Helper() + for { + n, err = p.Get(s, min, timeout) + if err != EINTR { + break + } + // If we did get EINTR, make sure we got 0 events + if n != 0 { + t.Fatalf("EventPort.Get returned events on EINTR.\ngot: %d\nexpected: 0", n) + } + } + return n, err +} + // Regression test for DissociatePath returning ENOENT // This test is intended to create a linear worst // case scenario of events being associated and @@ -143,7 +177,7 @@ func TestEventPortDissociateAlreadyGone(t *testing.T) { runtime.GC() // Before the fix, this would cause a nil pointer exception - e, err := port.GetOne(nil) + e, err := getOneRetry(t, port, nil) if err != nil { t.Errorf("failed to get an event: %v", err) } @@ -152,7 +186,7 @@ func TestEventPortDissociateAlreadyGone(t *testing.T) { t.Errorf(`expected "cookie1", got "%v"`, e.Cookie) } // Make sure that a cookie of the same value doesn't cause removal from the paths map incorrectly - e, err = port.GetOne(nil) + e, err = getOneRetry(t, port, nil) if err != nil { t.Errorf("failed to get an event: %v", err) } @@ -167,7 +201,7 @@ func TestEventPortDissociateAlreadyGone(t *testing.T) { } // Event has fired, but until processed it should still be in the map port.checkInternals(t, 0, 1, 1, 1) - e, err = port.GetOne(nil) + e, err = getOneRetry(t, port, nil) if err != nil { t.Errorf("failed to get an event: %v", err) } @@ -221,12 +255,12 @@ func TestEventPortGetAfterClose(t *testing.T) { port.paths = nil port.cookies = nil // Ensure that we get back reasonable errors rather than panic - _, err = port.GetOne(nil) + _, err = getOneRetry(t, port, nil) if err == nil || err.Error() != "this EventPort is already closed" { t.Errorf("didn't receive expected error of 'this EventPort is already closed'; got: %v", err) } events := make([]PortEvent, 2) - n, err = port.Get(events, 1, nil) + n, err = getRetry(t, port, events, 1, nil) if n != 0 { t.Errorf("expected to get back 0 events, got %d", n) } diff --git a/unix/syscall_solaris_test.go b/unix/syscall_solaris_test.go index 6c2b906df..88eccaf09 100644 --- a/unix/syscall_solaris_test.go +++ b/unix/syscall_solaris_test.go @@ -18,6 +18,40 @@ import ( "golang.org/x/sys/unix" ) +// getOneRetry wraps EventPort.GetOne which in turn wraps a syscall that can be +// interrupted causing us to receive EINTR. +// To prevent our tests from flaking, we retry the syscall until it works +// rather than get unexpected results in our tests. +func getOneRetry(t *testing.T, p *unix.EventPort, timeout *unix.Timespec) (e *unix.PortEvent, err error) { + t.Helper() + for { + e, err = p.GetOne(timeout) + if err != unix.EINTR { + break + } + } + return e, err +} + +// getRetry wraps EventPort.Get which in turn wraps a syscall that can be +// interrupted causing us to receive EINTR. +// To prevent our tests from flaking, we retry the syscall until it works +// rather than get unexpected results in our tests. +func getRetry(t *testing.T, p *unix.EventPort, s []unix.PortEvent, min int, timeout *unix.Timespec) (n int, err error) { + t.Helper() + for { + n, err = p.Get(s, min, timeout) + if err != unix.EINTR { + break + } + // If we did get EINTR, make sure we got 0 events + if n != 0 { + t.Fatalf("EventPort.Get returned events on EINTR.\ngot: %d\nexpected: 0", n) + } + } + return n, err +} + func TestStatvfs(t *testing.T) { if err := unix.Statvfs("", nil); err == nil { t.Fatal(`Statvfs("") expected failure`) @@ -84,13 +118,13 @@ func TestBasicEventPort(t *testing.T) { bs := []byte{42} tmpfile.Write(bs) timeout := new(unix.Timespec) - timeout.Sec = 1 - pevent, err := port.GetOne(timeout) + timeout.Nsec = 100 + pevent, err := getOneRetry(t, port, timeout) if err == unix.ETIME { t.Errorf("GetOne timed out: %v", err) } if err != nil { - t.Errorf("GetOne failed: %v", err) + t.Fatalf("GetOne failed: %v", err) } if pevent.Path != path { t.Errorf("Path mismatch: %v != %v", pevent.Path, path) @@ -135,13 +169,13 @@ func TestEventPortFds(t *testing.T) { t.Errorf("Pending() failed: %v, %v", n, err) } timeout := new(unix.Timespec) - timeout.Sec = 1 - pevent, err := port.GetOne(timeout) + timeout.Nsec = 100 + pevent, err := getOneRetry(t, port, timeout) if err == unix.ETIME { t.Errorf("GetOne timed out: %v", err) } if err != nil { - t.Errorf("GetOne failed: %v", err) + t.Fatalf("GetOne failed: %v", err) } if pevent.Fd != fd { t.Errorf("Fd mismatch: %v != %v", pevent.Fd, fd) @@ -181,10 +215,8 @@ func TestEventPortErrors(t *testing.T) { } timeout := new(unix.Timespec) timeout.Nsec = 1 - _, err = port.GetOne(timeout) + _, err = getOneRetry(t, port, timeout) if err != unix.ETIME { - // See https://go.dev/issue/58259 - // Perhaps we sometimes get EINTR ??? t.Errorf("port.GetOne(%v) returned error %v, want %v", timeout, err, unix.ETIME) } err = port.DissociateFd(uintptr(0)) @@ -192,15 +224,15 @@ func TestEventPortErrors(t *testing.T) { t.Errorf("unexpected success dissociating unassociated fd") } events := make([]unix.PortEvent, 4) - _, err = port.Get(events, 5, nil) + _, err = getRetry(t, port, events, 5, nil) if err == nil { t.Errorf("unexpected success calling Get with min greater than len of slice") } - _, err = port.Get(nil, 1, nil) + _, err = getRetry(t, port, nil, 1, nil) if err == nil { t.Errorf("unexpected success calling Get with nil slice") } - _, err = port.Get(nil, 0, nil) + _, err = getRetry(t, port, nil, 0, nil) if err == nil { t.Errorf("unexpected success calling Get with nil slice") } @@ -232,7 +264,13 @@ func ExamplePortEvent() { w.Write(bs) timeout := new(unix.Timespec) timeout.Sec = 1 - pevent, err := port.GetOne(timeout) + var pevent *unix.PortEvent + for { + pevent, err = port.GetOne(timeout) + if err != unix.EINTR { + break + } + } if err != nil { fmt.Printf("didn't get the expected event: %v\n", err) } @@ -278,7 +316,7 @@ func TestPortEventSlices(t *testing.T) { timeout := new(unix.Timespec) timeout.Nsec = 1 events := make([]unix.PortEvent, 4) - n, err = port.Get(events, 3, timeout) + n, err = getRetry(t, port, events, 3, timeout) if err != nil { t.Errorf("Get failed: %v", err) } @@ -291,7 +329,7 @@ func TestPortEventSlices(t *testing.T) { t.Errorf("unexpected event. got %v, expected %v", p.Events, unix.FILE_DELETE) } } - n, err = port.Get(events, 3, timeout) + n, err = getRetry(t, port, events, 3, timeout) if err != unix.ETIME { t.Errorf("unexpected error. got %v, expected %v", err, unix.ETIME) } @@ -314,7 +352,7 @@ func TestPortEventSlices(t *testing.T) { bs := []byte{41} w.Write(bs) - n, err = port.Get(events, 1, timeout) + n, err = getRetry(t, port, events, 1, timeout) if err != nil { t.Errorf("Get failed: %v", err) }