Wait for native thread exit outside GUI and cover FIN drain in tests
This commit is contained in:
1 parent
0f5a5580c5
commit
688d559787
2 files changed
+39
-37
No files matched your search
@@ -17,6 +17,9 @@
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <cmath>
|
||||
#ifdef TEST_NULL_AUDIO
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include "../src/live_video_encoder.h"
|
||||
#include "../src/stream_publish_engine.h"
|
||||
#include "../src/stream_publish_window.h"
|
||||
@@ -61,20 +64,18 @@ static std::atomic<unsigned> softwareOpens{0}, hardwareOpens{0};
|
||||
static std::atomic<bool> failHardware{false};
|
||||
static std::atomic<bool> requireGuiAtThreadExit{false};
|
||||
static std::atomic<unsigned> threadExitArmed{0}, threadExitCompleted{0};
|
||||
struct GuiThreadExit {
|
||||
bool armed = false;
|
||||
~GuiThreadExit() {
|
||||
if (!armed) return;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
QMetaObject::invokeMethod(qApp, [] { threadExitCompleted++; }, Qt::BlockingQueuedConnection);
|
||||
}
|
||||
};
|
||||
static pthread_key_t guiThreadExitKey;
|
||||
static void guiThreadExit(void*) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
QMetaObject::invokeMethod(qApp, [] { threadExitCompleted++; }, Qt::BlockingQueuedConnection);
|
||||
}
|
||||
extern "C" int __real_avcodec_open2(AVCodecContext*, const AVCodec*, AVDictionary**);
|
||||
extern "C" int __wrap_avcodec_open2(AVCodecContext* context, const AVCodec* codec, AVDictionary** options) {
|
||||
if (av_codec_is_encoder(codec)) {
|
||||
if (requireGuiAtThreadExit && QThread::currentThread() != qApp->thread()) {
|
||||
thread_local GuiThreadExit cleanup;
|
||||
if (!cleanup.armed) { cleanup.armed = true; threadExitArmed++; }
|
||||
if (!pthread_getspecific(guiThreadExitKey)) {
|
||||
pthread_setspecific(guiThreadExitKey, &guiThreadExitKey); threadExitArmed++;
|
||||
}
|
||||
}
|
||||
QByteArray name(codec->name);
|
||||
bool hardware = name.endsWith("_nvenc") || name.endsWith("_qsv") || name.endsWith("_amf") || name.endsWith("_vaapi");
|
||||
@@ -538,14 +539,18 @@ private slots:
|
||||
if (!qEnvironmentVariableIsSet("UTUN_TEST_SCREEN_CAPTURE")) QSKIP("Run under isolated Xvfb");
|
||||
QFETCH(bool, destroy);
|
||||
requireGuiAtThreadExit = true; failHardware = true; threadExitArmed = threadExitCompleted = 0;
|
||||
auto reset = qScopeGuard([] { requireGuiAtThreadExit = false; failHardware = false; });
|
||||
QCOMPARE(pthread_key_create(&guiThreadExitKey, guiThreadExit), 0);
|
||||
auto reset = qScopeGuard([] { requireGuiAtThreadExit = false; failHardware = false; pthread_key_delete(guiThreadExitKey); });
|
||||
auto engine = std::make_unique<StreamPublishEngine>();
|
||||
uint64_t id = 0;
|
||||
connect(engine.get(), &StreamPublishEngine::telemetryChanged, this, [&](const stream_publisher_status& status) { id = status.id; });
|
||||
connect(engine.get(), &StreamPublishEngine::softwareEncodingRequested, engine.get(),
|
||||
[&](uint64_t generation, const QString&, const QString&) { engine->answerSoftwareEncoding(generation, true); });
|
||||
StreamPublishConfig config; config.audio = false; config.video.width = 320; config.video.height = 180; config.video.fps = 15;
|
||||
for (const auto& source : engine->sources()) if (source.kind == StreamVideoSource::Screen) { config.source = source; break; }
|
||||
QVERIFY(!config.source.id.isEmpty()); QVERIFY(engine->start(group, config));
|
||||
QTRY_COMPARE_WITH_TIMEOUT(threadExitArmed.load(), 1u, 4000);
|
||||
QTRY_VERIFY(id != 0);
|
||||
unsigned heartbeats = 0; QTimer heartbeat;
|
||||
connect(&heartbeat, &QTimer::timeout, this, [&] { heartbeats++; }); heartbeat.start(10);
|
||||
QElapsedTimer elapsed; elapsed.start();
|
||||
@@ -559,6 +564,7 @@ private slots:
|
||||
QCOMPARE(threadExitCompleted.load(), 1u); QVERIFY(heartbeats >= 5);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "[PASS] publisher stop destroy=%d GUI-dependent TLS cleanup completed heartbeat=%u elapsed_ms=%lld",
|
||||
destroy, heartbeats, (long long)elapsed.elapsed());
|
||||
QTRY_VERIFY_WITH_TIMEOUT(ended(id), 4000); // Core продолжает FIN drain после удаления desktop engine.
|
||||
#else
|
||||
QSKIP("Capture test requires FFmpeg and null audio wrappers");
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user