LCOV - code coverage report
Current view: top level - ROOT-Sim/src/arch - thread.c Hit Total Coverage
Test: doc-coverage.info Lines: 9 9 100.0 %
Date: 2020-01-30 13:56:10

          Line data    Source code
       1           1 : /**
       2             :  * @file arch/thread.c
       3             :  *
       4             :  * @brief Generic thread management facilities.
       5             :  *
       6             :  * This module provides generic facilities for thread management.
       7             :  * In particular, helper functions to startup worker threads are exposed,
       8             :  * and a function to synchronize multiple threads on a software barrier.
       9             :  *
      10             :  * The software barrier also offers a leader election facility, so that
      11             :  * once all threads are synchronized on the barrier, the function returns
      12             :  * true to only one of them.
      13             :  *
      14             :  * @copyright
      15             :  * Copyright (C) 2008-2019 HPDCS Group
      16             :  * https://hpdcs.github.io
      17             :  *
      18             :  * This file is part of ROOT-Sim (ROme OpTimistic Simulator).
      19             :  *
      20             :  * ROOT-Sim is free software; you can redistribute it and/or modify it under the
      21             :  * terms of the GNU General Public License as published by the Free Software
      22             :  * Foundation; only version 3 of the License applies.
      23             :  *
      24             :  * ROOT-Sim is distributed in the hope that it will be useful, but WITHOUT ANY
      25             :  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      26             :  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
      27             :  *
      28             :  * You should have received a copy of the GNU General Public License along with
      29             :  * ROOT-Sim; if not, write to the Free Software Foundation, Inc.,
      30             :  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      31             :  *
      32             :  * @author Alessandro Pellegrini
      33             :  * @date Jan 25, 2012
      34             :  */
      35             : 
      36             : #include <stdbool.h>
      37             : #include <arch/thread.h>
      38             : #include <core/init.h>
      39             : #include <mm/mm.h>
      40             : 
      41             : /**
      42             :  * An OS-level thread id. We never do any join on worker threads, so
      43             :  * there is no need to keep track of system ids. Internally, each thread
      44             :  * is given a unique id in [0, n_thr], which is used to know who is who.
      45             :  * This variable is used only when spawning threads, because the system
      46             :  * wants us to know who is what thread from its point of view, although
      47             :  * we don't care at all.
      48             :  */
      49           1 : static tid_t os_tid;
      50             : 
      51             : 
      52             : /**
      53             :  * The id of the thread.
      54             :  *
      55             :  * @todo This is global across all kernel instances in a distributed
      56             :  *       run. We don't actually care at all about this, so this can be
      57             :  *       safely made per-machine. Once this is done, local_tid should
      58             :  *       go away.
      59             :  */
      60           1 : __thread unsigned int tid;
      61             : 
      62             : 
      63             : /**
      64             :  * The "local" id of the thread. This is a per-instance value which
      65             :  * uniquely identifies a thread on a compute node.
      66             :  *
      67             :  * @todo This is global across all kernel instances in a distributed
      68             :  *       run. We don't actually care at all about this, so this can be
      69             :  *       safely made per-machine. Once this is done, local_tid should
      70             :  *       go away.
      71             :  */
      72           1 : __thread unsigned int local_tid;
      73             : 
      74             : 
      75             : /**
      76             :  * The thread counter is a global variable which is incremented atomically
      77             :  * to assign a unique thread ID. Each spawned thread will compete using a
      78             :  * CAS to update this counter. Once a thread succeeds, it can use the
      79             :  * value it was keeping as its private local id.
      80             :  * This variable is used only as a synchronization point upon initialization,
      81             :  * later no one cares about its value.
      82             :  */
      83           1 : static unsigned int thread_counter = 0;
      84             : 
      85             : 
      86             : /**
      87             : * This helper function is the actual entry point for every thread created
      88             : * using the provided internal services. The goal of this function is to
      89             : * start a race on the thread_counter shared variable using a CAS. This
      90             : * race across all the threads is used to determine unique identifiers
      91             : * across all the worker threads. This is done in this helper function to
      92             : * silently set the new thread's tid before any simulation-specific function
      93             : * is ever called, so that any place in the simulator will find that
      94             : * already set.
      95             : * Additionally, when the created thread returns, it frees the memory used
      96             : * to maintain the real entry point and the pointer to its arguments.
      97             : *
      98             : * @param arg A pointer to an internally defined structure keeping the
      99             : *            real thread's entry point and its arguments
     100             : *
     101             : * @return This function always returns NULL
     102             : */
     103           1 : static void *__helper_create_thread(void *arg)
     104             : {
     105             : 
     106             :         struct _helper_thread *real_arg = (struct _helper_thread *)arg;
     107             : 
     108             :         // Get a unique local thread id...
     109             :         unsigned int old_counter;
     110             :         unsigned int _local_tid;
     111             : 
     112             :         while (true) {
     113             :                 old_counter = thread_counter;
     114             :                 _local_tid = old_counter + 1;
     115             :                 if (iCAS(&thread_counter, old_counter, _local_tid)) {
     116             :                         break;
     117             :                 }
     118             :         }
     119             :         local_tid = _local_tid;
     120             : 
     121             :         // ...and make it globally unique
     122             :         tid = to_global_tid(kid, _local_tid);
     123             : 
     124             :         // Set the affinity on a CPU core, for increased performance
     125             :         if (likely(rootsim_config.core_binding))
     126             :                 set_affinity(local_tid);
     127             : 
     128             :         // Now get into the real thread's entry point
     129             :         real_arg->start_routine(real_arg->arg);
     130             : 
     131             :         // Free arg and return (we don't really need any return value)
     132             :         rsfree(arg);
     133             :         return NULL;
     134             : }
     135             : 
     136             : /**
     137             : * This function creates n threads, all having the same entry point and
     138             : * the same arguments.
     139             : * It creates a new thread starting from the __helper_create_thread
     140             : * function which silently sets the new thread's tid.
     141             : * Note that the arguments passed to __helper_create_thread are malloc'd
     142             : * here, and free'd there. This means that if start_routine does not
     143             : * return, there is a memory leak.
     144             : * Additionally, note that we don't make a copy of the arguments pointed
     145             : * by arg, so all the created threads will share them in memory. Changing
     146             : * passed arguments from one of the newly created threads will result in
     147             : * all the threads seeing the change.
     148             : *
     149             : * @param n The number of threads which should be created
     150             : * @param start_routine The new threads' entry point
     151             : * @param arg A pointer to an array of arguments to be passed to the new
     152             : *            threads' entry point
     153             : *
     154             : */
     155           1 : void create_threads(unsigned short int n, void *(*start_routine)(void *), void *arg)
     156             : {
     157             :         int i;
     158             : 
     159             :         // We create our thread within our helper function, which accepts just
     160             :         // one parameter. We thus have to create one single parameter containing
     161             :         // the original pointer and the function to be used as real entry point.
     162             :         // This malloc'd array is free'd by the helper function.
     163             :         struct _helper_thread *new_arg = rsalloc(sizeof(struct _helper_thread));
     164             :         new_arg->start_routine = start_routine;
     165             :         new_arg->arg = arg;
     166             : 
     167             :         // n threads are created simply looping...
     168             :         for (i = 0; i < n; i++) {
     169             :                 new_thread(__helper_create_thread, (void *)new_arg);
     170             :         }
     171             : }
     172             : 
     173             : /**
     174             : * This function initializes a thread barrier. If more than the hereby specified
     175             : * number of threads try to synchronize on the barrier, the behaviour is undefined.
     176             : *
     177             : * @param b the thread barrier to initialize
     178             : * @param t the number of threads which will synchronize on the barrier
     179             : */
     180           1 : void barrier_init(barrier_t * b, int t)
     181             : {
     182             :         b->num_threads = t;
     183             :         thread_barrier_reset(b);
     184             : }
     185             : 
     186             : /**
     187             : * This function synchronizes all the threads. After a thread leaves this function,
     188             : * it is guaranteed that no other thread has (at least) not entered the function,
     189             : * therefore allowing to create a separation between the execution in portions of the code.
     190             : * If more threads than specified in the initialization of the barrier try to synchronize
     191             : * on it, the behaviour is undefined.
     192             : * The function additionally returns 'true' only to one of the calling threads, allowing
     193             : * the execution of portions of code in isolated mode after the barrier itself. This is
     194             : * like a leader election for free.
     195             : *
     196             : * @param b A pointer to the thread barrier to synchronize on
     197             : *
     198             : * @return false to all threads, except for one which is elected as the leader
     199             : */
     200           1 : bool thread_barrier(barrier_t * b)
     201             : {
     202             :         // Wait for the leader to finish resetting the barrier
     203             :         while (atomic_read(&b->barr) != -1) ;
     204             : 
     205             :         // Wait for all threads to synchronize
     206             :         atomic_dec(&b->c1);
     207             :         while (atomic_read(&b->c1)) ;
     208             : 
     209             :         // Leader election
     210             :         if (unlikely(atomic_inc_and_test(&b->barr))) {
     211             : 
     212             :                 // I'm sync'ed!
     213             :                 atomic_dec(&b->c2);
     214             : 
     215             :                 // Wait all the other threads to leave the first part of the barrier
     216             :                 while (atomic_read(&b->c2)) ;
     217             : 
     218             :                 // Reset the barrier to its initial values
     219             :                 thread_barrier_reset(b);
     220             : 
     221             :                 return true;
     222             :         }
     223             :         // I'm sync'ed!
     224             :         atomic_dec(&b->c2);
     225             : 
     226             :         return false;
     227             : }

Generated by: LCOV version 1.12