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;
|