1  /* Copyright (C) 2015-2023 Free Software Foundation, Inc.
       2     Contributed by Sebastian Huber <sebastian.huber@embedded-brains.de>.
       3  
       4     This file is part of the GNU Offloading and Multi Processing Library
       5     (libgomp).
       6  
       7     Libgomp is free software; you can redistribute it and/or modify it
       8     under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3, or (at your option)
      10     any later version.
      11  
      12     Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
      13     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      14     FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      15     more details.
      16  
      17     Under Section 7 of GPL version 3, you are granted additional
      18     permissions described in the GCC Runtime Library Exception, version
      19     3.1, as published by the Free Software Foundation.
      20  
      21     You should have received a copy of the GNU General Public License and
      22     a copy of the GCC Runtime Library Exception along with this program;
      23     see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24     <http://www.gnu.org/licenses/>.  */
      25  
      26  /* This is the RTEMS implementation of the thread pool management
      27     for libgomp.  This type is private to the library.  */
      28  
      29  #ifndef GOMP_POOL_H
      30  #define GOMP_POOL_H 1
      31  
      32  #include "libgomp.h"
      33  #include <sys/lock.h>
      34  #include <string.h>
      35  
      36  /* For each scheduler instance there may be a thread pool reservoir
      37     to limit the number of thread pools used by the OpenMP master threads of this
      38     scheduler instance.  The reservoirs are configured via the
      39     GOMP_RTEMS_THREAD_POOLS environment variable.  */
      40  struct gomp_thread_pool_reservoir {
      41    gomp_sem_t available;
      42    pthread_spinlock_t lock;
      43    size_t index;
      44    int priority;
      45    struct gomp_thread_pool *pools[];
      46  };
      47  
      48  struct gomp_tls_rtems_data {
      49    struct gomp_thread_pool_reservoir *thread_pool_reservoir;
      50  };
      51  
      52  extern struct gomp_thread_pool_reservoir **gomp_thread_pool_reservoirs;
      53  
      54  extern __thread struct gomp_tls_rtems_data gomp_tls_rtems_data;
      55  
      56  static inline struct gomp_thread_pool_reservoir *
      57  gomp_get_thread_pool_reservoir (void)
      58  {
      59    struct gomp_thread_pool_reservoir *res =
      60      gomp_tls_rtems_data.thread_pool_reservoir;
      61  
      62    if (res == NULL && gomp_thread_pool_reservoirs != NULL)
      63      {
      64        struct gomp_thread *thr = gomp_thread ();
      65        thr->thread_pool = gomp_malloc_cleared (sizeof (*thr->thread_pool));
      66        res = gomp_thread_pool_reservoirs[_Sched_Index ()];
      67        gomp_tls_rtems_data.thread_pool_reservoir = res;
      68      }
      69  
      70    return res;
      71  }
      72  
      73  static inline struct gomp_thread_pool *
      74  gomp_get_own_thread_pool (struct gomp_thread *thr, unsigned nthreads)
      75  {
      76    struct gomp_thread_pool *pool = thr->thread_pool;
      77    if (__builtin_expect (pool == NULL, 0))
      78      {
      79        pool = gomp_malloc_cleared (sizeof (*pool));
      80        pool->threads_busy = nthreads;
      81        thr->thread_pool = pool;
      82      }
      83    return pool;
      84  }
      85  
      86  static inline struct gomp_thread_pool *
      87  gomp_get_thread_pool (struct gomp_thread *thr, unsigned nthreads)
      88  {
      89    struct gomp_thread_pool *pool;
      90    struct gomp_thread_pool_reservoir *res;
      91  
      92    if (__builtin_expect (thr->thread_pool == NULL, 0))
      93      pthread_setspecific (gomp_thread_destructor, thr);
      94  
      95    res = gomp_get_thread_pool_reservoir ();
      96    if (res != NULL)
      97      {
      98        gomp_sem_wait (&res->available);
      99        pthread_spin_lock (&res->lock);
     100        pool = res->pools[--res->index];
     101        pthread_spin_unlock (&res->lock);
     102        pool->threads_busy = nthreads;
     103        thr->thread_pool = pool;
     104      }
     105    else
     106      pool = gomp_get_own_thread_pool (thr, nthreads);
     107  
     108    return pool;
     109  }
     110  
     111  static inline void
     112  gomp_release_thread_pool (struct gomp_thread_pool *pool)
     113  {
     114    struct gomp_thread_pool_reservoir *res =
     115      gomp_tls_rtems_data.thread_pool_reservoir;
     116    if (res != NULL)
     117      {
     118        pthread_spin_lock (&res->lock);
     119        res->pools[res->index++] = pool;
     120        pthread_spin_unlock (&res->lock);
     121        gomp_sem_post (&res->available);
     122      }
     123  }
     124  
     125  static inline pthread_attr_t *
     126  gomp_adjust_thread_attr (pthread_attr_t *attr, pthread_attr_t *mutable_attr)
     127  {
     128    struct gomp_thread_pool_reservoir *res = gomp_get_thread_pool_reservoir ();
     129    if (res != NULL && res->priority > 0)
     130      {
     131        struct sched_param param;
     132        int err;
     133        if (attr != mutable_attr)
     134  	{
     135  	  attr = mutable_attr;
     136  	  pthread_attr_init (attr);
     137  	}
     138        memset (¶m, 0, sizeof (param));
     139        param.sched_priority = res->priority;
     140        err = pthread_attr_setschedparam (attr, ¶m);
     141        if (err != 0)
     142  	gomp_fatal ("Thread attribute set scheduler parameters failed: %s", strerror (err));
     143        err = pthread_attr_setschedpolicy (attr, SCHED_FIFO);
     144        if (err != 0)
     145  	gomp_fatal ("Thread attribute set scheduler policy failed: %s", strerror (err));
     146        err = pthread_attr_setinheritsched (attr, PTHREAD_EXPLICIT_SCHED);
     147        if (err != 0)
     148  	gomp_fatal ("Thread attribute set explicit scheduler failed: %s", strerror (err));
     149      }
     150    return attr;
     151  }
     152  
     153  #endif /* GOMP_POOL_H */