diff --git a/python/fasttext_module/fasttext/tests/test_autotune_errors.py b/python/fasttext_module/fasttext/tests/test_autotune_errors.py new file mode 100644 index 0000000..2d12864 --- /dev/null +++ b/python/fasttext_module/fasttext/tests/test_autotune_errors.py @@ -0,0 +1,26 @@ +# SPDX-FileContributor: Arthit Suriyawongkul +# SPDX-FileCopyrightText: 2026-present, fasttext-community +# SPDX-FileType: SOURCE +# SPDX-License-Identifier: MIT + +"""Autotune errors must raise, not terminate the process.""" + +import pytest + +from .helpers import build_supervised_model, get_random_data + + +def test_autotune_error_raises(tmp_path): + data = get_random_data(3000, max_vocab_size=600) + valid = tmp_path / "valid.txt" + valid.write_text("".join(f"__label__{line}\n" for line in data)) + kwargs = { + # thread=12: thread <= 10 leaves the input matrix partly uninitialized. + "thread": 12, + "verbose": 0, + "autotuneValidationFile": str(valid), + "autotuneMetric": "f1:__label__missing", # fails after the first trial + "autotuneDuration": 60, # long enough for slow runners to reach it + } + with pytest.raises(RuntimeError, match="Unknown autotune metric label"): + build_supervised_model(data, kwargs) diff --git a/src/autotune.cc b/src/autotune.cc index 567731b..6ec44fb 100644 --- a/src/autotune.cc +++ b/src/autotune.cc @@ -215,6 +215,15 @@ Autotune::Autotune(const std::shared_ptr& fastText) strategy_(), timer_() {} +Autotune::~Autotune() noexcept { + // An exception leaving train() skips its timer join, and destroying a + // joinable std::thread calls std::terminate(). + if (timer_.joinable()) { + continueTraining_ = false; + timer_.join(); + } +} + void Autotune::printInfo(double maxDuration) { double progress = elapsed_ * 100 / maxDuration; progress = std::min(progress, 100.0); diff --git a/src/autotune.h b/src/autotune.h index 8b300ae..73e1405 100644 --- a/src/autotune.h +++ b/src/autotune.h @@ -81,7 +81,7 @@ class Autotune { Autotune(Autotune&&) = delete; Autotune& operator=(const Autotune&) = delete; Autotune& operator=(Autotune&&) = delete; - ~Autotune() noexcept = default; + ~Autotune() noexcept; void train(const Args& args); };