From b8ee2fc78087b7fbab0607bd7ccf01c400c0dcb6 Mon Sep 17 00:00:00 2001 From: wangwenjun <42320079+wangwenjunfromlanzhou@users.noreply.github.com> Date: Tue, 11 Aug 2026 22:53:55 +0800 Subject: [PATCH 1/2] fix(windows): run skippy-model-package body on an 8 MB stack thread Windows main threads get a 1 MB stack (PE default). On a 5 GB Qwen3-8B-Q4_K_M.gguf, sha256 hashing the source model plus FFI slice writing overflows the stack before any output is produced: thread 'main' (15404) has overflowed its stack The sibling mesh-llm binary already runs its workers at 8 MB (crates/mesh-llm/src/main.rs:14) for the same reason, but that rationale never propagated to skippy-model-package, which runs the heavy work directly on the main thread. Move the body into a spawned thread with an 8 MB stack and resume_unwind any panic so the original payload propagates to the main thread instead of a generic join error. --- crates/skippy-model-package/src/main.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/skippy-model-package/src/main.rs b/crates/skippy-model-package/src/main.rs index 954a23ab1b..f530e1f147 100644 --- a/crates/skippy-model-package/src/main.rs +++ b/crates/skippy-model-package/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Parser; mod cli; @@ -38,9 +38,26 @@ fn prepare_model_download_directories() { unsafe { prepared.apply_to_process_environment() }; } +// ponytail: main runs on a child thread because the Windows main thread has a +// 1 MB stack. sha256 over a multi-GB GGUF plus FFI slice writing blows that +// stack in debug builds. 8 MB matches the mesh-llm runtime default. If a real +// recursion sink appears, raise this or fix the recursion — don't go lower. +const MAIN_STACK_SIZE: usize = 8 * 1024 * 1024; + fn main() -> Result<()> { prepare_model_download_directories(); let args = Args::parse(); + + let handle = std::thread::Builder::new() + .stack_size(MAIN_STACK_SIZE) + .spawn(move || run(args)) + .context("spawn skippy-model-package worker thread")?; + handle.join().unwrap_or_else(|panic| { + std::panic::resume_unwind(panic); + }) +} + +fn run(args: Args) -> Result<()> { match args.command { Command::Inspect { model } => inspect::inspect(model), Command::Plan { model, stages } => plan::build_plan(&model, stages).and_then(|output| { From 23499b4b61da712e37d7b82b0914062222a69782 Mon Sep 17 00:00:00 2001 From: wangwenjun <42320079+wangwenjunfromlanzhou@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:18:16 +1000 Subject: [PATCH 2/2] fix(windows): use 64-bit seek when copying tensor data over 4 GB Rebased onto the reorganized llama.cpp patch queue: the 64-bit seek fix now lives in 0005-Add-Skippy-model-lifecycle-and-package-support.patch (src/skippy/model_package.cpp), where skippy_copy_source_tensors moved, instead of the retired 0001-Add-Skippy-ABI-and-package-writer-foundation.patch. Also hardens the offset per CodeRabbit review: computes the absolute offset without unsigned wraparound and rejects values that exceed the selected platform seek API's signed range before seeking, so an out-of-range offset fails loudly instead of wrapping into a wrong read position. Co-authored-by: Michael Neale Signed-off-by: Michael Neale --- ...-model-lifecycle-and-package-support.patch | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch b/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch index 7051b42da9..9a43f2d971 100644 --- a/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch +++ b/third_party/llama.cpp/patches/0005-Add-Skippy-model-lifecycle-and-package-support.patch @@ -1445,10 +1445,10 @@ index 000000000..ee1e43035 +} // extern "C" diff --git a/src/skippy/model_package.cpp b/src/skippy/model_package.cpp new file mode 100644 -index 000000000..3d199add8 +index 000000000..65d15b717 --- /dev/null +++ b/src/skippy/model_package.cpp -@@ -0,0 +1,705 @@ +@@ -0,0 +1,727 @@ +#include "skippy/model_package.h" +#include "skippy/errors.h" + @@ -1812,8 +1812,30 @@ index 000000000..3d199add8 + break; + } + -+ const size_t source_data_offset = gguf_get_data_offset(item.source->ctx); -+ if (std::fseek(input, static_cast(source_data_offset + item.tensor->offset), SEEK_SET) != 0) { ++ // std::fseek takes a long, which is 32-bit on Win32 MSVC, so GGUF files ++ // over 4 GB wrap and tensor reads land at the wrong offset. Seek with a ++ // 64-bit API where available, and validate the absolute offset first so ++ // an out-of-range value fails loudly instead of wrapping. ++ static_assert(std::numeric_limits::digits <= std::numeric_limits::digits, ++ "size_t wider than uint64_t is not supported here"); ++ const uint64_t source_data_offset = ++ static_cast(gguf_get_data_offset(item.source->ctx)); ++ const uint64_t tensor_offset = static_cast(item.tensor->offset); ++ bool seek_ok = false; ++ if (tensor_offset <= (std::numeric_limits::max)() - source_data_offset) { ++ const uint64_t absolute_offset = source_data_offset + tensor_offset; ++#if defined(_MSC_VER) ++ seek_ok = absolute_offset <= static_cast((std::numeric_limits<__int64>::max)()) && ++ _fseeki64(input, static_cast<__int64>(absolute_offset), SEEK_SET) == 0; ++#elif defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 ++ seek_ok = absolute_offset <= static_cast((std::numeric_limits::max)()) && ++ fseeko(input, static_cast(absolute_offset), SEEK_SET) == 0; ++#else ++ seek_ok = absolute_offset <= static_cast((std::numeric_limits::max)()) && ++ std::fseek(input, static_cast(absolute_offset), SEEK_SET) == 0; ++#endif ++ } ++ if (!seek_ok) { + std::fclose(input); + ok = false; + break;