derektan commited on
Commit
212e8c3
Β·
1 Parent(s): 2357f9d

Kills thread when changed tab

Browse files
Files changed (1) hide show
  1. app.py +54 -7
app.py CHANGED
@@ -49,6 +49,24 @@ def _stop_thread(thread: threading.Thread):
49
  # If it returned >1, cleanup and fail safe
50
  ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  # CHANGE ME!
53
  POLL_INTERVAL = 1.0 # For visualization
54
 
@@ -206,6 +224,11 @@ def process_search_tta(
206
  thread_tta.start()
207
  thread_no.start()
208
 
 
 
 
 
 
209
  sent_tta: set[str] = set()
210
  sent_no: set[str] = set()
211
  last_tta = None
@@ -278,6 +301,13 @@ def process_search_tta(
278
  _stop_thread(th)
279
  th.join(timeout=1)
280
 
 
 
 
 
 
 
 
281
  # One last scan after both threads have finished to catch any frame
282
  # that may have been written just before termination but after the last
283
  # polling iteration.
@@ -441,12 +471,29 @@ with gr.Blocks(title="Search-TTA (Simplified)", theme=gr.themes.Base()) as demo:
441
  # if def main
442
  if __name__ == "__main__":
443
 
444
- # Combine backup and current demos into a single tabbed interface (Backup first).
445
  from app_multimodal_inference import demo as multimodal_demo
446
 
447
- combined_demo = gr.TabbedInterface(
448
- [demo, multimodal_demo],
449
- ["Search-TTA", "Multimodal Inference"]
450
- )
451
- combined_demo.queue(max_size=15)
452
- combined_demo.launch(share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  # If it returned >1, cleanup and fail safe
50
  ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
51
 
52
+ # ──────────── Thread Registry for Cleanup on Tab Switch ─────────────
53
+ _running_threads: list[threading.Thread] = []
54
+ _running_threads_lock = threading.Lock()
55
+
56
+ def _register_thread(th: threading.Thread):
57
+ """Record a newly started worker thread so we can later cancel it."""
58
+ with _running_threads_lock:
59
+ _running_threads.append(th)
60
+
61
+ def _kill_running_threads():
62
+ """Stop all worker threads that are still alive."""
63
+ with _running_threads_lock:
64
+ for t in list(_running_threads):
65
+ _stop_thread(t)
66
+ # Clear list regardless of alive status
67
+ _running_threads.clear()
68
+
69
+
70
  # CHANGE ME!
71
  POLL_INTERVAL = 1.0 # For visualization
72
 
 
224
  thread_tta.start()
225
  thread_no.start()
226
 
227
+ # Register threads so they can be cancelled when user switches tabs
228
+ _register_thread(thread_tta)
229
+ _register_thread(thread_no)
230
+
231
+
232
  sent_tta: set[str] = set()
233
  sent_no: set[str] = set()
234
  last_tta = None
 
301
  _stop_thread(th)
302
  th.join(timeout=1)
303
 
304
+ # Remove finished threads from global registry
305
+ with _running_threads_lock:
306
+ if thread_tta in _running_threads:
307
+ _running_threads.remove(thread_tta)
308
+ if thread_no in _running_threads:
309
+ _running_threads.remove(thread_no)
310
+
311
  # One last scan after both threads have finished to catch any frame
312
  # that may have been written just before termination but after the last
313
  # polling iteration.
 
471
  # if def main
472
  if __name__ == "__main__":
473
 
474
+ # Build UI with explicit Tabs so we can detect tab selection and clean up
475
  from app_multimodal_inference import demo as multimodal_demo
476
 
477
+ with gr.Blocks() as root:
478
+ with gr.Tabs() as tabs:
479
+ with gr.TabItem("Search-TTA"):
480
+ demo.render()
481
+ with gr.TabItem("Multimodal Inference"):
482
+ multimodal_demo.render()
483
+
484
+ # Hidden textbox purely to satisfy Gradio's need for an output component.
485
+ _cleanup_status = gr.Textbox(visible=False)
486
+
487
+ outputs_on_tab = [_cleanup_status]
488
+
489
+ def _on_tab_change(evt: gr.SelectData):
490
+ # evt.value contains the name of the newly-selected tab.
491
+ if evt.value == "Multimodal Inference":
492
+ _kill_running_threads()
493
+ return "Stopped running Search-TTA threads."
494
+ return ""
495
+
496
+ tabs.select(_on_tab_change, outputs=outputs_on_tab)
497
+
498
+ root.queue(max_size=15)
499
+ root.launch(share=True)