mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-11 06:23:08 +00:00
The Emboss text-cut workflow can crash with SIGBUS at a stack-guard page on macOS (and equivalent on Linux) when CGAL's Polygon_mesh_processing::corefine falls back from filtered interval arithmetic (Epick) to exact rational arithmetic (Epeck / mpq_class). On near-degenerate inputs -- coplanar triangles in the projection footprint, very thin font stems, sharp edges or seams under the text -- CGAL's Filtered_predicate_with_state cascade ends up inside Triangulation_2<Projection_traits_3<Epeck>>::march_locate_2D, whose recursive walk plus mpq_class arithmetic frames overflows the worker's 4MB default stack. The fault address sits exactly inside the next thread's guard page, which is the textbook macOS signature. Crash trace (BambuStudio v02.07.00.55, macOS 26.4.1 arm64, embossing text into a model): __gmpn_mul_1 __gmpz_mul / __gmpq_mul CGAL::determinant<mpq_class> Projected_orientation_with_normal_3 Filtered_predicate_with_state::operator() Triangulation_2<...>::orientation Triangulation_2<...>::march_locate_2D Surface_intersection_visitor::triangulate_intersected_faces Polygon_mesh_processing::corefine Slic3r::cut_surface Emboss::cut_surface_to_its Emboss::GenerateTextJob::get_text_mesh PlaterWorker::PlaterJob::process The thread's stack region in the report was exactly 4128K -- the default 4MB plus a small TLS overhead -- and the faulting address hit the adjacent guard page. We have one observed reproducer; the 16 MB value is chosen as 4x defensive headroom over that, not as a measured upper bound. Future heavier emboss inputs may need more. Cumulative cost on a 64-bit target. Slic3r::create_thread has 22 callsites across the codebase. Realistic peak concurrent live count is on the order of 10-15 workers (Plater UI worker, slicing process, FDM- support gizmo, STEP loader, network sync helpers, per-task sender threads in TaskManager up to MaxSendingAtSameTime, per-machine info threads in device-list dialogs, long-lived sync helpers in GUI_App). At 16 MB reserve x ~15 = ~240 MB of address-space commitment in the worst case, which is bounded on any 64-bit target. Resident memory remains proportional to actual stack depth on all three platforms: macOS / Linux mmap the thread stack and defer-commit pages on touch, and Boost.Thread on Win32 passes STACK_SIZE_PARAM_IS_A_RESERVATION to _beginthreadex (verified at libs/thread/src/win32/thread.cpp), so on Windows the bumped value is the reserve, not the initial commit. The 32-bit branch of the previous (sizeof(void*) == 4) ternary is removed: BambuStudio doesn't ship a 32-bit build today, and the literal makes the value easier to read at the callsite. (cherry picked from commit e150b502b3d2afc98b83dcc9e5720e998f9eb79a) Co-authored-by: Abdel Gomez-Perez <nabdel07@icloud.com>
78 lines
3.4 KiB
C++
78 lines
3.4 KiB
C++
#ifndef GUI_THREAD_HPP
|
|
#define GUI_THREAD_HPP
|
|
|
|
#include <utility>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <boost/thread.hpp>
|
|
|
|
namespace Slic3r {
|
|
|
|
// Set / get thread name.
|
|
// Returns false if the API is not supported.
|
|
//
|
|
// It is a good idea to name the main thread before spawning children threads, because dynamic linking is used on Windows 10
|
|
// to initialize Get/SetThreadDescription functions, which is not thread safe.
|
|
//
|
|
// pthread_setname_np supports maximum 15 character thread names! (16th character is the null terminator)
|
|
//
|
|
// Methods taking the thread as an argument are not supported by OSX.
|
|
// Naming threads is only supported on newer Windows 10.
|
|
|
|
bool set_thread_name(std::thread &thread, const char *thread_name);
|
|
inline bool set_thread_name(std::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
|
|
bool set_thread_name(boost::thread &thread, const char *thread_name);
|
|
inline bool set_thread_name(boost::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
|
|
bool set_current_thread_name(const char *thread_name);
|
|
inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); }
|
|
|
|
// To be called at the start of the application to save the current thread ID as the main (UI) thread ID.
|
|
void save_main_thread_id();
|
|
// Retrieve the cached main (UI) thread ID.
|
|
boost::thread::id get_main_thread_id();
|
|
// Checks whether the main (UI) thread is active.
|
|
bool is_main_thread_active();
|
|
|
|
// Returns nullopt if not supported.
|
|
// Not supported by OSX.
|
|
// Naming threads is only supported on newer Windows 10.
|
|
std::optional<std::string> get_current_thread_name();
|
|
|
|
// To be called somewhere before the TBB threads are spinned for the first time, to
|
|
// give them names recognizible in the debugger.
|
|
// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
|
|
void name_tbb_thread_pool_threads_set_locale();
|
|
|
|
template<class Fn>
|
|
inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)
|
|
{
|
|
// Stack size for our worker threads. Originally duplicated TBB's pool
|
|
// default (4 MB), but the Emboss text-cut path calls into CGAL's
|
|
// Polygon_mesh_processing::corefine, which falls back from filtered
|
|
// interval arithmetic to exact rational arithmetic (mpq_class) on
|
|
// near-degenerate input, and the constrained 2D triangulation walker
|
|
// (Triangulation_2::march_locate_2D) can recurse deeply enough to
|
|
// exceed 4 MB on real models -- producing a SIGBUS at the next thread's
|
|
// stack guard page on macOS / Linux.
|
|
//
|
|
// 16 MB chosen as 4x defensive headroom over the observed crash
|
|
// threshold (n=1 reproducer at exactly 4 MB on macOS arm64). All three
|
|
// platforms defer-commit reserved stack pages: macOS / Linux mmap the
|
|
// stack and only fault in pages on touch; Boost.Thread on Win32 passes
|
|
// STACK_SIZE_PARAM_IS_A_RESERVATION to _beginthreadex, so the value is
|
|
// a reserve, not the initial commit. Resident memory therefore stays
|
|
// proportional to actual stack depth on every target.
|
|
attrs.set_stack_size(16 * 1024 * 1024);
|
|
return boost::thread{attrs, std::forward<Fn>(fn)};
|
|
}
|
|
|
|
template<class Fn> inline boost::thread create_thread(Fn &&fn)
|
|
{
|
|
boost::thread::attributes attrs;
|
|
return create_thread(attrs, std::forward<Fn>(fn));
|
|
}
|
|
|
|
}
|
|
|
|
#endif // GUI_THREAD_HPP
|