DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

Thread.hpp (9051B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #ifndef DISTRHO_THREAD_HPP_INCLUDED
     18 #define DISTRHO_THREAD_HPP_INCLUDED
     19 
     20 #include "Mutex.hpp"
     21 #include "Sleep.hpp"
     22 #include "String.hpp"
     23 
     24 #ifdef DISTRHO_OS_LINUX
     25 # include <sys/prctl.h>
     26 #endif
     27 
     28 #ifdef DISTRHO_OS_WASM
     29 # error Threads do not work under wasm!
     30 #endif
     31 
     32 START_NAMESPACE_DISTRHO
     33 
     34 // -----------------------------------------------------------------------
     35 // Thread class
     36 
     37 class Thread
     38 {
     39 protected:
     40     /*
     41      * Constructor.
     42      */
     43     Thread(const char* const threadName = nullptr) noexcept
     44         : fLock(),
     45           fSignal(),
     46           fName(threadName),
     47          #ifdef PTW32_DLLPORT
     48           fHandle({nullptr, 0}),
     49          #else
     50           fHandle(0),
     51          #endif
     52           fShouldExit(false) {}
     53 
     54     /*
     55      * Destructor.
     56      */
     57     virtual ~Thread() /*noexcept*/
     58     {
     59         DISTRHO_SAFE_ASSERT(! isThreadRunning());
     60 
     61         stopThread(-1);
     62     }
     63 
     64     /*
     65      * Virtual function to be implemented by the subclass.
     66      */
     67     virtual void run() = 0;
     68 
     69     // -------------------------------------------------------------------
     70 
     71 public:
     72     /*
     73      * Check if the thread is running.
     74      */
     75     bool isThreadRunning() const noexcept
     76     {
     77        #ifdef PTW32_DLLPORT
     78         return (fHandle.p != nullptr);
     79        #else
     80         return (fHandle != 0);
     81        #endif
     82     }
     83 
     84     /*
     85      * Check if the thread should exit.
     86      */
     87     bool shouldThreadExit() const noexcept
     88     {
     89         return fShouldExit;
     90     }
     91 
     92     /*
     93      * Start the thread.
     94      */
     95     bool startThread(const bool withRealtimePriority = false) noexcept
     96     {
     97         // check if already running
     98         DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true);
     99 
    100         pthread_t handle;
    101 
    102         pthread_attr_t attr;
    103         pthread_attr_init(&attr);
    104 
    105         struct sched_param sched_param = {};
    106 
    107         if (withRealtimePriority)
    108         {
    109            #ifdef __MOD_DEVICES__
    110             int rtprio;
    111             const char* const srtprio = std::getenv("MOD_PLUGIN_THREAD_PRIORITY");
    112             if (srtprio != nullptr && (rtprio = std::atoi(srtprio)) > 0)
    113                 sched_param.sched_priority = rtprio - 1;
    114             else
    115            #endif
    116             sched_param.sched_priority = 80;
    117 
    118            #ifndef DISTRHO_OS_HAIKU
    119             if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM)          == 0  &&
    120                 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0  &&
    121               #ifndef DISTRHO_OS_WINDOWS
    122                (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)              == 0  ||
    123                 pthread_attr_setschedpolicy(&attr, SCHED_RR)                == 0) &&
    124               #endif
    125                 pthread_attr_setschedparam(&attr, &sched_param)             == 0)
    126             {
    127                 d_stdout("Thread setup with realtime priority successful");
    128             }
    129             else
    130            #endif
    131             {
    132                 d_stdout("Thread setup with realtime priority failed, going with normal priority instead");
    133                 pthread_attr_destroy(&attr);
    134                 pthread_attr_init(&attr);
    135             }
    136         }
    137 
    138         const MutexLocker ml(fLock);
    139 
    140         fShouldExit = false;
    141 
    142         bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
    143         pthread_attr_destroy(&attr);
    144 
    145         if (withRealtimePriority && !ok)
    146        {
    147             d_stdout("Thread with realtime priority failed on creation, going with normal priority instead");
    148             pthread_attr_init(&attr);
    149             ok = pthread_create(&handle, &attr, _entryPoint, this) == 0;
    150             pthread_attr_destroy(&attr);
    151        }
    152 
    153         DISTRHO_SAFE_ASSERT_RETURN(ok, false);
    154        #ifdef PTW32_DLLPORT
    155         DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false);
    156        #else
    157         DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false);
    158        #endif
    159         pthread_detach(handle);
    160         _copyFrom(handle);
    161 
    162         // wait for thread to start
    163         fSignal.wait();
    164         return true;
    165     }
    166 
    167     /*
    168      * Stop the thread.
    169      * In the 'timeOutMilliseconds':
    170      * = 0 -> no wait
    171      * > 0 -> wait timeout value
    172      * < 0 -> wait forever
    173      */
    174     bool stopThread(const int timeOutMilliseconds) noexcept
    175     {
    176         const MutexLocker ml(fLock);
    177 
    178         if (isThreadRunning())
    179         {
    180             signalThreadShouldExit();
    181 
    182             if (timeOutMilliseconds != 0)
    183             {
    184                 // Wait for the thread to stop
    185                 int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2;
    186 
    187                 for (; isThreadRunning();)
    188                 {
    189                     d_msleep(2);
    190 
    191                     if (timeOutCheck < 0)
    192                         continue;
    193 
    194                     if (timeOutCheck > 0)
    195                         timeOutCheck -= 1;
    196                     else
    197                         break;
    198                 }
    199             }
    200 
    201             if (isThreadRunning())
    202             {
    203                 // should never happen!
    204                 d_stderr2("assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__);
    205 
    206                 // copy thread id so we can clear our one
    207                 pthread_t threadId;
    208                 _copyTo(threadId);
    209                 _init();
    210 
    211                 pthread_detach(threadId);
    212                 return false;
    213             }
    214         }
    215 
    216         return true;
    217     }
    218 
    219     /*
    220      * Tell the thread to stop as soon as possible.
    221      */
    222     void signalThreadShouldExit() noexcept
    223     {
    224         fShouldExit = true;
    225     }
    226 
    227     // -------------------------------------------------------------------
    228 
    229     /*
    230      * Returns the name of the thread.
    231      * This is the name that gets set in the constructor.
    232      */
    233     const String& getThreadName() const noexcept
    234     {
    235         return fName;
    236     }
    237 
    238     /*
    239      * Returns the Id/handle of the thread.
    240      */
    241     pthread_t getThreadId() const noexcept
    242     {
    243         return fHandle;
    244     }
    245 
    246     /*
    247      * Changes the name of the caller thread.
    248      */
    249     static void setCurrentThreadName(const char* const name) noexcept
    250     {
    251         DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
    252 
    253        #ifdef DISTRHO_OS_LINUX
    254         prctl(PR_SET_NAME, name, 0, 0, 0);
    255        #endif
    256        #if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD)
    257         pthread_setname_np(pthread_self(), name);
    258        #endif
    259     }
    260 
    261     // -------------------------------------------------------------------
    262 
    263 private:
    264     Mutex              fLock;       // Thread lock
    265     Signal             fSignal;     // Thread start wait signal
    266     const String       fName;       // Thread name
    267     volatile pthread_t fHandle;     // Handle for this thread
    268     volatile bool      fShouldExit; // true if thread should exit
    269 
    270     /*
    271      * Init pthread type.
    272      */
    273     void _init() noexcept
    274     {
    275        #ifdef PTW32_DLLPORT
    276         fHandle.p = nullptr;
    277         fHandle.x = 0;
    278        #else
    279         fHandle = 0;
    280        #endif
    281     }
    282 
    283     /*
    284      * Copy our pthread type from another var.
    285      */
    286     void _copyFrom(const pthread_t& handle) noexcept
    287     {
    288        #ifdef PTW32_DLLPORT
    289         fHandle.p = handle.p;
    290         fHandle.x = handle.x;
    291        #else
    292         fHandle = handle;
    293        #endif
    294     }
    295 
    296     /*
    297      * Copy our pthread type to another var.
    298      */
    299     void _copyTo(volatile pthread_t& handle) const noexcept
    300     {
    301        #ifdef PTW32_DLLPORT
    302         handle.p = fHandle.p;
    303         handle.x = fHandle.x;
    304        #else
    305         handle = fHandle;
    306        #endif
    307     }
    308 
    309     /*
    310      * Thread entry point.
    311      */
    312     void _runEntryPoint() noexcept
    313     {
    314         if (fName.isNotEmpty())
    315             setCurrentThreadName(fName);
    316 
    317         // report ready
    318         fSignal.signal();
    319 
    320         try {
    321             run();
    322         } catch(...) {}
    323 
    324         // done
    325         _init();
    326     }
    327 
    328     /*
    329      * Thread entry point.
    330      */
    331     static void* _entryPoint(void* userData) noexcept
    332     {
    333         static_cast<Thread*>(userData)->_runEntryPoint();
    334         return nullptr;
    335     }
    336 
    337     DISTRHO_DECLARE_NON_COPYABLE(Thread)
    338 };
    339 
    340 // -----------------------------------------------------------------------
    341 
    342 END_NAMESPACE_DISTRHO
    343 
    344 #endif // DISTRHO_THREAD_HPP_INCLUDED