0

I develop an update system in rust. It create patches between 2 binaries. You can download binaries or patches and install it. Patches and binaries are compressed. The client download compressed files and decompress it

I do

#[async_trait]
impl<'a> Stream for UpdatePackageStream<'a> {
    type Item = Result<SharedUpdateProgress, UpdateError>;

    async fn poll_next(
        self: Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> Poll<Option<Self::Item>> {
        let this = self.get_mut();
        let download_poll = this.download_stream.poll_next_unpin(cx);
        let apply_poll = this.apply_stream.poll_next_unpin(cx);

        match (download_poll, apply_poll) {
            (Poll::Ready(None), Poll::Ready(None)) => Poll::Ready(None),
            (Poll::Pending, Poll::Pending) => Poll::Pending,
            (Poll::Pending, Poll::Ready(None)) => Poll::Pending,
            (Poll::Ready(None), Poll::Pending) => Poll::Pending,
            (Poll::Ready(Some(Err(err))), _) => {
                // Download errors cause the apply thread to be cancelled
                this.apply_stream.cancel();
                Poll::Ready(Some(Err(err)))
            }
            (download_poll, apply_poll) => {
                let mut delta = Progression::default();
                if let Poll::Ready(Some(Ok(download_progress))) = download_poll {
                    this.state.borrow_mut().available = download_progress.available;

                    let mut state = this.shared_state.lock().await;
                    state.downloading_operation_idx = download_progress.available.operation_idx;
                    delta.downloaded_files = download_progress.delta_downloaded_files;
                    delta.downloaded_bytes = download_progress.delta_downloaded_bytes;
                    this.apply_stream.notify(download_progress.available);
                }
                if let Poll::Ready(Some(apply_progress)) = apply_poll {
                    match apply_progress {
                        Ok(apply_progress) => {
                            this.state.borrow_mut().applied.operation_idx =
                                apply_progress.operation_idx;
                            let mut state = this.shared_state.lock().await; // note the await here, so the closure must be async
                            state.applying_operation_idx = apply_progress.operation_idx;
                            delta.applied_files = apply_progress.delta_applied_files;
                            delta.applied_input_bytes = apply_progress.delta_input_bytes;
                            delta.applied_output_bytes = apply_progress.delta_output_bytes;
                        }
                        Err(ApplyError::OperationFailed { path, slice, cause }) => {
                            warn!("{} failed: {}", path, cause);
                            let mut state = this.state.borrow_mut();
                            state.failures.push(match slice {
                                Some(slice) => metadata::v1::Failure::Slice { path, slice },
                                None => metadata::v1::Failure::Path { path },
                            });
                            delta.failed_files = 1;
                        }
                        Err(ApplyError::Cancelled) => {}
                        Err(ApplyError::PoisonError) => {
                            return Poll::Ready(Some(Err(UpdateError::PoisonError)))
                        }
                    }
                }

              ``  {
 
                   let mut state = this.shared_state.lock().await;
                    state.inc_progress(delta);
                }

                Poll::Ready(Some(Ok(this.shared_state.clone())))
            }
        }
    }
}

But get

error[E0195]: lifetime parameters or bounds on method `poll_next` do not match the trait declaration
   --> lib/src/workspace/updater.rs:143:14
    |
143 |       async fn poll_next(
    |  ______________^
144 | |         self: Pin<&mut Self>,
145 | |         cx: &mut std::task::Context<'_>,
146 | |     ) -> Poll<Option<Self::Item>> {
    | |_____^ lifetimes do not match method in trait

Stream is from future::stream::Stream Without async_trait and the 2 async fn, the fn and my app build without issue.

If I use std::sync::Mutex instead of tokio::sync::Mutex, I get

   let state = update_state.lock();
    |             ----- has type `std::sync::MutexGuard<'_, UpdateProgress>` which is not `Send`
...
273 |         let res = update_stream.try_for_each(|_state| future::ready(Ok(()))).await;
    |                                                                             ^^^^^^ await occurs here, with `state` maybe used later
...
305 |     }
    |     - `state` is later dropped here
    = note: required for the cast from `impl futures::Future<Output = Result<tonic::Response<BuildOutput>, Status>>` to the object type `dyn futures::Future<Output = Result<tonic::Response<BuildOutput>, Status>> + std::marker::Send`

UpdateProgress is a simple struct.

I don't inderstand why I get these issues and don't know how to fix them

Vana
  • 753
  • 3
  • 11
  • 20
  • 1
    Can you clarify what `Stream` is? Because the de-facto [`Stream`](https://docs.rs/futures/latest/futures/stream/trait.Stream.html) from the futures crate does not have `poll_next` as an `async` method... – kmdreko Jan 17 '23 at 19:21
  • What kind of lock is `this.shared_state` and do you actually need it to be asynchronous? – kmdreko Jan 17 '23 at 20:03
  • 1
    If I understand well its documentation, `async_ trait` doesn't allow you to turn *any* trait into one that accepts `async` versions of its functions. It merely allows *you* to create new async traits. – jthulhu Jan 17 '23 at 20:28
  • So how to call async fn into `poll_next` ? – Vana Jan 17 '23 at 20:31
  • You don't. There are ways you *could* but I would not advise that. That is why I ask if the lock you're using actually does need to be asynchronous, because there are plenty of cases where a synchronous mutex is better. See [What is the difference between std::sync::Mutex vs tokio::sync::Mutex?](/q/73840520/2189130) If you still deem the lock *should* be asynchronous, then you need to reorganize your function and data structure to handle polling when the lock is not ready yet. – kmdreko Jan 17 '23 at 20:47
  • I switch to `std::sync::Mutex` but get another issue (I update my post) – Vana Jan 18 '23 at 06:31
  • The erroneous line does not appear in your original code. But regardless, it does indicate you're still trying to use `.await` where its already been mentioned that you can't make `poll_fn` an `async` function. – kmdreko Jan 18 '23 at 16:31

0 Answers0