Repository: actix/actix-net (language: rust)

Issue:
'actix-server accept loop' panic when resuming from sleep

I don't know if this bug can affect web servers in production but I encounter it daily as a developer.

## Steps to reproduce

1. Build and launch an actix server (tested with Ubuntu 20.10).
2. Suspend the computer.
3. Resume the computer.

```log
thread 'actix-server accept loop' panicked at 'Poll error: Interrupted system call (os error 4)', /home/yoric/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-server-2.0.0-beta.2/src/accept.rs:164:37
stack backtrace:
   0: rust_begin_unwind
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:483
   1: std::panicking::begin_panic_fmt
             at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:437
   2: actix_server::accept::Accept::poll_with::{{closure}}
             at /home/yoric/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-server-2.0.0-beta.2/src/accept.rs:164
   3: core::result::Result<T,E>::unwrap_or_else
             at /home/yoric/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs:825
   4: actix_server::accept::Accept::poll_with
             at /home/yoric/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-server-2.0.0-beta.2/src/accept.rs:162
   5: actix_server::accept::Accept::start::{{closure}}
             at /home/yoric/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-server-2.0.0-beta.2/src/accept.rs:114
```

While the situation in which a server goes to sleep is unusual, I can think of applications that embed a web server for use on a desktop/laptop, which makes it a potential issue for some apps.

## Expected Behavior

The web server should be able to somehow resume activity when the computer wakes up.


## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->

* Rust Version (I.e, output of `rustc -V`):1.48
* Actix Web Version: 2.0.0-beta.2



Discussion / hints:
```rust
            if let Err(e) = self.poll.poll(&mut events, None) {
                if e.kind() == std::io::ErrorKind::Interrupted {
                    continue;
                } else {
                    panic!("Poll error: {}", e);
                }
            }
```

I personally think interrupted should not be handled. mio remove the auto retry for a reason. so I would leave the change to others.
Met same problem when strace attach to an running server, it failed with
```
[pid  8114] epoll_wait(104,  <unfinished ...>
[pid  8123] write(2, "' panicked at '", 15) = 15
[pid  8113] epoll_wait(109,  <unfinished ...>
[pid  8123] write(2, "Poll error: Interrupted system call (os error 4)", 48 <unfinished ...>
[pid  8112] epoll_wait(67,  <unfinished ...>
[pid  8123] <... write resumed> )       = 48
[pid  8110] epoll_wait(113,  <unfinished ...>
[pid  8123] write(2, "', ", 3)          = 3
[pid  8109] epoll_wait(117,  <unfinished ...>
[pid  8123] write(2, "/opt/tiger/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-server-2.0.0-beta.2/src/accept.rs", 98 <unfinished ...>
```
FYI: Seems golang automatically retries net related syscalls.
```
Packages <code>os</code> and <code>net</code> now automatically
      retry system calls that fail with <code>EINTR</code>. Previously
      this led to spurious failures, which became more common in Go
      1.14 with the addition of asynchronous preemption. Now this is
      handled transparently.
```
Also I've searched EINTR and tokio, then find the issue that tokio/async team decides swallow the EINTR for AsyncRead and AsyncWrite, though didn't mention what should happen on accept. The general idea is "retry error is not that useful for async code" I think?
Ref: https://github.com/rust-lang/futures-rs/issues/803#issuecomment-368617707
> ```rust
>             if let Err(e) = self.poll.poll(&mut events, None) {
>                 if e.kind() == std::io::ErrorKind::Interrupted {
>                     continue;
>                 } else {
>                     panic!("Poll error: {}", e);
>                 }
>             }
> ```
> 
> I personally think interrupted should not be handled. mio remove the auto retry for a reason. so I would leave the change to others.

Seems the reason to remove this in mio is to reduce api surfaces, and in tokio-rs, they retried this Err.  I think this is a proper fix to introduce into actix-net, otherwise, end user will see more panics or hanging.
ref: https://github.com/tokio-rs/mio/issues/494

Fix patch:
diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md
index 70728064ab..d99287ef71 100644
--- a/actix-server/CHANGES.md
+++ b/actix-server/CHANGES.md
@@ -2,8 +2,10 @@
 
 ## Unreleased - 2021-xx-xx
 * Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246]
+* Add retry for EINTR(`io::Interrupted`) in `Accept`'s poll loop. [#264]
 
 [#246]: https://github.com/actix/actix-net/pull/246
+[#264]: https://github.com/actix/actix-net/pull/264
 
 
 ## 2.0.0-beta.2 - 2021-01-03
diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs
index a52184d9e1..c8c1da4792 100644
--- a/actix-server/src/accept.rs
+++ b/actix-server/src/accept.rs
@@ -161,9 +161,16 @@ impl Accept {
         let mut events = mio::Events::with_capacity(128);
 
         loop {
-            self.poll
-                .poll(&mut events, None)
-                .unwrap_or_else(|e| panic!("Poll error: {}", e));
+            if let Err(e) = self.poll.poll(&mut events, None) {
+                match e.kind() {
+                    std::io::ErrorKind::Interrupted => {
+                        continue;
+                    }
+                    _ => {
+                        panic!("Poll error: {}", e);
+                    }
+                }
+            }
 
             for event in events.iter() {
                 let token = event.token();


Test patch: