Skip to content

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

@skelly-energid

Description

@skelly-energid

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().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions