LCOV - code coverage report
Current view: top level - AP_HAL_Linux - Thread.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 66 108 61.1 %
Date: 2026-09-09 11:02:08 Functions: 9 12 75.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2016  Intel Corporation. All rights reserved.
       3             :  *
       4             :  * This file is free software: you can redistribute it and/or modify it
       5             :  * under the terms of the GNU General Public License as published by the
       6             :  * Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This file is distributed in the hope that it will be useful, but
      10             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      12             :  * See the GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License along
      15             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : #include "Thread.h"
      18             : 
      19             : #include <alloca.h>
      20             : #include <limits.h>
      21             : #include <sys/types.h>
      22             : #include <stdio.h>
      23             : #include <unistd.h>
      24             : #include <utility>
      25             : 
      26             : #include <AP_HAL/AP_HAL.h>
      27             : #include <AP_Math/AP_Math.h>
      28             : #include "Scheduler.h"
      29             : 
      30             : #define STACK_POISON 0xBEBACAFE
      31             : 
      32             : extern const AP_HAL::HAL &hal;
      33             : 
      34             : namespace Linux {
      35             : 
      36             : 
      37           4 : void *Thread::_run_trampoline(void *arg)
      38             : {
      39           4 :     Thread *thread = static_cast<Thread *>(arg);
      40           4 :     thread->_poison_stack();
      41           4 :     thread->_run();
      42             : 
      43           4 :     if (thread->_auto_free) {
      44           0 :         delete thread;
      45             :     }
      46             : 
      47           4 :     return nullptr;
      48             : }
      49             : 
      50           2 : bool Thread::_run()
      51             : {
      52           2 :     if (!_task) {
      53           0 :         return false;
      54             :     }
      55             : 
      56           2 :     _task();
      57             : 
      58           2 :     return true;
      59             : }
      60             : 
      61             : /* Round up to the specified alignment.
      62             :  *
      63             :  * Let u be the address p rounded up to the alignment a. Then:
      64             :  *    u = p + a - 1 - r, where r = (p + a - 1) % a
      65             :  *
      66             :  * If p % a = 0, i.e. if p is already aligned, then:
      67             :  *     r = a - 1 ==> u = p
      68             :  *
      69             :  * Otherwise:
      70             :  *     r = p % a -1 ==> u = p + a - p % a
      71             :  *
      72             :  *     p can be written p = q + p % a, where q is rounded down to the
      73             :  *     alignment a. Then u = q + a.
      74             :  */
      75           4 : static inline void *align_to(void *p, size_t align)
      76             : {
      77           4 :     return (void *)(((uintptr_t)p + align - 1) & ~(align - 1));
      78             : }
      79             : 
      80           4 : void Thread::_poison_stack()
      81             : {
      82             :     pthread_attr_t attr;
      83             :     size_t stack_size, guard_size;
      84             :     void *stackp;
      85             :     uint32_t *p, *curr, *begin, *end;
      86             : 
      87             :     // `pthread_self` should be used here since _ctx could be not initialized
      88             :     // in a race condition.
      89           8 :     if (pthread_getattr_np(pthread_self(), &attr) != 0 ||
      90           8 :         pthread_attr_getstack(&attr, &stackp, &stack_size) != 0 ||
      91           4 :         pthread_attr_getguardsize(&attr, &guard_size) != 0) {
      92           0 :         return;
      93             :     }
      94             : 
      95           4 :     stack_size /= sizeof(uint32_t);
      96           4 :     guard_size /= sizeof(uint32_t);
      97             : 
      98             :     /* The stack either grows upward or downard. The guard part always
      99             :      * protects the end */
     100           4 :     end = (uint32_t *)stackp;
     101           4 :     begin = end + stack_size;
     102           4 :     curr = (uint32_t *)align_to(alloca(sizeof(uint32_t)), alignof(uint32_t));
     103             : 
     104             :     /* if curr is closer to @end, the stack actually grows from low to high
     105             :      * virtual address: this is because this function should be executing very
     106             :      * early in the thread's life and close to the thread creation, assuming
     107             :      * the actual stack size is greater than the guard size and the stack
     108             :      * until now is resonably small */
     109           4 :     if (abs(curr - begin) > abs(curr - end)) {
     110           0 :         std::swap(end, begin);
     111           0 :         end -= guard_size;
     112             : 
     113           0 :         for (p = end; p > curr; p--) {
     114           0 :             *p = STACK_POISON;
     115             :         }
     116             :     } else {
     117           4 :         end += guard_size;
     118             : 
     119             :         /* we aligned curr to the up boundary, make sure this didn't cause us
     120             :          * to lose some bytes */
     121           4 :         curr--;
     122             : 
     123     8379760 :         for (p = end; p < curr; p++) {
     124     8379756 :             *p = STACK_POISON;
     125             :         }
     126             :     }
     127             : 
     128           4 :     _stack_debug.start = begin;
     129           4 :     _stack_debug.end = end;
     130             : }
     131             : 
     132           0 : size_t Thread::get_stack_usage()
     133             : {
     134             :     uint32_t *p;
     135           0 :     size_t result = 0;
     136             : 
     137             :     /* Make sure we are tracking usage for this thread */
     138           0 :     if (_stack_debug.start == 0 || _stack_debug.end == 0) {
     139           0 :         return 0;
     140             :     }
     141             : 
     142           0 :     if (_stack_debug.start < _stack_debug.end) {
     143           0 :         for (p = _stack_debug.end; p > _stack_debug.start; p--) {
     144           0 :             if (*p != STACK_POISON) {
     145           0 :                 break;
     146             :             }
     147             :         }
     148           0 :         result = p - _stack_debug.start;
     149             :     } else {
     150           0 :         for (p = _stack_debug.end; p < _stack_debug.start; p++) {
     151           0 :             if (*p != STACK_POISON) {
     152           0 :                 break;
     153             :             }
     154             :         }
     155           0 :         result = _stack_debug.start - p;
     156             :     }
     157             : 
     158           0 :     return result;
     159             : }
     160             : 
     161           4 : bool Thread::start(const char *name, int policy, int prio)
     162             : {
     163           4 :     if (_started) {
     164           0 :         return false;
     165             :     }
     166             : 
     167           4 :     struct sched_param param = { .sched_priority = prio };
     168             :     pthread_attr_t attr;
     169             :     int r;
     170             : 
     171           4 :     pthread_attr_init(&attr);
     172             : 
     173             :     /*
     174             :       we need to run as root to get realtime scheduling. Allow it to
     175             :       run as non-root for debugging purposes, plus to allow the Replay
     176             :       tool to run
     177             :      */
     178           4 :     if (geteuid() == 0) {
     179           0 :         if ((r = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) != 0 ||
     180           0 :             (r = pthread_attr_setschedpolicy(&attr, policy)) != 0 ||
     181           0 :             (r = pthread_attr_setschedparam(&attr, &param)) != 0) {
     182           0 :             AP_HAL::panic("Failed to set attributes for thread '%s': %s",
     183             :                           name, strerror(r));
     184             :         }
     185             :     }
     186             : 
     187           4 :     if (_stack_size) {
     188           0 :         if (pthread_attr_setstacksize(&attr, _stack_size) != 0) {
     189           0 :             return false;
     190             :         }
     191             :     }
     192             : 
     193           4 :     r = pthread_create(&_ctx, &attr, &Thread::_run_trampoline, this);
     194           4 :     if (r != 0) {
     195           0 :         AP_HAL::panic("Failed to create thread '%s': %s",
     196             :                       name, strerror(r));
     197             :     }
     198           4 :     pthread_attr_destroy(&attr);
     199             : 
     200           4 :     if (name) {
     201           0 :         pthread_setname_np(_ctx, name);
     202             :     }
     203             : 
     204           4 :     _started = true;
     205             : 
     206           4 :     return true;
     207             : }
     208             : 
     209           0 : bool Thread::is_current_thread()
     210             : {
     211           0 :     return pthread_equal(pthread_self(), _ctx);
     212             : }
     213             : 
     214           2 : bool Thread::join()
     215             : {
     216             :     void *ret;
     217             : 
     218           2 :     if (_ctx == 0) {
     219           0 :         return false;
     220             :     }
     221             : 
     222           4 :     if (pthread_join(_ctx, &ret) != 0 ||
     223           2 :         (intptr_t)ret != 0) {
     224           0 :         return false;
     225             :     }
     226             : 
     227           2 :     return true;
     228             : }
     229             : 
     230             : 
     231           2 : bool PeriodicThread::set_rate(uint32_t rate_hz)
     232             : {
     233           2 :     if (_started || rate_hz == 0) {
     234           1 :         return false;
     235             :     }
     236             : 
     237           1 :     _period_usec = hz_to_usec(rate_hz);
     238             : 
     239           1 :     return true;
     240             : }
     241             : 
     242           0 : bool Thread::set_stack_size(size_t stack_size)
     243             : {
     244           0 :     if (_started) {
     245           0 :         return false;
     246             :     }
     247             : 
     248           0 :     _stack_size = MAX(stack_size, (size_t) PTHREAD_STACK_MIN);
     249             : 
     250           0 :     return true;
     251             : }
     252             : 
     253           1 : bool PeriodicThread::_run()
     254             : {
     255           1 :     if (_period_usec == 0) {
     256           0 :         return false;
     257             :     }
     258             : 
     259           1 :     uint64_t next_run_usec = AP_HAL::micros64() + _period_usec;
     260             : 
     261          10 :     while (!_should_exit) {
     262           9 :         uint64_t dt = next_run_usec - AP_HAL::micros64();
     263           9 :         if (dt > _period_usec) {
     264             :             // we've lost sync - restart
     265           0 :             next_run_usec = AP_HAL::micros64();
     266             :         } else {
     267           9 :             Scheduler::from(hal.scheduler)->microsleep(dt);
     268             :         }
     269           9 :         next_run_usec += _period_usec;
     270             : 
     271           9 :         _task();
     272             :     }
     273             : 
     274           1 :     _started = false;
     275           1 :     _should_exit = false;
     276             : 
     277           1 :     return true;
     278             : }
     279             : 
     280           1 : bool PeriodicThread::stop()
     281             : {
     282           1 :     if (!is_started()) {
     283           0 :         return false;
     284             :     }
     285             : 
     286           1 :     _should_exit = true;
     287             : 
     288           1 :     return true;
     289             : }
     290             : 
     291             : }

Generated by: LCOV version 1.14