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

Namespace-level thread can cause other threads to not start sometimes #20477

Open
skelly-energid opened this issue Oct 17, 2023 · 5 comments
Open

Comments

@skelly-energid
Copy link

Version of emscripten/emsdk:

 3.1.46 

Failing command line in full:

The script will build and run the page, refreshing until the thread fails to start for some time. Due to the namespace-level object, it is random whether runThread() runs successfully. When it breaks, it seems to deadlock when attempting to execute the cout line.

#!/bin/sh

set -e
set -v

cat << EOF > mywrapper.cpp

#include <atomic>
#include <memory>
#include <thread>

class MyWrapper
{
public:
    explicit MyWrapper();
    ~MyWrapper();
    bool isThreadReady() const;

private:
    void runThread();

    std::unique_ptr<std::thread> m_ThreadPtr;

    std::atomic_bool m_IsThreadReady;
};

#include <emscripten/bind.h>

#include <iostream>

struct StaticInitStruct
{
  StaticInitStruct() : m_thread_id(0)
  {
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, 1024 * 1024);

    const int thread_created = pthread_create(&m_thread_id, &attr, _start, this);
    pthread_attr_destroy(&attr);

  }

  static void* _start(void* context)
  {
    std::cout << "STARTED\n";
    return nullptr;
  }

  pthread_t m_thread_id;
};

int i1;
std::string s1;
std::string s2;
int i2;
std::string s3;
std::string s4;
unsigned int s5;
StaticInitStruct statInit;

int main(int argc, char** argv)
{
    return 0;
}

MyWrapper::MyWrapper() :
    m_IsThreadReady(false)
{
    m_ThreadPtr.reset(
        new std::thread(&MyWrapper::runThread, this));
}

MyWrapper::~MyWrapper()
{
    if (m_ThreadPtr)
    {
        m_ThreadPtr->join();
        m_ThreadPtr.reset();
    }
}

void MyWrapper::runThread()
{
    std::cout << "runThread\n";

    m_IsThreadReady.store(true);
}


bool MyWrapper::isThreadReady() const
{
    return m_IsThreadReady.load();
}

// clang-format off
EMSCRIPTEN_BINDINGS(my_wrapper)
{
    emscripten::class_<MyWrapper>("MyWrapper")
        .constructor()
        .function("isThreadReady", &MyWrapper::isThreadReady)
        ;
}
// clang-format on
EOF

cat << EOF > index.html
<head>

    <script type='text/javascript' src=./myWrapperModule.js />
</head>

<body>
  <script>

    console.log("Get started");
</script>

  <script>

    MyWrapperModule().then(module => {
        var wrapper = new module.MyWrapper();

        var refreshPageNow = false;

        setTimeout(() => {
            refreshPageNow = true;
        }, 1750);

        var threadCheckInterval = setInterval(() => {
            if (wrapper.isThreadReady())
            {
                console.trace("READY");
                clearInterval(threadCheckInterval);
                location.reload();
            } else if (refreshPageNow) {
                console.log("BROKEN");
                clearInterval(threadCheckInterval);
                document.getElementById("result").innerHTML = "BROKEN";
            } else {
                console.log("WAIT");
            }
        }, 500);
    });
  </script>

    <p>TESTING</p>
    <p id="result"></p>
</body>
EOF

cat << EOF > pre.js
Module['mainScriptUrlOrBlob'] = self.location.origin + '/myWrapperModule.js';
EOF

em++ -sUSE_PTHREADS -fwasm-exceptions  -O2 -g -o mywrapper.cpp.o -c mywrapper.cpp
em++ -sUSE_PTHREADS -fwasm-exceptions  -O2 -g -sWASM=1 \
        -sPROXY_TO_PTHREAD -sINITIAL_MEMORY=128MB -sPTHREAD_POOL_SIZE=1 -sMODULARIZE=1 \
        -sEXPORT_NAME=MyWrapperModule --pre-js=pre.js --minify=0 --bind \
        -sENVIRONMENT=web,worker mywrapper.cpp.o -o myWrapperModule.js

emrun index.html

This likely can't be made to work, so it should maybe be documented, diagnosed and possibly fail-early if pthread_create is called before main().

@sbc100
Copy link
Collaborator

sbc100 commented Oct 26, 2023

I don't see any reason why pthread_create wouldn't work in a static constructor (i.e. before main) . That should work fine.

@skelly-energid
Copy link
Author

skelly-energid commented Oct 27, 2023

I don't see any reason why pthread_create wouldn't work in a static constructor (i.e. before main) . That should work fine.

Ok, then ignore my speculating :). The testcase fails nevertheless.

@sbc100
Copy link
Collaborator

sbc100 commented Oct 27, 2023

Anything you can do to narrow down the test case would be great. For example, can you remove the StaticInitStruct completely now?

Can s1 - s5 removed? Can i1 and i2 be removed?

Can main be removed?

@skelly-energid
Copy link
Author

skelly-energid commented Oct 27, 2023

As a sanity check - can you try my repro and confirm you can reproduce the issue?

@sbc100
Copy link
Collaborator

sbc100 commented Oct 27, 2023

I don't think I'll have time to look at this today, but maybe next week. The smaller you can make the repro the easier it will be for whoever does look at it.

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

2 participants