summaryrefslogblamecommitdiffstats
path: root/04_exercise/threadpool.h
blob: 5ffefdf5efcfe6b7a1bff254118a1eddb9d95ad2 (plain) (tree)
1
2
3
4
5
6
7
8
9


                             
                      
                   
                          

                                                                     
  


                                                                          
                                     
 
                                 


                                                      


                                                                                 



                                                                         






































































                                                                               






























                                                                                                

      
#ifndef THREADPOOL_H_INCLUDED
#define THREADPOOL_H_INCLUDED

#include <stdatomic.h>
#include <stddef.h>
#include "ppmlib/ppmlib.h"

/**@brief Funktionszeiger auf eine asynchron auszuführende Funktion.
 *
 * Der Parameter kann zur Übergabe von Argumenten und den Rückgabewerten
 * der Funktion genutzt werden.
 */
typedef void (*ThreadTask_f)(void *);

typedef atomic_char FutureStatus;
enum FurutureStatusEnum {
    FUT_WAITING = 0, FUT_IN_PROGRESS = 1, FUT_DONE = 2
};
/**@brief Handle zum zukünftigen Rückgabewert eines asynchronen Funktionsrufes.
 */
typedef struct Future {
    ThreadTask_f fn;   ///<@brief Zeiger auf die auszuführende Funktion.
    /* TODO: benötigte Attribute hinzufügen */
    FutureStatus status;
}                   Future;

/**@brief Initialisiert den globalen Thread-Pool.
 * 
 * Diese Funktion legt \p size viele Worker-Threads an und initialisiert den
 * restlichen Zustand des Thread-Pools. Danach werden keine weiteren Threads
 * mehr durch den Thread-Pool angelegt.
 * 
 * @param size  Anzahl der Worker-Threads
 */
extern int tpInit(size_t size);

/**@brief Beendet alle Worker-Threads im Thread-Pool kontrolliert und gibt
 * sämtliche Systemressourcen des Thread-Pools frei.
 * 
 * Bereits gestartete Futures können abgebrochen oder fertiggestellt werden.
 */
extern void tpRelease(void);

/**@brief Startet eine nutzerdefinierte Future.
 * 
 * Die Funktion initialisiert die übergebene Future und fügt sie zur Liste
 * der abzuarbeitenden Tasks hinzu. Die Abarbeitungsreihenfolge ist
 * unspezifiziert und die Abarbeitung startet sofort (eager).
 * 
 * @param future  partiell-initialisiertes Future-Objekt
 * @note Der Rufer ist dafür verantwortlich, dass der Speicher nicht vor
 * der Auswertung des asynchronen Funktionsrufes freigegeben wird.
 */
extern void tpAsync(Future *future);

/**@brief Erwartet die Abarbeitung einer Future.
 * 
 * Nachdem diese Funktion zurückgekommen ist, ist der asynchrone Funktionsruf
 * garantiert abgeschlossen und der Zugriff auf die Future Data-Race-frei.
 * Die Funktion gibt auch alle Systemressourcen des Thread-Pools frei.
 * 
 * @param future  asynchron gestartetes Future-Objekt
 * @note Der Rufer kann danach den Speicher für die Future wieder freigeben.
 */
extern void tpAwait(Future *future);

/**@brief Erzeugt Code für eine spezialisierte Schnittstelle einer Funktion.
 * 
 * Die direkte Nutzung der minimalistischen Schnittstelle des Thread-Pools ist
 * unkomfortabel und fehleranfällig, daher ist hier ein Makro definiert,
 * mithilfe dessen eine spezifische Schnittstelle für asynchrone Funktionen
 * erzeugt werden kann.
 * 
 * Das Makro muss im Filescope expandiert werden und erzeugt folgende Elemente:
 *   1. eine spezifische Datenstruktur für Argumente und Rückgabewerte der
 *      konkreten Funktion mit dem Namen `func_fut`
 *   2. eine sogenannte thunk-Funktion mit dem Namen `funcThunk`, welche die
 *      Funktionsargumente aus dem generischen Argument entpackt, die konkrete
 *      Funktion ausführt und dann den Rückgabewert zurückschreibt
 *   3. eine asynchrone Ruf-Funktion mit dem Namen `funcAsync`, welche ein
 *      Future-Objekt initialisiert und zurückgibt
 *   4. eine synchrone Warte-Funktion mit dem Namen `funcAwait`, welche auf die
 *      Abarbeitung eines zuvor asynchron gerufenen Future-Objektes wartet und
 *      das Ergebnis zurückgibt
 * 
 * Zusätzlich dazu wird die konkrete Funktion anfangs deklariert.
 * Benutzung ist wie folgt möglich:
 * @code
 * 	// deklariert die Schnittstelle einer Funktion ´long fib(long)´
 * 	TASK(long, fib, long);
 * @endcode
 * 
 * @param TYPE  Rückgabetyp der Funktion
 * @param NAME  Name der Funktion
 * @param ARG   Parametertyp der Funktion
 */
#define DECL_ARG(ARG) ARG UID(ARG);
#define DO_NOTHING(ARG) ARG
#define POINT_ARG(ARG) data->UID(ARG)
#define AWAIT_ARG(ARG) ARG UID(ARG)
#define ASSIGN_ARG(ARG) .UID(ARG) = UID(ARG)
#define APPEND_COMMA(ARG) ARG,
#define TASK(TYPE, NAME, ...) \
    TYPE NAME(__VA_ARGS__); \
    \
    typedef struct { \
        Future fut;  \
        FOREACH(DO_NOTHING,DECL_ARG,(__VA_ARGS__)) \
        TYPE   res;  \
    } NAME ## _fut;  \
    \
    static void NAME ## Thunk(void *args) { \
        NAME ## _fut *data = args;          \
        data->res = NAME(FOREACH(APPEND_COMMA,POINT_ARG,(__VA_ARGS__)));        \
    } \
    static inline NAME ## _fut NAME ## Future(FOREACH(APPEND_COMMA,AWAIT_ARG,(__VA_ARGS__))) { \
        return (NAME ## _fut) {                          \
            .fut = { .fn = &NAME ## Thunk, .status=FUT_WAITING},             \
            FOREACH(APPEND_COMMA,ASSIGN_ARG,(__VA_ARGS__))                                   \
        };                                               \
    } \
    static inline NAME ## _fut* NAME ## Async(NAME ## _fut *future) { \
        return tpAsync(&future->fut), future;                         \
    } \
    static inline TYPE NAME ## Await(NAME ## _fut *future) { \
        return tpAwait(&future->fut), future->res;           \
    }

#endif