Skip to content

Refactor threads to use daemon threads and Event-based timing#219

Merged
hemna merged 13 commits intomasterfrom
feature-daemon-threads-event-refactor
Mar 24, 2026
Merged

Refactor threads to use daemon threads and Event-based timing#219
hemna merged 13 commits intomasterfrom
feature-daemon-threads-event-refactor

Conversation

@hemna
Copy link
Copy Markdown
Collaborator

@hemna hemna commented Mar 24, 2026

Summary

This PR refactors all APRSD thread classes to:

  • Use daemon threads by default (with explicit non-daemon for critical I/O threads)
  • Replace counter-based sleep patterns with threading.Event() for interruptible waits
  • Enable faster, cleaner shutdown

Changes

Base Class (aprsd/threads/aprsd.py)

  • Add daemon = True class attribute (subclasses override to False)
  • Add period = 1 class attribute for wait interval
  • Replace thread_stop boolean with _shutdown_event (threading.Event)
  • Add wait() method for interruptible sleeps
  • Add join_non_daemon() to APRSDThreadList for graceful shutdown

Non-Daemon Threads (3)

These threads are marked daemon = False for graceful shutdown:

  • PacketSendSchedulerThread - manages packet send queue
  • AckSendSchedulerThread - manages ACK send queue
  • APRSDStatsStoreThread - writes stats to disk

Thread Migrations

All threads migrated from counter-based timing to period-based:

  • KeepAliveThread - period=60
  • APRSDStatsStoreThread - period=10
  • APRSDPushStatsThread - period from config
  • StatsLogThread - period=10
  • APRSDRXThread - interruptible error recovery
  • APRSDFilterThread - queue timeout as wait
  • BeaconSendThread - period from config
  • APRSRegistryThread - period from config

Signal Handler

  • Replace time.sleep(1.5) with join_non_daemon(timeout=5.0)

Benefits

  • Faster shutdown: Threads respond immediately to shutdown signal instead of waiting for time.sleep() to finish
  • Cleaner code: No more counter math (loop_count % 60 == 0)
  • Proper daemon semantics: Program can exit cleanly; critical threads get graceful shutdown

Testing

  • All 679 tests pass
  • Linting passes (ruff check + format)

Breaking Changes

  • thread_stop boolean replaced with _shutdown_event
  • Code checking thread.thread_stop directly must use thread._shutdown_event.is_set()

hemna added 12 commits March 24, 2026 13:22
…hread

- Add daemon=True class attribute (subclasses override to False)
- Add period=1 class attribute for wait interval
- Replace thread_stop boolean with _shutdown_event (threading.Event)
- Add wait() method for interruptible sleeps
- Update tests for new Event-based API

BREAKING: thread_stop boolean replaced with _shutdown_event.
Code checking thread.thread_stop directly must use thread._shutdown_event.is_set()
Allows graceful shutdown by waiting for non-daemon threads to complete
while allowing daemon threads to be terminated immediately.
- APRSDRXThread: Replace time.sleep with self.wait for interruptible waits
- APRSDRXThread.stop(): Use _shutdown_event.set() instead of thread_stop
- APRSDRXThread: Error recovery waits check for shutdown signal
- APRSDFilterThread: Use queue timeout with self.period for interruptible wait
- Remove unused time import
- Update tests to use new Event-based API
- PacketSendSchedulerThread: Add daemon=False, replace time.sleep with self.wait
- AckSendSchedulerThread: Add daemon=False, replace time.sleep with self.wait
- SendPacketThread: Replace time.sleep with self.wait, remove manual loop_count
- SendAckThread: Replace time.sleep with self.wait, remove manual loop_count
- BeaconSendThread: Set self.period=CONF.beacon_interval, remove counter-based
  conditional, replace time.sleep with self.wait, remove _loop_cnt tracking
- Update tests to use new Event-based API
- Set self.period=CONF.aprs_registry.frequency_seconds in __init__
- Remove counter-based conditional (loop every N seconds pattern)
- Replace time.sleep(1) with self.wait()
- Remove _loop_cnt tracking (use inherited loop_count from base)
- Remove unused time import
- Replace time.sleep(1.5) with thread_list.join_non_daemon(timeout=5.0)
- Remove unused import time since time.sleep is no longer used
- Remove outdated commented-out code
- Improve log message (removed '10 seconds' reference)
- Add -> bool return type annotation to abstract loop() method
- Replace 'from typing import List' with built-in list[] (Python 3.9+)
@hemna hemna force-pushed the feature-daemon-threads-event-refactor branch from 08636f5 to 8d8648e Compare March 24, 2026 17:23
@hemna hemna merged commit f2526ef into master Mar 24, 2026
12 checks passed
@hemna hemna deleted the feature-daemon-threads-event-refactor branch March 24, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant