├── .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 | --------------------------------------------------------------------------------