skypjack / uvw

Header-only, event based, tiny and easy to use libuv wrapper in modern C++ - now available as also shared/static library!
MIT License
1.84k stars 209 forks source link

How to handle the data if send fails. #255

Closed pengweichu closed 3 years ago

pengweichu commented 3 years ago

If the write is failed, the ErrorEvent will be triggered, but I would like to resend send that data again, so how can get the data in the ErrorEvent?

Can I set any id with the write function then that id will be callback in the ErrorEvent or WriteEvent?

Thanks

skypjack commented 3 years ago

Uhm... Data are consumed and likely destroyed by when the callback is invoked. However, you've an user data field on all handles. You can probably attach them there to reuse the sane information in case of errors.

pengweichu commented 3 years ago

Uhm... Data are consumed and likely destroyed by when the callback is invoked. However, you've an user data field on all handles. You can probably attach them there to reuse the sane information in case of errors.

I would like to have the user data for every write, seems not to support it now. Thanks

stefanofiorentino commented 3 years ago

Hello @pengweichu, I'm not a libuv super-expert. AFAIK, the uv_write performs a copy and invalidate your data. Cannot understand if you can recover this, I think not. You should take this "sending" message somewhere suitable (I suggest not global ;)) and remove once sent.

stefanofiorentino commented 3 years ago

@pengweichu I recall that you can use tryWrite instead, to know if you are not able to send data and easier manage the failure.

pengweichu commented 3 years ago

Thanks all, I'm going to use the ASIO.

mutexstream commented 2 years ago

This seems to be a constant pain-point when using this library and it is essentially caused by having all errors published to ErrorEvent. It'd be nice to be able to easily determine the source of an error so the corresponding operation can be retried.

For example, if we had uvw::WriteErrorEvent, this write-specific error can contain the data that the user tried to write.

skypjack commented 2 years ago

The v3 branch already contains a lot of changes in this regard. I'm more than willing to refine it further but please give it a look and try to formulate a request in this regard.

CC @stefanofiorentino side question: what if we merged the branch upstream next week?

mutexstream commented 2 years ago

@skypjack Do you have an example of how to perform write() retries with the v3 api?

mutexstream commented 2 years ago

I ended up modifying the write method in uvw to run a callback in the listener once write completes/fails. The callback then fires whatever events I need to be fired.

stefanofiorentino commented 2 years ago

CC @stefanofiorentino side question: what if we merged the branch upstream next week?

@skypjack up to me it's ok to merge. Let's remember we'll break several projects pointing to master's HEAD, though.

mutexstream commented 2 years ago

For anyone who later stumbles across this issue, here's the modification I made to the write() method in the stream.hpp (or stream.h) file.

    void write(char *data, unsigned int len, std::function<void(void)> done) {
        auto req = this->loop().template resource<details::WriteReq>(
                std::unique_ptr < char[], details::WriteReq::Deleter > {
                        data, [](char *) {}
                }, len);

        auto listener = [ptr = this->shared_from_this(), done](const auto &event, const auto &) {
            ptr->publish(event);
            done();
        };

        req->template once<ErrorEvent>(listener);
        req->template once<WriteEvent>(listener);
        req->write(this->template get<uv_stream_t>());
    }

Of course, one can have a separate listener for WriteEvent and ErrorEvent and only run the done() callback when a failure occurs (i.e. the ErrorEvent listener). The done() callback then triggers the retry.

mutexstream commented 2 years ago

To spell it all out, the retry logic should look like this.

    void write(char *data, unsigned int len, std::function<void(char *, unsigned int)> retry) {
        auto req = this->loop().template resource<details::WriteReq>(
                std::unique_ptr < char[], details::WriteReq::Deleter > {
                        data, [](char *) {}
                }, len);

        auto writeListener = [ptr = this->shared_from_this()](const auto &event, const auto &) {
            ptr->publish(event);
        };

        auto errorListener = [ptr = this->shared_from_this(), retry](const auto &event, const auto &) {
            ptr->publish(event);
            retry(data, len);
        };

        req->template once<ErrorEvent>(errorListener);
        req->template once<WriteEvent>(writeListener);
        req->write(this->template get<uv_stream_t>());
    }