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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions