emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.84k stars 3.31k forks source link

emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set #14344

Open yangfangfang1204 opened 3 years ago

yangfangfang1204 commented 3 years ago

when i run the 'emscripten-main\tests\resize_offscreencanvas_from_main_thread.cpp'; and compile it 'emcc resize_offscreencanvas_from_main_thread.cpp -o resize_offscreencanvas_from_main_thread.html -s USE_PTHREADS=1 -s TOTAL_MEMORY=2000MB -s ALLOW_MEMORY_GROWTH=0 -s MAX_WEBGL_VERSION=2 -s OFFSCREENCANVAS_SUPPORT=1' Then run it in chrome,it take a error: 'emscripten_force_exit cannot actually shut down the runtime, as the build does not have EXIT_RUNTIME set; main thread called exit: keepRuntimeAlive=false (counter=0); Program terminated with exit(0);'

could you tell me why

yangfangfang1204 commented 3 years ago

I use 'emscripten_cancel_main_loop();' and -s EXIT_RUNTIME=0 ; when the thread exit , there is an error uncaught unwind ?

sbc100 commented 3 years ago

I believe the message you are seeing in from the resize_offscreencanvas_from_main_thread.cpp test is just a warning isn't it? I imagine we could fix that by adding -s EXIT_RUNTIME to that test.

emscripten_set_main_loop is actually the function that will throw "unwind" if you call it with simulateInfiniteLoop = true. Normally emscripten_cancel_main_loop is called from a try/catch context that can handle the "unwind" so it appear as "uncaught", can you post your backtrace so we can see why that is not that case for your example?

yangfangfang1204 commented 3 years ago

resize_offscreencanvas_from_main_thread.cpp:

