├── .gitignore
├── Makefile
├── README.md
├── UNLICENSE
├── lqueue.c
├── lqueue.h
├── main.c
├── sha1.c
├── sha1.h
├── wqueue.c
└── wqueue.h
/.gitignore:
--------------------------------------------------------------------------------
1 | main
2 | *.o
3 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CFLAGS = -std=c11 -Wall -Wextra -O3
2 | LDFLAGS = -pthread
3 |
4 | main : main.o lqueue.o wqueue.o sha1.o
5 |
6 | main.o : main.c lqueue.h wqueue.h sha1.h
7 | lqueue.o : lqueue.c lqueue.h
8 | wqueue.o : wqueue.c lqueue.h wqueue.h
9 | sha1.o : sha1.c
10 |
11 | .PHONY : run clean
12 |
13 | run : main
14 | ./$^
15 |
16 | clean :
17 | $(RM) main *.o
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # C11 + Pthreads Atomic Bounded Work Queue
2 |
3 | This is a small library providing a single-writer, multiple-reader
4 | lock-free queue (lqueue) using C11's `stdatomic.h` features. This
5 | queue wrapped in a multi-threaded work queue (wqueue) using POSIX
6 | threads and semaphores. Jobs in the form of function pointers /
7 | argument tuples are submitted to the queue and completed by the
8 | queue's threads, blocking as necessary when the queue is full.
9 |
10 | ~~~c
11 | struct sha1_job {
12 | char message[64];
13 | char hash[SHA1_DIGEST_SIZE * 2 + 1];
14 | };
15 |
16 | static void
17 | sha1_worker(int thread_id, void *arg)
18 | {
19 | /* ... compute hashes ... */
20 | }
21 |
22 | void
23 | sha1_compute_all(void)
24 | {
25 | int nthreads = sysconf(_SC_NPROCESSORS_ONLN);
26 | wqueue *queue = wqueue_create(50, nthreads);
27 | struct sha1_job jobs[400];
28 | for (unsigned i = 0; i < countof(jobs); i++) {
29 | sprintf(jobs[i].message, "hello%06d", i);
30 | wqueue_add(queue, sha1_worker, &jobs[i]);
31 | }
32 | wqueue_wait(queue); // wait for full job completion
33 | /* ... process results and/or queue more jobs ... */
34 | wqueue_free(queue);
35 | }
36 | ~~~
37 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/lqueue.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "lqueue.h"
5 |
6 | struct lqueue {
7 | _Atomic unsigned long head;
8 | _Atomic unsigned long tail;
9 | unsigned long mask;
10 | size_t element_size;
11 | char buffer[];
12 | };
13 |
14 | lqueue *
15 | lqueue_create(unsigned min_size, size_t element_size)
16 | {
17 | /* Round up nearest power of 2. */
18 | int exponent = 1;
19 | while (min_size >>= 1)
20 | exponent++;
21 | min_size = 1UL << exponent;
22 | lqueue *q = malloc(sizeof(*q) + element_size * min_size);
23 | q->mask = min_size - 1;
24 | q->element_size = element_size;
25 | q->head = ATOMIC_VAR_INIT(0);
26 | q->tail = ATOMIC_VAR_INIT(0);
27 | return q;
28 | }
29 |
30 | void
31 | lqueue_free(lqueue *q)
32 | {
33 | free(q);
34 | }
35 |
36 | int
37 | lqueue_offer(lqueue *q, void *v)
38 | {
39 | unsigned long head = atomic_load(&q->head);
40 | unsigned long tail = atomic_load(&q->tail);
41 | if (((tail + 1) & q->mask) == head)
42 | return 1;
43 | memcpy(q->buffer + q->element_size * tail, v, q->element_size);
44 | atomic_store(&q->tail, ((tail + 1) & q->mask));
45 | return 0;
46 | }
47 |
48 | int
49 | lqueue_poll(lqueue *q, void *v)
50 | {
51 | unsigned long head = atomic_load(&q->head);
52 | unsigned long tail = atomic_load(&q->tail);
53 | unsigned long next_head;
54 | do {
55 | if (head == tail)
56 | return 1;
57 | memcpy(v, q->buffer + q->element_size * head, q->element_size);
58 | next_head = (head + 1) & q->mask;
59 | } while (!atomic_compare_exchange_weak(&q->head, &head, next_head));
60 | return 0;
61 | }
62 |
--------------------------------------------------------------------------------
/lqueue.h:
--------------------------------------------------------------------------------
1 | /**
2 | * C11 lock-free bounded queue. Supports one writer and multiple
3 | * readers. To simplify memory management queue users, data offered to
4 | * the queue are copied into the queue's buffers and copied back out
5 | * on retrieval.
6 | *
7 | * Queue functions return non-zero if the queue is full/empty.
8 | */
9 | #ifndef LQUEUE_H
10 | #define LQUEUE_H
11 |
12 | #include
13 |
14 | typedef struct lqueue lqueue;
15 |
16 | lqueue *lqueue_create(unsigned min_size, size_t element_size);
17 | void lqueue_free(lqueue *);
18 | int lqueue_offer(lqueue *, void *);
19 | int lqueue_poll(lqueue *, void *);
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | /**
2 | * Work queue demo using SHA-1 whitening.
3 | */
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "wqueue.h"
11 | #include "sha1.h"
12 |
13 | #define ITERATIONS (1024 * 256)
14 |
15 | #define countof(a) (sizeof(a) / sizeof(0[a]))
16 |
17 | struct sha1_job {
18 | char message[64];
19 | char hash[SHA1_DIGEST_SIZE * 2 + 1];
20 | };
21 |
22 | static void
23 | sha1_worker(int thread_id, void *arg)
24 | {
25 | struct sha1_job *job = arg;
26 | uint8_t digest[SHA1_DIGEST_SIZE];
27 | SHA1_CTX context;
28 | SHA1_Init(&context);
29 | SHA1_Update(&context, (void *)job->message, strlen(job->message));
30 | SHA1_Final(&context, digest);
31 | for (int i = 0; i < ITERATIONS - 1; i++) {
32 | SHA1_Init(&context);
33 | SHA1_Update(&context, digest, SHA1_DIGEST_SIZE);
34 | SHA1_Final(&context, digest);
35 | }
36 | for (int i = 0; i < SHA1_DIGEST_SIZE; i++)
37 | sprintf(job->hash + i * 2, "%02x", digest[i]);
38 | char line[128];
39 | int length = sprintf(line, "%s: %s (thread %d)\n",
40 | job->message, job->hash, thread_id);
41 | fwrite(line, length, 1, stdout);
42 | }
43 |
44 | int
45 | main(void)
46 | {
47 | int nthreads = sysconf(_SC_NPROCESSORS_ONLN);
48 | wqueue *queue = wqueue_create(50, nthreads);
49 | struct sha1_job jobs[400];
50 | for (unsigned i = 0; i < countof(jobs); i++) {
51 | sprintf(jobs[i].message, "hello%06d", i);
52 | wqueue_add(queue, sha1_worker, &jobs[i]);
53 | }
54 | fprintf(stderr, "\x1b[91;1mWaiting ...\n\x1b[0m");
55 | wqueue_wait(queue);
56 | wqueue_free(queue);
57 | return 0;
58 | }
59 |
--------------------------------------------------------------------------------
/sha1.c:
--------------------------------------------------------------------------------
1 | /*
2 | SHA-1 in C
3 | By Steve Reid
4 | 100% Public Domain
5 |
6 | -----------------
7 | Modified 7/98
8 | By James H. Brown
9 | Still 100% Public Domain
10 |
11 | Corrected a problem which generated improper hash values on 16 bit machines
12 | Routine SHA1Update changed from
13 | void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
14 | len)
15 | to
16 | void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
17 | long len)
18 |
19 | The 'len' parameter was declared an int which works fine on 32 bit machines.
20 | However, on 16 bit machines an int is too small for the shifts being done
21 | against
22 | it. This caused the hash function to generate incorrect values if len was
23 | greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
24 |
25 | Since the file IO in main() reads 16K at a time, any file 8K or larger would
26 | be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
27 | "a"s).
28 |
29 | I also changed the declaration of variables i & j in SHA1Update to
30 | unsigned long from unsigned int for the same reason.
31 |
32 | These changes should make no difference to any 32 bit implementations since
33 | an
34 | int and a long are the same size in those environments.
35 |
36 | --
37 | I also corrected a few compiler warnings generated by Borland C.
38 | 1. Added #include for exit() prototype
39 | 2. Removed unused variable 'j' in SHA1Final
40 | 3. Changed exit(0) to return(0) at end of main.
41 |
42 | ALL changes I made can be located by searching for comments containing 'JHB'
43 | -----------------
44 | Modified 8/98
45 | By Steve Reid
46 | Still 100% public domain
47 |
48 | 1- Removed #include and used return() instead of exit()
49 | 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
50 | 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
51 |
52 | -----------------
53 | Modified 4/01
54 | By Saul Kravitz
55 | Still 100% PD
56 | Modified to run on Compaq Alpha hardware.
57 |
58 | -----------------
59 | Modified 07/2002
60 | By Ralph Giles
61 | Still 100% public domain
62 | modified for use with stdint types, autoconf
63 | code cleanup, removed attribution comments
64 | switched SHA1Final() argument order for consistency
65 | use SHA1_ prefix for public api
66 | move public api to sha1.h
67 | */
68 |
69 | /*
70 | Test Vectors (from FIPS PUB 180-1)
71 | "abc"
72 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
73 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
74 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
75 | A million repetitions of "a"
76 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
77 | */
78 |
79 | /* #define SHA1HANDSOFF */
80 |
81 | #ifdef HAVE_CONFIG_H
82 | #include "config.h"
83 | #endif
84 |
85 | #include
86 | #include
87 | #include
88 | #include "sha1.h"
89 |
90 | void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
91 |
92 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
93 |
94 | /* blk0() and blk() perform the initial expand. */
95 | /* I got the idea of expanding during the round function from SSLeay */
96 | /* FIXME: can we do this in an endian-proof way? */
97 | #ifdef WORDS_BIGENDIAN
98 | #define blk0(i) block->l[i]
99 | #else
100 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
101 | |(rol(block->l[i],8)&0x00FF00FF))
102 | #endif
103 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
104 | ^block->l[(i+2)&15]^block->l[i&15],1))
105 |
106 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
107 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
108 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
109 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
110 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
111 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
112 |
113 |
114 | #ifdef VERBOSE /* SAK */
115 | void SHAPrintContext(SHA1_CTX *context, char *msg){
116 | printf("%s (%d,%d) %x %x %x %x %x\n",
117 | msg,
118 | context->count[0], context->count[1],
119 | context->state[0],
120 | context->state[1],
121 | context->state[2],
122 | context->state[3],
123 | context->state[4]);
124 | }
125 | #endif /* VERBOSE */
126 |
127 | /* Hash a single 512-bit block. This is the core of the algorithm. */
128 | void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
129 | {
130 | uint32_t a, b, c, d, e;
131 | typedef union {
132 | uint8_t c[64];
133 | uint32_t l[16];
134 | } CHAR64LONG16;
135 | CHAR64LONG16* block;
136 |
137 | #ifdef SHA1HANDSOFF
138 | static uint8_t workspace[64];
139 | block = (CHAR64LONG16*)workspace;
140 | memcpy(block, buffer, 64);
141 | #else
142 | block = (CHAR64LONG16*)buffer;
143 | #endif
144 |
145 | /* Copy context->state[] to working vars */
146 | a = state[0];
147 | b = state[1];
148 | c = state[2];
149 | d = state[3];
150 | e = state[4];
151 |
152 | /* 4 rounds of 20 operations each. Loop unrolled. */
153 | R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
154 | R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
155 | R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
156 | R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
157 | R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
158 | R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
159 | R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
160 | R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
161 | R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
162 | R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
163 | R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
164 | R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
165 | R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
166 | R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
167 | R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
168 | R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
169 | R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
170 | R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
171 | R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
172 | R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
173 |
174 | /* Add the working vars back into context.state[] */
175 | state[0] += a;
176 | state[1] += b;
177 | state[2] += c;
178 | state[3] += d;
179 | state[4] += e;
180 |
181 | /* Wipe variables */
182 | a = b = c = d = e = 0;
183 | }
184 |
185 |
186 | /* SHA1Init - Initialize new context */
187 | void SHA1_Init(SHA1_CTX* context)
188 | {
189 | /* SHA1 initialization constants */
190 | context->state[0] = 0x67452301;
191 | context->state[1] = 0xEFCDAB89;
192 | context->state[2] = 0x98BADCFE;
193 | context->state[3] = 0x10325476;
194 | context->state[4] = 0xC3D2E1F0;
195 | context->count[0] = context->count[1] = 0;
196 | }
197 |
198 |
199 | /* Run your data through this. */
200 | void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
201 | {
202 | size_t i, j;
203 |
204 | #ifdef VERBOSE
205 | SHAPrintContext(context, "before");
206 | #endif
207 |
208 | j = (context->count[0] >> 3) & 63;
209 | if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
210 | context->count[1] += (len >> 29);
211 | if ((j + len) > 63) {
212 | memcpy(&context->buffer[j], data, (i = 64-j));
213 | SHA1_Transform(context->state, context->buffer);
214 | for ( ; i + 63 < len; i += 64) {
215 | SHA1_Transform(context->state, data + i);
216 | }
217 | j = 0;
218 | }
219 | else i = 0;
220 | memcpy(&context->buffer[j], &data[i], len - i);
221 |
222 | #ifdef VERBOSE
223 | SHAPrintContext(context, "after ");
224 | #endif
225 | }
226 |
227 |
228 | /* Add padding and return the message digest. */
229 | void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
230 | {
231 | uint32_t i;
232 | uint8_t finalcount[8];
233 |
234 | for (i = 0; i < 8; i++) {
235 | finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
236 | >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
237 | }
238 | SHA1_Update(context, (uint8_t *)"\200", 1);
239 | while ((context->count[0] & 504) != 448) {
240 | SHA1_Update(context, (uint8_t *)"\0", 1);
241 | }
242 | SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
243 | for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
244 | digest[i] = (uint8_t)
245 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
246 | }
247 |
248 | /* Wipe variables */
249 | i = 0;
250 | memset(context->buffer, 0, 64);
251 | memset(context->state, 0, 20);
252 | memset(context->count, 0, 8);
253 | memset(finalcount, 0, 8); /* SWR */
254 |
255 | #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
256 | SHA1_Transform(context->state, context->buffer);
257 | #endif
258 | }
259 |
260 | /*************************************************************/
261 |
262 | #if 0
263 | int main(int argc, char** argv)
264 | {
265 | int i, j;
266 | SHA1_CTX context;
267 | unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
268 | FILE* file;
269 |
270 | if (argc > 2) {
271 | puts("Public domain SHA-1 implementation - by Steve Reid ");
272 | puts("Modified for 16 bit environments 7/98 - by James H. Brown "); /* JHB */
273 | puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
274 | return(0);
275 | }
276 | if (argc < 2) {
277 | file = stdin;
278 | }
279 | else {
280 | if (!(file = fopen(argv[1], "rb"))) {
281 | fputs("Unable to open file.", stderr);
282 | return(-1);
283 | }
284 | }
285 | SHA1_Init(&context);
286 | while (!feof(file)) { /* note: what if ferror(file) */
287 | i = fread(buffer, 1, 16384, file);
288 | SHA1_Update(&context, buffer, i);
289 | }
290 | SHA1_Final(&context, digest);
291 | fclose(file);
292 | for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
293 | for (j = 0; j < 4; j++) {
294 | printf("%02X", digest[i*4+j]);
295 | }
296 | putchar(' ');
297 | }
298 | putchar('\n');
299 | return(0); /* JHB */
300 | }
301 | #endif
302 |
303 | /* self test */
304 |
305 | #ifdef TEST
306 |
307 | static char *test_data[] = {
308 | "abc",
309 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
310 | "A million repetitions of 'a'"};
311 | static char *test_results[] = {
312 | "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
313 | "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
314 | "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"};
315 |
316 |
317 | void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
318 | {
319 | int i,j;
320 | char *c = output;
321 |
322 | for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
323 | for (j = 0; j < 4; j++) {
324 | sprintf(c,"%02X", digest[i*4+j]);
325 | c += 2;
326 | }
327 | sprintf(c, " ");
328 | c += 1;
329 | }
330 | *(c - 1) = '\0';
331 | }
332 |
333 | int main(int argc, char** argv)
334 | {
335 | int k;
336 | SHA1_CTX context;
337 | uint8_t digest[20];
338 | char output[80];
339 |
340 | fprintf(stdout, "verifying SHA-1 implementation... ");
341 |
342 | for (k = 0; k < 2; k++){
343 | SHA1_Init(&context);
344 | SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k]));
345 | SHA1_Final(&context, digest);
346 | digest_to_hex(digest, output);
347 |
348 | if (strcmp(output, test_results[k])) {
349 | fprintf(stdout, "FAIL\n");
350 | fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]);
351 | fprintf(stderr,"\t%s returned\n", output);
352 | fprintf(stderr,"\t%s is correct\n", test_results[k]);
353 | return (1);
354 | }
355 | }
356 | /* million 'a' vector we feed separately */
357 | SHA1_Init(&context);
358 | for (k = 0; k < 1000000; k++)
359 | SHA1_Update(&context, (uint8_t*)"a", 1);
360 | SHA1_Final(&context, digest);
361 | digest_to_hex(digest, output);
362 | if (strcmp(output, test_results[2])) {
363 | fprintf(stdout, "FAIL\n");
364 | fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]);
365 | fprintf(stderr,"\t%s returned\n", output);
366 | fprintf(stderr,"\t%s is correct\n", test_results[2]);
367 | return (1);
368 | }
369 |
370 | /* success */
371 | fprintf(stdout, "ok\n");
372 | return(0);
373 | }
374 | #endif /* TEST */
375 |
--------------------------------------------------------------------------------
/sha1.h:
--------------------------------------------------------------------------------
1 | /* public api for steve reid's public domain SHA-1 implementation */
2 | /* this file is in the public domain */
3 |
4 | #ifndef __SHA1_H
5 | #define __SHA1_H
6 |
7 | #include
8 |
9 | #ifdef __cplusplus
10 | extern "C" {
11 | #endif
12 |
13 | typedef struct {
14 | uint32_t state[5];
15 | uint32_t count[2];
16 | uint8_t buffer[64];
17 | } SHA1_CTX;
18 |
19 | #define SHA1_DIGEST_SIZE 20
20 |
21 | void SHA1_Init(SHA1_CTX* context);
22 | void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
23 | void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
24 |
25 | #ifdef __cplusplus
26 | }
27 | #endif
28 |
29 | #endif /* __SHA1_H */
30 |
--------------------------------------------------------------------------------
/wqueue.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "lqueue.h"
6 | #include "wqueue.h"
7 |
8 | #define POISON NULL
9 |
10 | struct wqueue {
11 | lqueue *lqueue;
12 | sem_t count;
13 | int nthreads;
14 | struct wqueue_data {
15 | pthread_t thread;
16 | int id;
17 | struct wqueue *q;
18 | sem_t pause;
19 | sem_t complete;
20 | } threads[];
21 | };
22 |
23 | struct job {
24 | void (*f)(int, void *);
25 | void *arg;
26 | };
27 |
28 | static void
29 | pause(int id, void *arg)
30 | {
31 | wqueue *q = arg;
32 | sem_post(&q->threads[id - 1].complete);
33 | sem_wait(&q->threads[id - 1].pause);
34 | }
35 |
36 | static void *
37 | worker(void *arg)
38 | {
39 | struct wqueue_data *data = arg;
40 | for (;;) {
41 | sem_wait(&data->q->count);
42 | struct job job;
43 | if (0 == lqueue_poll(data->q->lqueue, &job)) {
44 | if (job.f == POISON)
45 | break;
46 | job.f(data->id, job.arg);
47 | }
48 | }
49 | return NULL;
50 | }
51 |
52 | wqueue *
53 | wqueue_create(unsigned min_size, int nthreads)
54 | {
55 | assert(nthreads > 0);
56 | assert((unsigned)nthreads < min_size);
57 | nthreads--;
58 | wqueue *q = malloc(sizeof(*q) + sizeof(q->threads[0]) * nthreads);
59 | q->lqueue = lqueue_create(min_size, sizeof(struct job));
60 | sem_init(&q->count, 0, 0);
61 | q->nthreads = nthreads;
62 | for (int i = 0; i < nthreads; i++) {
63 | q->threads[i].q = q;
64 | q->threads[i].id = i + 1;
65 | sem_init(&q->threads[i].pause, 0, 0);
66 | sem_init(&q->threads[i].complete, 0, 0);
67 | pthread_create(&q->threads[i].thread, NULL, worker, &q->threads[i]);
68 | }
69 | return q;
70 | }
71 |
72 | void
73 | wqueue_free(wqueue *q)
74 | {
75 | wqueue_wait(q);
76 | for (int i = 0; i < q->nthreads; i++)
77 | wqueue_add(q, POISON, NULL);
78 | for (int i = 0; i < q->nthreads; i++) {
79 | pthread_join(q->threads[i].thread, NULL);
80 | sem_destroy(&q->threads[i].pause);
81 | sem_destroy(&q->threads[i].complete);
82 | }
83 | lqueue_free(q->lqueue);
84 | sem_destroy(&q->count);
85 | free(q);
86 | }
87 |
88 | void
89 | wqueue_add(wqueue *q, void (*f)(int, void *), void *v)
90 | {
91 | struct job job = {f, v};
92 | while (lqueue_offer(q->lqueue, &job) != 0) {
93 | /* Help finish jobs until a spot opens in the queue. */
94 | struct job job;
95 | if (0 == lqueue_poll(q->lqueue, &job))
96 | job.f(0, job.arg);
97 | }
98 | sem_post(&q->count);
99 | }
100 |
101 | void
102 | wqueue_wait(wqueue *q)
103 | {
104 | /* Help finish running jobs. */
105 | struct job job;
106 | while (0 == lqueue_poll(q->lqueue, &job))
107 | job.f(0, job.arg);
108 | /* Ask all threads to pause. */
109 | for (int i = 0; i < q->nthreads; i++)
110 | wqueue_add(q, pause, q);
111 | /* Wait for all threads to complete. */
112 | for (int i = 0; i < q->nthreads; i++)
113 | sem_wait(&q->threads[i].complete);
114 | /* Unpause threads. */
115 | for (int i = 0; i < q->nthreads; i++)
116 | sem_post(&q->threads[i].pause);
117 | }
118 |
--------------------------------------------------------------------------------
/wqueue.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Atomic, bounded work queue for one writer and multiple readers.
3 | * Threads are spawned internally by the work queue on creation. For
4 | * each job added to the queue with wqueue_add(), the pass function is
5 | * called with the queue-local thread id (0 - n) and the given void
6 | * pointer argument. The thread id can be used to access, for example,
7 | * custom thread-local storage.
8 | *
9 | * When the queue is full, the caller of wqueue_add() (thread 0) will
10 | * assist in completing jobs to open up space in the queue. Similarly,
11 | * in wqueue_wait(), the calling thread will assist in jobs until all
12 | * jobs are consumed *and* completed. Because of this, the queue
13 | * spawns one less thread, because the "main" queuing thread
14 | * participates in job completion.
15 | *
16 | * Calling wqueue_free() will block (a la wqueue_wait()) for all jobs
17 | * to complete before destroying the queue.
18 | */
19 | #ifndef WQUEUE_H
20 | #define WQUEUE_H
21 |
22 | typedef struct wqueue wqueue;
23 |
24 | wqueue *wqueue_create(unsigned min_size, int nthreads);
25 | void wqueue_free(wqueue *);
26 | void wqueue_add(wqueue *, void (*)(int, void *), void *);
27 | void wqueue_wait(wqueue *);
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------