ultralight-ux / Ultralight

Lightweight, high-performance HTML renderer for game and app developers.
https://ultralig.ht
4.69k stars 197 forks source link

Bad access error when creating view on a different thread #326

Open remaininlight opened 3 years ago

remaininlight commented 3 years ago

Merry Christmas to you all!

I'm integrating Ultralight into a multiplatform C++ audio framework called JUCE (https://github.com/juce-framework/JUCE). It's working great on the main thread but when I try and run it on a different thread I'm getting EXC_BAD_ACCESS when I try to create a view.

Loving Ultralight, very excited to use it but the crash seems to be happening in the proprietary part, which I'm not able to debug so any advice is gratefully received!

Running the below code on macOS, XCode 11.3, the error comes at the line: view = renderer->CreateView(600, 400, false, nullptr);

``

pragma once

include

include "../../dependencies/ultralight-sdk/include/Ultralight/Ultralight.h"

using namespace juce; using namespace ultralight;

namespace juce_ultralight { class UltralightComponent : public juce::Component, public juce::Thread { public: //============================================================================== UltralightComponent() : Thread("Ultralight Thread"), display() { addAndMakeVisible(display);

        // It runs fine if Ultralight is initialised from the main thread here
        initialiseUltralight();
        tick();

        // This causes the run() method to be called from a new thread, causing the crash
        startThread(8);
    }

    ~UltralightComponent()
    {
        app = nullptr;
    }

    void initialiseUltralight() {

        factory = std::make_unique<JuceImageSurfaceFactory>();
        Platform::instance().set_surface_factory(factory.get());

        Settings settings;
        Config config;
        config.scroll_timer_delay = 1.0 / 90.0;

        config.device_scale = 2.0;
        config.font_family_standard = "Arial";
        config.resource_path = "./resources/";
        config.use_gpu_renderer = false;

        Platform::instance().set_config(config);

        Platform::instance().set_font_loader(GetPlatformFontLoader());
        Platform::instance().set_file_system(GetPlatformFileSystem("."));
        Platform::instance().set_logger(GetDefaultLogger("ultralight.log"));

        renderer = Renderer::Create();

        // CRASH. The crash occurs here
        // Ultralight Thread (10): EXC_BAD_ACCESS (code=1, address=0x9a)
        view = renderer->CreateView(600, 400, false, nullptr);

        view->set_load_listener(this);

        view->LoadHTML("<h1>Hello World!</h1>");

        view->Focus();

        JuceImageSurface* surface = (JuceImageSurface*)view->surface();
        display.setImage(*(surface->image));
    }

    void run() override
    {
        // Calling initialiseUltralight from this thread causes the crash
        initialiseUltralight();

        while (! threadShouldExit()) {

            wait(2);
            tick();
        };
    }

    void tick() {

        renderer->Update();
        renderer->Render();
    }

    void resized() override
    {
        display.setBounds(0, 0, 600, 400);
    }

protected:

    RefPtr<App> app;
    RefPtr<Renderer> renderer;
    RefPtr<Window> window;
    RefPtr<View> view;

    std::unique_ptr<JuceImageSurfaceFactory> factory;
    ImageComponent display;

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UltralightComponent)
};

} ``

Here is the stack of the crashed thread, it seems to attempt the bad access within the proprietary part of Ultralight so I am unable to debug further - any assistance/advice/pointers appreciated.

`` Ultralight Thread (11)#0 0x00000001020de8e3 in ___lldb_unnamed_symbol395$$libWebCore.dylib ()

1 0x0000000102f66bb5 in WebCore::NetworkStateNotifier::singleton() ()

2 0x0000000101dbaf6e in ___lldb_unnamed_symbol844$$libUltralight.dylib ()

3 0x0000000102e054d7 in WebCore::Page::Page(WebCore::PageConfiguration&&) ()

4 0x0000000101dca553 in ___lldb_unnamed_symbol1134$$libUltralight.dylib ()

5 0x0000000101db158c in ___lldb_unnamed_symbol313$$libUltralight.dylib ()

6 0x0000000101daaaa8 in ___lldb_unnamed_symbol160$$libUltralight.dylib ()

7 0x000000010001ac2d in juce_ultralight::UltralightComponent::initialiseUltralight() at /Users/user/Bounce/groove-engine/dependencies/juce-ultralight/ultralight/core/ultralight_UltralightComponent.h:67

8 0x0000000100019a26 in juce_ultralight::UltralightComponent::run() at /Users/user/Bounce/groove-engine/dependencies/juce-ultralight/ultralight/core/ultralight_UltralightComponent.h:89

9 0x0000000100261566 in juce::Thread::threadEntryPoint() at /Users/user/Bounce/JUCE/modules/juce_core/threads/juce_Thread.cpp:96

10 0x0000000100261a45 in juce::juce_threadEntryPoint(void*) at /Users/user/Bounce/JUCE/modules/juce_core/threads/juce_Thread.cpp:118

11 0x000000010028b686 in juce::threadEntryProc(void*) at /Users/user/Bounce/JUCE/modules/juce_core/native/juce_posix_SharedCode.h:838

12 0x0000000104fd5dc3 in _pthread_body ()

13 0x0000000104fd8e8d in _pthread_start ()

14 0x0000000104fd4e11 in thread_start ()

``

Janrupf commented 3 years ago

Ultralight does currently not support Multithreading - you will have to do all work on one thread.

remaininlight commented 3 years ago

Thanks for your quick reply Janrupf.

I believe I am doing all the work on one thread here? In the above example code the initialiseUltralight() method contains all the interactions with Ultralight prior to the crash, when it is called from the main thread it works fine but when it is called from a different thread I get the crash detailed above.

Superxwolf commented 3 years ago

I believe it was commented on Discord that trying to initialize Ultralight outside of the main thread in MacOS had issues.

remaininlight commented 3 years ago

Thanks for the info Superxwolf. Is there anything I can do to help debug this?

I think there would be plenty of people interested in an Ultralight integration with JUCE, I'm planning to give it a simple interface and open source it when it's done. The issue is that for audio programming the main thread is fairly sacred, blocking/ non-deterministic operations can very easily cause clicks and pops in the audio.

Would be keen to help if I can, I just can't seem to debug the proprietary part of Ultralight where the error is occuring.