summaryrefslogtreecommitdiff
path: root/src/threadpool.c
blob: 8f1cf0b86fc1ea0feeef2b2e0f5cd01e5270e94f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "threadpool.h"
#include "shared.h"

#include <threads.h>
#include <stdlib.h>
#include <errno.h>

/* Mutex: Lock a shared resource. Used to prevent race conditions when accessing / modifying some shared resource. A lock must 
//      always be followed by an unlock

// Semaphore: Send / wait on a signal; solves the consumer/producer problem. A function that sends should never wait, and a 
//      function that waits should never send */

// Pair some data with a mutex. Specifically a way to deal with mutices easier, not for data storage (mtxpair_free does not free the `(void*)data` member)
typedef struct mtxp {
    void *data;
    mtx_t *mtx;
} mtxpair;

mtxpair * mtxpair_init(void * const data, int type) {
    mtxpair *mtxp = malloc(1 * sizeof(*mtxp));
    if(!mtxp)
        return NULL;

    // Make room for the mutex
    mtxp->mtx = malloc(1 * sizeof(*mtxp->mtx));
    if(!mtxp->mtx) {
        free(mtxp);
        return NULL;
    }

    // Init the mutex
    if(mtx_init(mtxp->mtx, type) == thrd_error) {
        free(mtxp); free(mtxp->mtx);
        RETURNWERR(errno, NULL);
    }

    mtxp->data = data;
    return mtxp;
}

void mtxpair_free(mtxpair *mp) {
    if(!mp)
        return;

    mtx_destroy(mp->mtx);
    free(mp->mtx);
    free(mp);

    return;
}

int mtxpair_setdata(mtxpair * const mp, void * const data) {
    if(!mp)
        RETURNWERR(EINVAL, -1);

    mp->data = data;
    return 0;
}


// thrd_create which calls mtx_lock/unlock on `arg` automatically
int thrd_createwmx(thrd_t * const thr, thrd_start_t func, mtxpair * const mtxd) {
    if(!thr)
        RETURNWERR(EINVAL, thrd_error);
    if(!func)
        RETURNWERR(EINVAL, thrd_error);
    if(!mtxd)
        RETURNWERR(EINVAL, thrd_error);

    if(mtx_lock(mtxd->mtx) == thrd_error) {RETURNWERR(errno, thrd_error);}
    int retval = thrd_create(thr, func, mtxd->data);
    if(mtx_unlock(mtxd->mtx) == thrd_error) {RETURNWERR(errno, thrd_error);}

    return retval;
}


/* Ok, after doing a little more research, the best way to do this is probaby via a producer/consumer architecture. Spawn a bunch of
// threads waiting on a queue (via semaphore) and when one is notified pop a task of the queue and execute it. In this case, the
// producer would be the filesystem scanner funciton providing new files to encrypt, and the consumers would be threads waiting 
// to encrypt them */

// Threadpool:
    // Array of threads
    // Task Queue
        // Readiness semaphore
        // Linked List of Tasks
            // Task:
                // int (*callback)(void*)
                // void *arg

// Here's a good reference of this implemented in C++ using Boost: https://gist.github.com/mikeando/482342

typedef struct cq {

} cqueue;

typedef struct task {
    int (*callback)(void*);
    void *arg;
} task;
typedef struct tp {
    thrd_t **threads;
    int nthreads;                

    cqueue *taskqueue;
} threadpool;