`#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <unistd.h>

// Tests that the size of an OffscreenCanvas can be queried and set from the main thread.
// Supporting this is needed since it is very common to resize Canvas size on main thread while relayouting DOM in JavaScript code.

// Define the following to test the scenario where the pthread that owns the OffscreenCanvas is running its own synchronously blocking loop (never yields to event loop).
// If not defined, the pthread that owns the OffscreenCanvas is using emscripten_set_main_loop() so periodically yields back to the event loop.
//#define TEST_SYNC_BLOCKING_LOOP

void thread_local_main_loop()
{
  int w = 0, h = 0;
  emscripten_get_canvas_element_size("#canvas", &w, &h);
  if (w == 699 && h == 299)
  {
    printf("Observed OffscreenCanvas resize to 699x299 from main thread! Test passed!\n");
#ifdef REPORT_RESULT
    REPORT_RESULT(1);
#endif

#ifndef TEST_SYNC_BLOCKING_LOOP
    emscripten_cancel_main_loop();
#endif
    emscripten_force_exit(0);
  }
  printf("%dx%d\n", w, h);
}

void *thread_main(void *arg)
{
  EmscriptenWebGLContextAttributes attr;
  emscripten_webgl_init_context_attributes(&attr);
  attr.explicitSwapControl = EM_TRUE;
  EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr);
  assert(ctx);

  // To start off, change the size of the OffscreenCanvas, main thread will test that it sees this change.
  printf("pthread resizing OffscreenCanvas to size 355x233.\n");
  emscripten_set_canvas_element_size("#canvas", 355, 233);

  // In pthread, keep polling the canvas size until we see it being changed from the main thread.
#ifdef TEST_SYNC_BLOCKING_LOOP
  for(;;)
  {
    //thread_local_main_loop();
    int w = 0, h = 0;
    emscripten_get_canvas_element_size("#canvas", &w, &h);
    printf("%dx%d\n", w, h);
    if (w == 699 && h == 299)
    {
        break;
    }
    emscripten_current_thread_process_queued_calls();
    usleep(16*1000);
  }
#else
  emscripten_set_main_loop(thread_local_main_loop, 1, 0);
#endif

  return 0;
}

void resize_canvas(void *)
{
  // Test that on the main thread, we can observe size changes to the canvas size.
  int w, h;
  emscripten_get_canvas_element_size("#canvas", &w, &h);
  assert(w == 355 && "We did not observe the effect of pthread having resized OffscreenCanvas");
  assert(h == 233);
  printf("Main thread saw canvas to get resized to %dx%d.\n", w, h);

  // Test that on the main thread, we can also change the size. (pthread listens to see this)
  printf("Main thread resizing OffscreenCanvas to size 699x299.\n");
  emscripten_set_canvas_element_size("#canvas", 699, 299);
}

//should be able to do this regardless of offscreen canvas support
void get_canvas_size()
{
  int w, h;
  emscripten_get_canvas_element_size("#canvas", &w, &h);
  assert(h == 150);
  assert(w == 300);
}

int main()
{
  get_canvas_size();
  if (!emscripten_supports_offscreencanvas())
  {
    printf("Current browser does not support OffscreenCanvas. Skipping the rest of the tests.\n");
#ifdef REPORT_RESULT
    REPORT_RESULT(1);
#endif
    return 0;
  }

  pthread_attr_t attr;
  pthread_attr_init(&attr);
  emscripten_pthread_attr_settransferredcanvases(&attr, "#canvas");

  pthread_t thread;
  printf("Creating thread.\n");
  pthread_create(&thread, &attr, thread_main, NULL);
  pthread_detach(thread);

  // Wait for a while, then change the canvas size on the main thread.
  printf("Waiting for 5 seconds for good measure.\n");
  emscripten_async_call(resize_canvas, 0, 5000);
  return 1;
}

Secode compile script is

emcc resize_offscreencanvas_from_main_thread.cpp -o resize_offscreencanvas_from_main_thread.html -s USE_PTHREADS=1 -s TOTAL_MEMORY=1000MB -s ALLOW_MEMORY_GROWTH=0 -s MAX_WEBGL_VERSION=2 -s OFFSCREENCANVAS_SUPPORT=1 -s PTHREAD_POOL_SIZE=2 -s EXIT_RUNTIME=1

Then run the resize_offscreencanvas_from_main_thread.html in chrome . there is a error(not warning): main thread called exit: keepRuntimeAlive=false (counter=0)

i haven't know how to post my backtrace , i am learning , and could you tell me keepRuntimeAlive=false (counter=0) , how to solve the error?

sbc100 commented 3 years ago

thanks for the sample code. I'll try to take a look later.

On first inspection it looks like keepRuntimeAlive should be true because the emscripten_async_call should increment that counter. So perhaps there is a bug in emscripten_async_call

sbc100 commented 3 years ago

I believe everything you are seeing is expected behaviour. When the program calls exit() or emscripten_force_exit() will see an exception flow out the top level. If you want to program to keep going then you don't call exit() or emscripten_force_exit() and you won't see any uncaught exception.

Perhaps you can describe the behaviour you expect to see or the behaviour that you want instead?

yangfangfang1204 commented 3 years ago

I want to exit a detachable thread, when using pthread_exit(0), there is a error "Uncaught ExitStatus {name: "ExitStatus", message: "Program terminated with exit(0)", status: 0}"; so how exit a detachable subThread ?

sbc100 commented 3 years ago

Can you describe you use case a little more precisely or post some example code.

Tell me if I understand what you are trying to do: You want to create and detach a pthread, then you want your main application to continue to run and have your detached thread exit using pthread_exit.

If you want your main thread to keep running after it returns you should not set EXIT_RUNTIME=1 setting this option means that the entire application will exit when the main thread returns. Alternatively you can call emscripten_exit_with_live_runtime to keep the application running after the main function returns.

Where does emscripten_force_exit() come into play in your application?

yangfangfang1204 commented 3 years ago

As discussed earlier,the example code is resize_offscreencanvas_from_main_thread.cpp,The function of this CPP is to create a detachable child thread in the main thread.The main thread changes the size of the canvas. When the child thread detects that the canvas size has changed, the child thread exits.The problem is that when the child thread exits using emscripten_force_exit(0), it returns an error. If set EXIT_RUNTIME=1, the error is main thread called exit: keepRuntimeAlive=false (counter=0), if not set EXIT_RUNTIME=1, the error is Uncaught ExitStatus {name: "ExitStatus", message: "Program terminated with exit(0)", status: 0}

yangfangfang1204 commented 3 years ago

there is another example:

SuperRender.cpp:

#include <stdio.h>
#include <stdlib.h>
#include <GLES2/gl2.h>
#include <emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <unistd.h>
#include <iostream>
#include <string>
int                  g_nStopFlag    = 0;
pthread_t       thread2;

#ifdef __cplusplus
extern "C"
{
#endif

EMSCRIPTEN_KEEPALIVE
void requestStaticRender()
{
    if(g_nStopFlag)
    {
        emscripten_cancel_main_loop();
        emscripten_force_exit(0);//exit the detached thread
    }
    else
    {
        printf("detached thread is running\n");
    }
    return;
}

EMSCRIPTEN_KEEPALIVE
void* displayThread(void* p)
{
    emscripten_set_main_loop(requestStaticRender,0,0);
    return p;
}

EMSCRIPTEN_KEEPALIVE
int Start() {
    if (!emscripten_supports_offscreencanvas())
    {
        printf("Current browser does not support OffscreenCanvas. Skipping the rest of the tests.\n");
#ifdef REPORT_RESULT
        REPORT_RESULT(1);
#endif
        return -1;
  }  
    //create offscreencavas
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    emscripten_pthread_attr_settransferredcanvases(&attr, "#canvas");
    //create detached thread
    pthread_create(&thread2, &attr, displayThread, NULL);
    pthread_detach(thread2);
    return 1;
}

EMSCRIPTEN_KEEPALIVE
int Stop()
{
    g_nStopFlag = 1;
    return 1;
}

#ifdef __cplusplus
}
#endif`

Second, compile script:

CC = emcc
AR = emar

CFLAGS  =  -O3 -Wall 
LDFLAGS =  --bind -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'getValue','writeArrayToMemory']" -s EXPORTED_FUNCTIONS="['_Start','_Stop']" -s USE_PTHREADS=1 -s TOTAL_MEMORY=1GB -s ALLOW_MEMORY_GROWTH=0 -s MAX_WEBGL_VERSION=2 -s OFFSCREENCANVAS_SUPPORT=1 -s PTHREAD_POOL_SIZE_STRICT=0 -s GL_PREINITIALIZED_CONTEXT=1
SRCS += ./SuperRender.cpp   
OBJS = $(patsubst %.cpp,%.o,$(SRCS))
TARGET = SuperRender.js
OBJ_TARGET = SuperRender.a
all: $(TARGET) $(OBJS) $(OBJ_TARGET)
$(OBJ_TARGET):$(OBJS)
    $(AR) rcs $(OBJ_TARGET) $(notdir $(OBJS))
$(TARGET): $(OBJ_TARGET)
    $(CC) $(OBJ_TARGET) -o $(TARGET) $(LDFLAGS) $(CFLAGS)   
.cpp.o:
    $(CC) $(CFLAGS) -c $<
clean:
    rm -f *.o *.a *.js *.wasm *.worker.js $(OBJS)
    rm -rf $(DIR)

Third , test.html:

<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
<section>
</section>
    <button type="button" onClick="start()">Start</button>
    <button type="button" onClick="stop()">Stop</button>
    <script async type="text/javascript" src="SuperRender.js"></script>
    <div class="button_box">
       <canvas  id="canvas" width="400px" height="200px" style="border: 2px; background-color:black"></canvas>
    </div>
<script>    
    function start () {
        Module._Start();
    }
    function stop()
    {
        Module._Stop();
    }     
 </script>   
</body>
</html>

To summarize, First compiled the SuperRender.cpp into SuperRender js, SuperRender.wasm, SuperRender.worker.js. Second, in the test.html invokes the interface of the cpp, Performs the start and stop operations of a child thread. when stop the child thread there is a error , Uncaught ExitStatus {name: "ExitStatus", message: "Program terminated with exit(0)", status: 0}`

sbc100 commented 3 years ago

Can you use pthread_exit() instead? I believe that emscripten_force_exit() is designed to being down the entire program. If you just want to exist the current thread I think pthread_exit() is the was to go.

yangfangfang1204 commented 3 years ago

I tried to exit the thread using pthread_exit, there is a error 'Uncaught unwind'

SuperRender.js:1 pthread sent an error! http://localhost:8081/SuperRender.js:1: Uncaught unwind
worker.onerror @ SuperRender.js:1
error (async)
loadWasmModuleToWorker @ SuperRender.js:1
getNewWorker @ SuperRender.js:1
spawnThread @ SuperRender.js:1
_pthread_create @ SuperRender.js:1
H @ 00017742:0x4337
Module._Start @ SuperRender.js:1
start @ testMT.html:23
onclick @ testMT.html:8
SuperRender.js:1 Uncaught unwind
maybeExit @ SuperRender.js:1
checkIsRunning @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
requestAnimationFrame (async)
requestAnimationFrame @ SuperRender.js:1
Browser_mainLoop_scheduler_rAF @ SuperRender.js:1
Browser_mainLoop_runner @ SuperRender.js:1
yangfangfang1204 commented 3 years ago

![Uploading console.png…]()

sbc100 commented 3 years ago

I think the uncaught unwind issue should be fixed in #13666. Can you verify?