Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return response from asynchronous fetch call made inside C++ function bound using Embind #18834

Closed
harichards opened this issue Feb 23, 2023 · 7 comments

Comments

@harichards
Copy link
Contributor

I am writing a function in C++ that uses the Emscripten Fetch API. This function is invoked from JavaScript using Embind. I would like to return the response of an asynchronous Fetch call back to JS. Ideally, I would return a promise, as is the case when using Asyncify. However, according to #12255, Fetch is not compatible with Asyncify.

Here is an attempt at a solution, but it does not return a promise like Asyncify would:

#include <emscripten/bind.h>
#include <emscripten/fetch.h>

#include <future>

unsigned short worker()
{
    emscripten_fetch_attr_t attr;
    emscripten_fetch_attr_init(&attr);

    strcpy(attr.requestMethod, "GET");
    attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS;
    
    emscripten_fetch_t *fetch = emscripten_fetch(&attr, "https://example.com/");
    auto status = fetch->status;
    emscripten_fetch_close(fetch);

    return status;
}

unsigned short foo()
{
    auto status = std::async(&worker);
    return status.get();
}

EMSCRIPTEN_BINDINGS(my_module)
{
    emscripten::function("foo", &foo);
}

What would be the best way to accomplish this? Any help would be greatly appreciated. Thanks!

@sbc100
Copy link
Collaborator

sbc100 commented Feb 23, 2023

We have recently add a C and C++ interface to JS promises. See #18598.

However, making this work with embind would likely require some kind of magic such that the actual JS promise is returned rather then integer handle. @tlively @brendandahl any ideas?

@sbc100
Copy link
Collaborator

sbc100 commented Feb 23, 2023

@tlively I guess step one would be some kind of std::future and promise.h integration? i.e. being able to create a promise based on a std::future?

@harichards
Copy link
Contributor Author

I guess in summary, I am looking for a way to allow JS to await an asynchronous call that was started in C++, namely emscripten_fetch. This is my understanding of how Asyncify + Embind works in some cases.

My solution right now is to make a synchronous call to emscripten_fetch in another thread, await that result in C++, then return to JS. This complicates my life a bit because I am forced to compile with pthreads.

@harichards
Copy link
Contributor Author

I get the following warning with the above code:

WARN: 'Blocking on the main thread is very dangerous, see https://emscripten.org/docs/porting/pthreads.html#blocking-on-the-main-browser-thread'

I understand that std::future::get is a blocking call. After reading the above page, I am not sure what to do. I am using Embind so PROXY_TO_PTHREAD does not apply according to #9847.

@sbc100
Copy link
Collaborator

sbc100 commented Feb 23, 2023

PROXY_TO_PTHREAD only applies to the main function itself. If you call foo from the main thread it will still run on the main thread.

We have APIs for proxying other work to background threads though. See threading.h and the new proxying.h

@harichards
Copy link
Contributor Author

For my use, I found it easier to fetch using "inline" JS as opposed to emscripten_fetch:

#include <emscripten/bind.h>
#include <emscripten/emscripten.h>

EM_ASYNC_JS(int, do_fetch, (), {
    let response = await fetch("https://example.com/");
    return await response.status;
});

int foo() {
    return do_fetch();
}

EMSCRIPTEN_BINDINGS(my_module) {
    emscripten::function("foo", &foo);
}

This returns a promise when using -sASYNCIFY. I can close this now. Thank you for your help!

@tlively
Copy link
Member

tlively commented Mar 1, 2023

@tlively I guess step one would be some kind of std::future and promise.h integration? i.e. being able to create a promise based on a std::future?

Although it may be possible to integrate with <future> via a custom std::launch flag, I expect that using C++20 coroutines (which are completely unrelated to std::future, unfortunately) will be a better match.

I don't know enough about embind to say what would be required to make it work nicely with promise.h. Once we have a C++ API for promises, let's discuss this again and figure something nice out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants