LCOV - code coverage report
Current view: top level - AP_HAL_SITL - Scheduler.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 117 167 70.1 %
Date: 2026-09-09 11:02:08 Functions: 17 19 89.5 %

          Line data    Source code
       1             : #include <AP_HAL/AP_HAL.h>
       2             : 
       3             : #include "AP_HAL_SITL.h"
       4             : #include <AP_HAL_SITL/I2CDevice.h>
       5             : #include "Scheduler.h"
       6             : #include "UARTDriver.h"
       7             : #include <sys/time.h>
       8             : #include <fenv.h>
       9             : #include <AP_BoardConfig/AP_BoardConfig.h>
      10             : #if defined (__clang__) || (defined (__APPLE__) && defined (__MACH__)) || defined (__OpenBSD__)
      11             : #include <stdlib.h>
      12             : #else
      13             : #include <malloc.h>
      14             : #endif
      15             : #include <AP_RCProtocol/AP_RCProtocol.h>
      16             : #ifdef UBSAN_ENABLED
      17             : #include <fcntl.h>
      18             : #include <sanitizer/asan_interface.h>
      19             : #endif
      20             : 
      21             : using namespace HALSITL;
      22             : 
      23             : extern const AP_HAL::HAL& hal;
      24             : 
      25             : #ifndef SITL_STACK_CHECKING_ENABLED
      26             : //#define SITL_STACK_CHECKING_ENABLED !defined(__CYGWIN__) && !defined(__CYGWIN64__)
      27             : // stack checking is disabled until the memory corruption issues are
      28             : // fixed with pthread_attr_setstack.  These may be due to
      29             : // changes in the way guard pages are handled.
      30             : #define SITL_STACK_CHECKING_ENABLED 0
      31             : #endif
      32             : 
      33             : AP_HAL::Proc Scheduler::_failsafe = nullptr;
      34             : 
      35             : AP_HAL::MemberProc Scheduler::_timer_proc[SITL_SCHEDULER_MAX_TIMER_PROCS] = {nullptr};
      36             : uint8_t Scheduler::_num_timer_procs = 0;
      37             : bool Scheduler::_in_timer_proc = false;
      38             : 
      39             : AP_HAL::MemberProc Scheduler::_io_proc[SITL_SCHEDULER_MAX_TIMER_PROCS] = {nullptr};
      40             : uint8_t Scheduler::_num_io_procs = 0;
      41             : bool Scheduler::_in_io_proc = false;
      42             : bool Scheduler::_should_exit = false;
      43             : 
      44             : bool Scheduler::_in_semaphore_take_wait = false;
      45             : 
      46             : Scheduler::thread_attr *Scheduler::threads;
      47             : HAL_Semaphore Scheduler::_thread_sem;
      48             : 
      49        2145 : Scheduler::Scheduler(SITL_State *sitlState) :
      50        2145 :     _sitlState(sitlState),
      51        2145 :     _stopped_clock_usec(0)
      52             : {
      53        2145 : }
      54             : 
      55             : #ifdef UBSAN_ENABLED
      56             : /*
      57             :   catch ubsan errors and append to a log file
      58             :  */
      59             : extern "C" {
      60             : void __ubsan_get_current_report_data(const char **OutIssueKind,
      61             :                                      const char **OutMessage,
      62             :                                      const char **OutFilename, unsigned *OutLine,
      63             :                                      unsigned *OutCol, char **OutMemoryAddr);
      64             : 
      65             : void __ubsan_on_report();
      66             : void __ubsan_on_report()
      67             : {
      68             :     static int fd = -1;
      69             :     if (fd == -1) {
      70             :         const char *ubsan_log_path = getenv("UBSAN_LOG_PATH");
      71             :         if (ubsan_log_path == nullptr) {
      72             :             ubsan_log_path = "ubsan.log";
      73             :         }
      74             :         if (ubsan_log_path != nullptr) {
      75             :             fd = open(ubsan_log_path, O_APPEND|O_CREAT|O_WRONLY, 0644);
      76             :         }
      77             :     }
      78             :     if (fd != -1) {
      79             :         const char *OutIssueKind = nullptr;
      80             :         const char *OutMessage = nullptr;
      81             :         const char *OutFilename = nullptr;
      82             :         unsigned OutLine=0;
      83             :         unsigned OutCol=0;
      84             :         char *OutMemoryAddr=nullptr;
      85             :         __ubsan_get_current_report_data(&OutIssueKind, &OutMessage, &OutFilename,
      86             :                                         &OutLine, &OutCol, &OutMemoryAddr);
      87             :         dprintf(fd, "ubsan error: %s:%u:%u %s:%s\n",
      88             :                 OutFilename, OutLine, OutCol,
      89             :                 OutIssueKind, OutMessage);
      90             :     }
      91             : }
      92             : }
      93             : #endif
      94             : 
      95        2094 : void Scheduler::init()
      96             : {
      97        2094 :     _main_ctx = pthread_self();
      98        2094 : }
      99             : 
     100  5713279994 : bool Scheduler::in_main_thread() const
     101             : {
     102  5713279994 :     if (!_in_timer_proc && !_in_io_proc && pthread_self() == _main_ctx) {
     103  4221205145 :         return true;
     104             :     }
     105  1492074849 :     return false;
     106             : }
     107             : 
     108             : /*
     109             :  * semaphore_wait_hack_required - possibly move time input step
     110             :  * forward even if we are currently pretending to be the IO or timer
     111             :  * threads.
     112             :  *
     113             :  * Without this, if another thread has taken a semaphore (e.g. the
     114             :  * Object Avoidance thread), and an "IO process" tries to take that
     115             :  * semaphore with a timeout specified, then we end up not advancing
     116             :  * time (due to the logic in SITL_State::wait_clock) and thus taking
     117             :  * the semaphore never times out - meaning we essentially deadlock.
     118             :  */
     119   109438397 : bool Scheduler::semaphore_wait_hack_required() const
     120             : {
     121   109438397 :     if (pthread_self() != _main_ctx) {
     122             :         // only the main thread ever moves stuff forwards
     123   109438862 :         return false;
     124             :     }
     125             : 
     126           0 :     return _in_semaphore_take_wait;
     127             : }
     128             : 
     129   189696991 : void Scheduler::delay_microseconds(uint16_t usec)
     130             : {
     131   189696991 :     if (_sitlState->_sitl == nullptr) {
     132             :         // this allows examples to run
     133        2000 :         hal.scheduler->stop_clock(AP_HAL::micros64()+usec);
     134        2000 :         return;
     135             :     }
     136   189694991 :     uint64_t start = AP_HAL::micros64();
     137             :     do {
     138   337688757 :         uint64_t dtime = AP_HAL::micros64() - start;
     139   337606070 :         if (dtime >= usec) {
     140   189677286 :             break;
     141             :         }
     142   147928784 :         _sitlState->wait_clock(start + usec);
     143   148013669 :     } while (true);
     144             : }
     145             : 
     146     1916382 : void Scheduler::delay(uint16_t ms)
     147             : {
     148     1916382 :     uint32_t start = AP_HAL::millis();
     149     1916381 :     uint32_t now = start;
     150             :     do {
     151    10535173 :         delay_microseconds(1000);
     152    10535141 :         if (_min_delay_cb_ms <= (ms - (now - start))) {
     153     8299869 :             if (in_main_thread()) {
     154     2619247 :                 call_delay_cb();
     155             :             }
     156             :         }
     157    10535126 :         now = AP_HAL::millis();
     158    10534935 :     } while (now - start < ms);
     159     1916143 : }
     160             : 
     161       14638 : void Scheduler::register_timer_process(AP_HAL::MemberProc proc)
     162             : {
     163       58537 :     for (uint8_t i = 0; i < _num_timer_procs; i++) {
     164       43899 :         if (_timer_proc[i] == proc) {
     165           0 :             return;
     166             :         }
     167             :     }
     168             : 
     169       14638 :     if (_num_timer_procs < SITL_SCHEDULER_MAX_TIMER_PROCS) {
     170       14638 :         _timer_proc[_num_timer_procs] = proc;
     171       14638 :         _num_timer_procs++;
     172             :     }
     173             : }
     174             : 
     175       11336 : void Scheduler::register_io_process(AP_HAL::MemberProc proc)
     176             : {
     177       36746 :     for (uint8_t i = 0; i < _num_io_procs; i++) {
     178       25410 :         if (_io_proc[i] == proc) {
     179           0 :             return;
     180             :         }
     181             :     }
     182             : 
     183       11336 :     if (_num_io_procs < SITL_SCHEDULER_MAX_TIMER_PROCS) {
     184       11336 :         _io_proc[_num_io_procs] = proc;
     185       11336 :         _num_io_procs++;
     186             :     }
     187             : }
     188             : 
     189        2059 : void Scheduler::register_timer_failsafe(AP_HAL::Proc failsafe, uint32_t period_us)
     190             : {
     191        2059 :     _failsafe = failsafe;
     192        2059 : }
     193             : 
     194        2086 : void Scheduler::set_system_initialized() {
     195        2086 :     if (_initialized) {
     196           0 :         AP_HAL::panic(
     197             :             "PANIC: scheduler system initialized called more than once");
     198             :     }
     199        2086 :     int exceptions = FE_OVERFLOW | FE_DIVBYZERO;
     200             : #ifndef __i386__
     201             :     // i386 with gcc doesn't work with FE_INVALID
     202        2086 :     exceptions |= FE_INVALID;
     203             : #endif
     204             : #if !defined(HAL_BUILD_AP_PERIPH)
     205        2086 :     if (_sitlState->_sitl == nullptr || _sitlState->_sitl->float_exception) {
     206        2086 :         feenableexcept(exceptions);
     207             :     } else {
     208           0 :         feclearexcept(exceptions);
     209             :     }
     210             : #else
     211             :     feclearexcept(exceptions);
     212             : #endif
     213        2086 :     _initialized = true;
     214        2086 : }
     215             : 
     216   187268324 : void Scheduler::sitl_end_atomic() {
     217   187268324 :     if (_nested_atomic_ctr == 0) {
     218           0 :         hal.serial(0)->printf("NESTED ATOMIC ERROR\n");
     219             :     } else {
     220   187268324 :         _nested_atomic_ctr--;
     221             :     }
     222   187268324 : }
     223             : 
     224        1639 : void Scheduler::reboot(bool hold_in_bootloader)
     225             : {
     226        1639 :     HAL_SITL::actually_reboot();
     227           0 :     abort();
     228             : }
     229             : 
     230   187268325 : void Scheduler::_run_timer_procs()
     231             : {
     232   187268325 :     if (_in_timer_proc) {
     233             :         // the timer calls took longer than the period of the
     234             :         // timer. This is bad, and may indicate a serious
     235             :         // driver failure. We can't just call the drivers
     236             :         // again, as we could run out of stack. So we only
     237             :         // call the _failsafe call. It's job is to detect if
     238             :         // the drivers or the main loop are indeed dead and to
     239             :         // activate whatever failsafe it thinks may help if
     240             :         // need be.  We assume the failsafe code can't
     241             :         // block. If it does then we will recurse and die when
     242             :         // we run out of stack
     243           0 :         if (_failsafe != nullptr) {
     244           0 :             _failsafe();
     245             :         }
     246           0 :         return;
     247             :     }
     248   187268325 :     _in_timer_proc = true;
     249             : 
     250             :     // now call the timer based drivers
     251  1489327529 :     for (int i = 0; i < _num_timer_procs; i++) {
     252  1302059204 :         if (_timer_proc[i]) {
     253  1302059204 :             _timer_proc[i]();
     254             :         }
     255             :     }
     256             : 
     257             :     // and the failsafe, if one is setup
     258   187268325 :     if (_failsafe != nullptr) {
     259   183880867 :         _failsafe();
     260             :     }
     261             : 
     262   187268325 :     _in_timer_proc = false;
     263             : }
     264             : 
     265   243357862 : void Scheduler::_run_io_procs()
     266             : {
     267   243357862 :     if (_in_io_proc) {
     268           0 :         return;
     269             :     }
     270   243357862 :     _in_io_proc = true;
     271             : 
     272             :     // now call the IO based drivers
     273  1555468512 :     for (int i = 0; i < _num_io_procs; i++) {
     274  1312110650 :         if (_io_proc[i]) {
     275  1312110650 :             _io_proc[i]();
     276             :         }
     277             :     }
     278             : 
     279   243357862 :     _in_io_proc = false;
     280             : 
     281  2676936472 :     for (uint8_t i=0; i<hal.num_serial; i++) {
     282  2433578611 :         hal.serial(i)->_timer_tick();
     283             :     }
     284   243357861 :     hal.storage->_timer_tick();
     285             : 
     286             :     // in lieu of a thread-per-bus:
     287   243357861 :     ((HALSITL::I2CDeviceManager*)(hal.i2c_mgr))->_timer_tick();
     288             : 
     289             : #if SITL_STACK_CHECKING_ENABLED
     290             :     check_thread_stacks();
     291             : #endif
     292             : 
     293             : #if AP_RCPROTOCOL_ENABLED
     294   243357861 :     AP::RC().update();
     295             : #endif
     296             : }
     297             : 
     298             : /*
     299             :   set simulation timestamp
     300             :  */
     301   187279594 : void Scheduler::stop_clock(uint64_t time_usec)
     302             : {
     303   187279594 :     _stopped_clock_usec = time_usec;
     304   187279594 :     if (_sitlState->_sitl != nullptr && time_usec - _last_io_run > 10000) {
     305    14404915 :         _last_io_run = time_usec;
     306    14404915 :         _run_io_procs();
     307             :     }
     308   187279594 : }
     309             : 
     310             : /*
     311             :   trampoline for thread create
     312             : */
     313        2428 : void *Scheduler::thread_create_trampoline(void *ctx)
     314             : {
     315        2428 :     struct thread_attr *a = (struct thread_attr *)ctx;
     316        2428 :     a->thread = pthread_self();
     317        2428 :     a->f[0]();
     318             :     
     319           0 :     WITH_SEMAPHORE(_thread_sem);
     320           0 :     if (threads == a) {
     321           0 :         threads = a->next;
     322             :     } else {
     323           0 :         for (struct thread_attr *p=threads; p->next; p=p->next) {
     324           0 :             if (p->next == a) {
     325           0 :                 p->next = p->next->next;
     326           0 :                 break;
     327             :             }
     328             :         }
     329             :     }
     330           0 :     free(a->stack);
     331           0 :     free(a->f);
     332           0 :     delete a;
     333           0 :     return nullptr;
     334           0 : }
     335             : 
     336             : #ifndef PTHREAD_STACK_MIN
     337             : #define PTHREAD_STACK_MIN 16384U
     338             : #endif
     339             : 
     340             : /*
     341             :   create a new thread
     342             : */
     343        2428 : bool Scheduler::thread_create(AP_HAL::MemberProc proc, const char *name, uint32_t stack_size, priority_base base, int8_t priority)
     344             : {
     345        2428 :     WITH_SEMAPHORE(_thread_sem);
     346             : 
     347             :     // even an empty thread takes 2500 bytes on Linux, so always add 2300, giving us 200 bytes
     348             :     // safety margin
     349        2428 :     stack_size += 2300;
     350             :     
     351        2428 :     pthread_t thread {};
     352        2428 :     const uint32_t alloc_stack = MAX(size_t(PTHREAD_STACK_MIN),stack_size);
     353             : 
     354        2428 :     struct thread_attr *a = NEW_NOTHROW struct thread_attr;
     355        2428 :     if (!a) {
     356           0 :         return false;
     357             :     }
     358             :     // take a copy of the MemberProc, it is freed after thread exits
     359        2428 :     a->f = (AP_HAL::MemberProc *)malloc(sizeof(proc));
     360        2428 :     if (!a->f) {
     361           0 :         goto failed;
     362             :     }
     363        2428 :     if (posix_memalign(&a->stack, 4096, alloc_stack) != 0) {
     364           0 :         goto failed;
     365             :     }
     366        2428 :     if (!a->stack) {
     367           0 :         goto failed;
     368             :     }
     369        2428 :     memset(a->stack, stackfill, alloc_stack);
     370        2428 :     a->stack_min = (const uint8_t *)((((uint8_t *)a->stack) + alloc_stack) - stack_size);
     371             : 
     372        2428 :     a->stack_size = stack_size;
     373        2428 :     a->f[0] = proc;
     374        2428 :     a->name = name;
     375             : 
     376        2428 :     if (pthread_attr_init(&a->attr) != 0) {
     377           0 :         goto failed;
     378             :     }
     379             : #if SITL_STACK_CHECKING_ENABLED
     380             :     if (pthread_attr_setstack(&a->attr, a->stack, alloc_stack) != 0) {
     381             :         AP_HAL::panic("Failed to set stack of size %u for thread %s", alloc_stack, name);
     382             :     }
     383             : #endif
     384        2428 :     if (pthread_create(&thread, &a->attr, thread_create_trampoline, a) != 0) {
     385           0 :         goto failed;
     386             :     }
     387             : 
     388             : #if !defined(__APPLE__) && !defined(__OpenBSD__)
     389        2428 :     pthread_setname_np(thread, name);
     390             : #endif
     391             : 
     392        2428 :     a->next = threads;
     393        2428 :     threads = a;
     394        2428 :     return true;
     395             : 
     396           0 : failed:
     397           0 :     if (a->stack) {
     398           0 :         free(a->stack);
     399             :     }
     400           0 :     if (a->f) {
     401           0 :         free(a->f);
     402             :     }
     403           0 :     delete a;
     404           0 :     return false;
     405        2428 : }
     406             : 
     407             : /*
     408             :   check for stack overflow
     409             :  */
     410           0 : void Scheduler::check_thread_stacks(void)
     411             : {
     412           0 :     WITH_SEMAPHORE(_thread_sem);
     413           0 :     for (struct thread_attr *p=threads; p; p=p->next) {
     414           0 :         const uint8_t ncheck = 8;
     415           0 :         for (uint8_t i=0; i<ncheck; i++) {
     416           0 :             if (p->stack_min[i] != stackfill) {
     417           0 :                 AP_HAL::panic("stack overflow in thread %s", p->name);
     418             :             }
     419             :         }
     420             :     }
     421           0 : }
     422             : 
     423             : // get the name of the current thread, or nullptr if not known
     424           0 : const char *Scheduler::get_current_thread_name(void) const
     425             : {
     426           0 :     const pthread_t self = pthread_self();
     427           0 :     for (struct thread_attr *a=threads; a; a=a->next) {
     428           0 :         if (a->thread == self) {
     429           0 :             return a->name;
     430             :         }
     431             :     }
     432           0 :     return nullptr;
     433             : }

Generated by: LCOV version 1.14