Calling Julia functions from Streamlit applications
I talked about the way to call Julia from multithreaded Python scripts like Streamlit applications. This talk was held on JuliaTokyo #11 on Feb 3 2024. If you're interested in this talk, please refer https://github.com/mrkn/streamlit-julia-call.
Julia solution • We currently develop Pluto.jl notebooks for tasks requiring Julia functionality; however, this approach poses a steeper learning curve for our data analysts. • Streamlit-like way o ff ers a more intuitive and accessible user experience, especially for those with limited programming expertise. • By adopting a Streamlit-like way, we want to democratize access to Julia’s power, ensuring that advanced computational functionality are readily available to all analysts.
are two ways available: pyjulia and juliacall • pyjulia is the counterpart to PyCall.jl • juliacall is the counterpart to PythonCall.jl • Both libraries use Python’s and Julia’s C APIs to bridge the gap between both languages’ runtime environments
PythonCall/juliacall support multithreading • Why? — This is because Julia’s C API isn’t designed to be called from threads that aren’t managed by the Julia’s runtime environment • When Julia is called from a thread that isn’t managed by Julia, a SEGV is triggered
Julia manages the current task information, what a task is running on the current thread, in the TLS (i.e. thread local storage) 2. Julia’s C API retrieves the current task information from the TLS 3. However, a thread outside of Julia doesn’t possess this information in the TLS, resulting in the Julia’s C API receiving a NULL pointer 4. Julia’s C API attempts to access the memory location pointed by the NULL pointer, thus leading to a SEGV
multithreaded Python program • Initialize Julia in the main thread • Avoid calling Julia from non-main threads • Call Julia only from the main thread every time • But how do we delegate calls to Julia to the main thread?
Streamlit consists of the following parts: • Web server: Communicates with the web browsers • Runtime: Acts as the mediator between the web server and sessions • Session Manager: Manages sessions corresponding to clients • Session: Communicates with the individual client • Script Runner: Manages the script execution thread
load 1. Establish a new websocket connection between the client and the server 2. A new session corresponding to the websocket connection is created 3. Create the corresponding script runner for the session 4. The script runner starts a thread to run script running loop 5. The script running loop thread runs the main page script once, then wait the next “script rerun” request from the client
multithreaded application • Streamlit creates a thread for each client to run a page script • The page script may run on multiple threads, simultaneously • We cannot call Julia from Streamlit application without speci fi c workarounds
• The direct approach below fails by SEGV $ cat main.py import streamlit as st from julia import Main st.write(Main.eval("2 + 2")) $ streamlit run main.py --server.port=8080 You can now view your Streamlit app in your browser. Network URL: http://10.110.5.39:8080 External URL: http://18.180.104.211:8080 [98859] signal (11.1): Segmentation fault in expression starting at none:0 Allocations: 2906 (Pool: 2897; Big: 9); GC: 0 Segmentation fault (core dumped)
running on the main thread • Streamlit uses the async event loop to handle messages from the web server • We can retrieve the event loop by: Runtime.instance()._get_async_objs().eventloop • This event loop runs on the main thread • We can utilize this event loop to run a coroutine including Julia calls on the main thread by call_soon_threadsafe method
provides the features to call Julia in Streamlit’s page scripts • The julia_eval function evaluates Julia code in the main thread and returns the result • The julia_display function displays a given Julia object on the Streamlit page using methods such as st.write and st.image, which are chosen based on MIME types supported by the object in a manner similar to IJulia’s approach
Python is explained • Streamlit’s internal mechanism of page execution is illustrated • Our approach to safely calling Julia from the script execution threads of Streamlit is introduced • Some simple example use cases of our approach are demonstrated • Existing known issues are shown
working on a pure Julia Streamlit clone • We attempt to completely reuse Streamlit’s client-side artifacts • We want to make it compatible with the existing Streamlit components • We will talk about this at the next JuliaTokyo even if we fail