├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README ├── example.c ├── queue.c ├── queue.h ├── queue_internal.c └── queue_internal.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | build/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (Queue) 3 | 4 | option(BUILD_32 "build 32-bit on linux/windows" OFF) 5 | option(BUILD_64 "build 64-bit on linux/windows" OFF) 6 | 7 | set(DEPS_DIR "${PROJECT_BINARY_DIR}/deps/") 8 | 9 | include_directories("${PROJECT_SOURCE_DIR}") 10 | 11 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter") 12 | 13 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT CMAKE_OSX_ARCHITECTURES) 14 | set (CMAKE_OSX_ARCHITECTURES "i386;x86_64") 15 | endif () 16 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND BUILD_32) 17 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") 18 | endif () 19 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND BUILD_64) 20 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") 21 | endif () 22 | if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") 23 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/include/pthread.h" "${DEPS_DIR}/pthreads-w32/include/pthread.h") 24 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/include/sched.h" "${DEPS_DIR}/pthreads-w32/include/sched.h") 25 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/include/semaphore.h" "${DEPS_DIR}/pthreads-w32/include/semaphore.h") 26 | if (BUILD_64) 27 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/dll/x64/pthreadVC2.dll" "${PROJECT_BINARY_DIR}/pthreadVC2.dll") 28 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/lib/x64/pthreadVC2.lib" "${DEPS_DIR}/pthreads-w32/lib/pthreadVC2.lib") 29 | else () 30 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/dll/x86/pthreadVC2.dll" "${PROJECT_BINARY_DIR}/pthreadVC2.dll") 31 | file(DOWNLOAD "ftp://sourceware.org/pub/pthreads-win32/dll-latest/lib/x86/pthreadVC2.lib" "${DEPS_DIR}/pthreads-w32/lib/pthreadVC2.lib") 32 | endif () 33 | include_directories("${DEPS_DIR}/pthreads-w32/include/") 34 | set_source_files_properties (queue.c queue_internal.c example.c PROPERTIES LANGUAGE CXX) 35 | else () 36 | find_package (Threads) 37 | endif () 38 | 39 | add_library (queue SHARED queue.c queue_internal.c) 40 | add_library (queue_static STATIC queue.c queue_internal.c) 41 | add_executable (queueexample example.c) 42 | target_link_libraries (queueexample queue_static) 43 | if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") 44 | target_link_libraries (queue "${DEPS_DIR}/pthreads-w32/lib/pthreadVC2.lib") 45 | target_link_libraries (queue_static "${DEPS_DIR}/pthreads-w32/lib/pthreadVC2.lib") 46 | else () 47 | target_link_libraries (queue ${CMAKE_THREAD_LIBS_INIT}) 48 | target_link_libraries (queue_static ${CMAKE_THREAD_LIBS_INIT}) 49 | endif () 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 Tobias Thiel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Queue 2 | - thread-safe 3 | - ability to define max number of elements 4 | - sort elements into queue 5 | - get in O(1), except when filtered (worst case O(n)) 6 | - put in O(1) when unsorted, in O(n) in the worst case when sorted 7 | - MIT license (windows support relies on LGPL licensed pthreads-win32 (http://sourceware.org/pthreads-win32/)) 8 | 9 | sorted queue: 10 | your elements in the queue will be ordered according to the sort function which is given. 11 | 12 | Build steps: 13 | - mkdir build && cd build 14 | - cmake .. 15 | - make / build VS project under windows 16 | 17 | Building 32-/64-bit: 18 | - OS X: universal binaries are built 19 | - Linux: use 'cmake -DBUILT_32=ON ..' to force 32-bit built, -DBUILT_64=ON for 64-bit 20 | - Windows: use 'cmake -G "Visual Studio 11 Win64" -DBUILT_64=ON ..' to force a 64-bit build, 'cmake -G "Visual Studio 11" -DBUILT_32=ON ..' for 32-bit 21 | 22 | TODO: 23 | - proper test suite, so that everybody can easily see whats tested and run the tests himself 24 | - ability to turn of element counting => only unlimited queue 25 | - function to peek at next element 26 | 27 | error codes: 28 | < 0 => error 29 | = 0 => okay 30 | 31 | 0 - everything alright 32 | 33 | -1 - invalid queue 34 | -2 - could not acquire lock 35 | -3 - could not allocate memory 36 | -4 - no new data allowed 37 | -5 - invalid element in queue 38 | -6 - invalid callback 39 | -7 - max_elements reached or no elements in queue 40 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | typedef struct { 7 | char *test; 8 | } test; 9 | 10 | DEFINE_Q_GET(queue_get_test, test) 11 | DEFINE_Q_DESTROY(queue_destroy_complete_test, test) 12 | 13 | void free_test(test *t) { 14 | if(t->test != NULL) 15 | free(t->test); 16 | free(t); 17 | } 18 | 19 | int cmp_int_ptr(int *a, int *b) { 20 | if(*a < *b) 21 | return -1; 22 | else if(*a > *b) 23 | return 1; 24 | else 25 | return 0; 26 | } 27 | 28 | void unsorted_mode() { 29 | queue_t *q = queue_create(); 30 | 31 | char *t1 = (char *)malloc(1); 32 | char *t2 = (char *)malloc(2); 33 | char *t3 = (char *)malloc(4); 34 | char *t4 = (char *)malloc(8); 35 | 36 | test *s1 = (test *)malloc(sizeof(test)); 37 | s1->test = t1; 38 | test *s2 = (test *)malloc(sizeof(test)); 39 | s2->test = t2; 40 | test *s3 = (test *)malloc(sizeof(test)); 41 | s3->test = t3; 42 | test *s4 = (test *)malloc(sizeof(test)); 43 | s4->test = t4; 44 | 45 | queue_put(q, s1); 46 | queue_put(q, s2); 47 | queue_put(q, s3); 48 | queue_put(q, s4); 49 | 50 | test *t; 51 | queue_get_test(q, &t); 52 | free_test(t); 53 | queue_get_test(q, &t); 54 | free_test(t); 55 | 56 | queue_destroy_complete_test(q, free_test); 57 | } 58 | 59 | void sorted_mode() { 60 | queue_t *q = queue_create_sorted(1, (int (*)(void *, void *))cmp_int_ptr); 61 | 62 | int *t1 = (int *)malloc(sizeof(int)); 63 | int *t2 = (int *)malloc(sizeof(int)); 64 | int *t3 = (int *)malloc(sizeof(int)); 65 | int *t4 = (int *)malloc(sizeof(int)); 66 | 67 | *t1 = 10; 68 | *t2 = 12; 69 | *t3 = 1; 70 | *t4 = 1; 71 | 72 | queue_put(q, t1); 73 | queue_put(q, t2); 74 | queue_put(q, t3); 75 | queue_put(q, t4); 76 | 77 | int *t; 78 | queue_get(q, (void **)&t); 79 | printf("first int %i\n", *t); 80 | free(t); 81 | queue_get(q, (void **)&t); 82 | printf("second int %i\n", *t); 83 | free(t); 84 | queue_get(q, (void **)&t); 85 | printf("third int %i\n", *t); 86 | free(t); 87 | queue_get(q, (void **)&t); 88 | printf("fourth int %i\n", *t); 89 | free(t); 90 | 91 | queue_destroy_complete(q, NULL); 92 | } 93 | 94 | void sorted2_mode() { 95 | queue_t *q = queue_create_limited_sorted(3, 1, (int (*)(void *, void *))cmp_int_ptr); 96 | 97 | int t1 = 1; 98 | queue_put(q, &t1); 99 | int t2 = 15; 100 | queue_put(q, &t2); 101 | int t3 = 3; 102 | queue_put(q, &t3); 103 | int t4 = 27; 104 | queue_put(q, &t4); 105 | int t5 = 9; 106 | queue_put(q, &t5); 107 | 108 | int *i; 109 | queue_get(q, (void **)&i); 110 | printf("first element was %d\n", *i); 111 | queue_get(q, (void **)&i); 112 | printf("second element was %d\n", *i); 113 | queue_get(q, (void **)&i); 114 | printf("third element was %d\n", *i); 115 | queue_get(q, (void **)&i); 116 | printf("fourth element was %p\n", i); 117 | queue_get(q, (void **)&i); 118 | printf("fifth element was %p\n", i); 119 | 120 | queue_destroy_complete(q, NULL); 121 | } 122 | 123 | int main(int argc, char *argv[]) { 124 | unsorted_mode(); 125 | sorted_mode(); 126 | sorted2_mode(); 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /queue.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 by Tobias Thiel 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "queue.h" 23 | #include "queue_internal.h" 24 | 25 | queue_t *queue_create(void) { 26 | queue_t *q = (queue_t *)malloc(sizeof(queue_t)); 27 | if(q != NULL) { 28 | q->mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); 29 | if(q->mutex == NULL) { 30 | free(q); 31 | return NULL; 32 | } 33 | pthread_mutex_init(q->mutex, NULL); 34 | 35 | q->cond_get = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); 36 | if(q->cond_get == NULL) { 37 | pthread_mutex_destroy(q->mutex); 38 | free(q->mutex); 39 | free(q); 40 | return NULL; 41 | } 42 | pthread_cond_init(q->cond_get, NULL); 43 | 44 | q->cond_put = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); 45 | if(q->cond_put == NULL) { 46 | pthread_cond_destroy(q->cond_get); 47 | free(q->cond_get); 48 | pthread_mutex_destroy(q->mutex); 49 | free(q->mutex); 50 | free(q); 51 | return NULL; 52 | } 53 | pthread_cond_init(q->cond_put, NULL); 54 | 55 | q->first_el = NULL; 56 | q->last_el = NULL; 57 | q->num_els = 0; 58 | q->max_els = 0; 59 | q->new_data = 1; 60 | q->sort = 0; 61 | q->asc_order = 1; 62 | q->cmp_el = NULL; 63 | } 64 | 65 | return q; 66 | } 67 | 68 | queue_t *queue_create_limited(uintX_t max_elements) { 69 | queue_t *q = queue_create(); 70 | if(q != NULL) 71 | q->max_els = max_elements; 72 | 73 | return q; 74 | } 75 | 76 | queue_t *queue_create_sorted(int8_t asc, int (*cmp)(void *, void *)) { 77 | if(cmp == NULL) 78 | return NULL; 79 | 80 | queue_t *q = queue_create(); 81 | if(q != NULL) { 82 | q->sort = 1; 83 | q->asc_order = asc; 84 | q->cmp_el = cmp; 85 | } 86 | 87 | return q; 88 | } 89 | 90 | queue_t *queue_create_limited_sorted(uintX_t max_elements, int8_t asc, int (*cmp)(void *, void *)) { 91 | if(cmp == NULL) 92 | return NULL; 93 | 94 | queue_t *q = queue_create(); 95 | if(q != NULL) { 96 | q->max_els = max_elements; 97 | q->sort = 1; 98 | q->asc_order = asc; 99 | q->cmp_el = cmp; 100 | } 101 | 102 | return q; 103 | } 104 | 105 | int8_t queue_destroy(queue_t *q) { 106 | if(q == NULL) 107 | return Q_ERR_INVALID; 108 | return queue_destroy_internal(q, 0, NULL); 109 | } 110 | 111 | int8_t queue_destroy_complete(queue_t *q, void (*ff)(void *)) { 112 | if(q == NULL) 113 | return Q_ERR_INVALID; 114 | return queue_destroy_internal(q, 1, ff); 115 | } 116 | 117 | int8_t queue_flush(queue_t *q) { 118 | if(q == NULL) 119 | return Q_ERR_INVALID; 120 | if (0 != queue_lock_internal(q)) 121 | return Q_ERR_LOCK; 122 | 123 | int8_t r = queue_flush_internal(q, 0, NULL); 124 | 125 | if (0 != queue_unlock_internal(q)) 126 | return Q_ERR_LOCK; 127 | return r; 128 | } 129 | 130 | int8_t queue_flush_complete(queue_t *q, void (*ff)(void *)) { 131 | if(q == NULL) 132 | return Q_ERR_INVALID; 133 | if (0 != queue_lock_internal(q)) 134 | return Q_ERR_LOCK; 135 | 136 | int8_t r = queue_flush_internal(q, 1, ff); 137 | 138 | if (0 != queue_unlock_internal(q)) 139 | return Q_ERR_LOCK; 140 | return r; 141 | } 142 | 143 | uintX_t queue_elements(queue_t *q) { 144 | uintX_t ret = UINTX_MAX; 145 | if(q == NULL) 146 | return ret; 147 | if (0 != queue_lock_internal(q)) 148 | return ret; 149 | 150 | ret = q->num_els; 151 | 152 | if (0 != queue_unlock_internal(q)) 153 | return Q_ERR_LOCK; 154 | return ret; 155 | } 156 | 157 | int8_t queue_empty(queue_t *q) { 158 | if(q == NULL) 159 | return Q_ERR_INVALID; 160 | if (0 != queue_lock_internal(q)) 161 | return Q_ERR_LOCK; 162 | 163 | uint8_t ret; 164 | if(q->first_el == NULL || q->last_el == NULL) 165 | ret = 1; 166 | else 167 | ret = 0; 168 | 169 | if (0 != queue_unlock_internal(q)) 170 | return Q_ERR_LOCK; 171 | return ret; 172 | } 173 | 174 | int8_t queue_set_new_data(queue_t *q, uint8_t v) { 175 | if(q == NULL) 176 | return Q_ERR_INVALID; 177 | if (0 != queue_lock_internal(q)) 178 | return Q_ERR_LOCK; 179 | q->new_data = v; 180 | if (0 != queue_unlock_internal(q)) 181 | return Q_ERR_LOCK; 182 | 183 | if(q->new_data == 0) { 184 | // notify waiting threads, when new data isn't accepted 185 | pthread_cond_broadcast(q->cond_get); 186 | pthread_cond_broadcast(q->cond_put); 187 | } 188 | 189 | return Q_OK; 190 | } 191 | 192 | uint8_t queue_get_new_data(queue_t *q) { 193 | if(q == NULL) 194 | return 0; 195 | if (0 != queue_lock_internal(q)) 196 | return 0; 197 | 198 | uint8_t r = q->new_data; 199 | 200 | if (0 != queue_unlock_internal(q)) 201 | return 0; 202 | return r; 203 | } 204 | 205 | int8_t queue_put(queue_t *q, void *el) { 206 | if(q == NULL) 207 | return Q_ERR_INVALID; 208 | if (0 != queue_lock_internal(q)) 209 | return Q_ERR_LOCK; 210 | 211 | int8_t r = queue_put_internal(q, el, NULL); 212 | 213 | if (0 != queue_unlock_internal(q)) 214 | return Q_ERR_LOCK; 215 | return r; 216 | } 217 | 218 | int8_t queue_put_wait(queue_t *q, void *el) { 219 | if(q == NULL) 220 | return Q_ERR_INVALID; 221 | if (0 != queue_lock_internal(q)) 222 | return Q_ERR_LOCK; 223 | 224 | int8_t r = queue_put_internal(q, el, pthread_cond_wait); 225 | 226 | if (0 != queue_unlock_internal(q)) 227 | return Q_ERR_LOCK; 228 | return r; 229 | } 230 | 231 | int8_t queue_get(queue_t *q, void **e) { 232 | *e = NULL; 233 | if(q == NULL) 234 | return Q_ERR_INVALID; 235 | if (0 != queue_lock_internal(q)) 236 | return Q_ERR_LOCK; 237 | 238 | int8_t r = queue_get_internal(q, e, NULL, NULL, NULL); 239 | 240 | if (0 != queue_unlock_internal(q)) 241 | return Q_ERR_LOCK; 242 | return r; 243 | } 244 | 245 | int8_t queue_get_wait(queue_t *q, void **e) { 246 | *e = NULL; 247 | if(q == NULL) 248 | return Q_ERR_INVALID; 249 | if (0 != queue_lock_internal(q)) 250 | return Q_ERR_LOCK; 251 | 252 | int8_t r = queue_get_internal(q, e, pthread_cond_wait, NULL, NULL); 253 | 254 | if (0 != queue_unlock_internal(q)) 255 | return Q_ERR_LOCK; 256 | return r; 257 | } 258 | 259 | int8_t queue_get_filtered(queue_t *q, void **e, int (*cmp)(void *, void *), void *cmpel) { 260 | *e = NULL; 261 | if(q == NULL) 262 | return Q_ERR_INVALID; 263 | if (0 != queue_lock_internal(q)) 264 | return Q_ERR_LOCK; 265 | 266 | int8_t r = queue_get_internal(q, e, NULL, cmp, cmpel); 267 | 268 | if (0 != queue_unlock_internal(q)) 269 | return Q_ERR_LOCK; 270 | return r; 271 | } 272 | 273 | int8_t queue_flush_put(queue_t *q, void (*ff)(void *), void *e) { 274 | if(q == NULL) 275 | return Q_ERR_INVALID; 276 | if (0 != queue_lock_internal(q)) 277 | return Q_ERR_LOCK; 278 | 279 | int8_t r = queue_flush_internal(q, 0, NULL); 280 | r = queue_put_internal(q, e, NULL); 281 | 282 | if (0 != queue_unlock_internal(q)) 283 | return Q_ERR_LOCK; 284 | return r; 285 | } 286 | 287 | int8_t queue_flush_complete_put(queue_t *q, void (*ff)(void *), void *e) { 288 | if(q == NULL) 289 | return Q_ERR_INVALID; 290 | if (0 != queue_lock_internal(q)) 291 | return Q_ERR_LOCK; 292 | 293 | int8_t r = queue_flush_internal(q, 1, ff); 294 | r = queue_put_internal(q, e, NULL); 295 | 296 | if (0 != queue_unlock_internal(q)) 297 | return Q_ERR_LOCK; 298 | return r; 299 | } 300 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __QUEUE_H__ 2 | #define __QUEUE_H__ 3 | 4 | /** 5 | * Copyright (C) 2011 by Tobias Thiel 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include /* (u)intX_t */ 28 | #ifndef _WIN32 29 | #include /* usleep */ 30 | #else 31 | #include /* Sleep */ 32 | #endif 33 | #include /* EBUSY */ 34 | 35 | #include /* pthread_mutex_t, pthread_cond_t */ 36 | 37 | #ifdef _WIN32 38 | #define sleepmilli(x) Sleep(x) 39 | #else 40 | #define sleepmilli(x) usleep(x * 1000) 41 | #endif 42 | 43 | /** 44 | * type which is used for counting the number of elements. 45 | * needed for limited queues 46 | */ 47 | #ifndef UINTX_MAX 48 | typedef uint16_t uintX_t; 49 | #define UINTX_MAX UINT16_MAX 50 | #endif 51 | 52 | /** 53 | * simple macros to reduce necessary casting to void * 54 | * or function pointers 55 | */ 56 | #define DEFINE_Q_DESTROY(fname, type) int8_t fname(queue_t *q, void (*ff)(type *)) { return queue_destroy_complete(q, (void (*)(void *))ff); } 57 | #define DEFINE_Q_FLUSH(fname, type) int8_t fname(queue_t *q, void (*ff)(type *)) { return queue_flush_complete(q, (void (*)(void *))ff); } 58 | #define DEFINE_Q_GET(fname, type) int8_t fname(queue_t *q, type **e) { return queue_get(q, (void **)e); } 59 | #define DEFINE_Q_GET_WAIT(fname, type) int8_t fname(queue_t *q, type **e) { return queue_get_wait(q, (void **)e); } 60 | #define DEFINE_Q_PUT(fname, type) int8_t fname(queue_t *q, type *e) { return queue_put(q, (void *)e); } 61 | #define DEFINE_Q_PUT_WAIT(fname, type) int8_t fname(queue_t *q, type *e) { return queue_put_wait(q, (void *)e); } 62 | #define DEFINE_Q_FLUSH_PUT(fname, type) int8_t fname(queue_t *q, void (*ff)(type *), type *e) { return queue_flush_complete_put(q, (void (*)(void *))ff, (void *)e); } 63 | 64 | /** 65 | * returned error codes, everything except Q_OK should be < 0 66 | */ 67 | typedef enum queue_erros_e { 68 | Q_OK = 0, 69 | Q_ERR_INVALID = -1, 70 | Q_ERR_LOCK = -2, 71 | Q_ERR_MEM = -3, 72 | Q_ERR_NONEWDATA = -4, 73 | Q_ERR_INVALID_ELEMENT = -5, 74 | Q_ERR_INVALID_CB = -6, 75 | Q_ERR_NUM_ELEMENTS = -7 76 | } queue_errors_t; 77 | 78 | typedef struct queue_element_s { 79 | void *data; 80 | struct queue_element_s *next; 81 | } queue_element_t; 82 | 83 | typedef struct queue_s { 84 | queue_element_t *first_el, *last_el; 85 | // (max.) number of elements 86 | uintX_t num_els; 87 | uintX_t max_els; 88 | // no new data allowed 89 | uint8_t new_data; 90 | // sorted queue 91 | int8_t sort; 92 | int8_t asc_order; 93 | int (*cmp_el)(void *, void *); 94 | // multithreaded 95 | pthread_mutex_t *mutex; 96 | pthread_cond_t *cond_get; 97 | pthread_cond_t *cond_put; 98 | } queue_t; 99 | 100 | /** 101 | * initializes and allocates a queue with unlimited elements 102 | * 103 | * returns NULL on error, or a pointer to the queue 104 | */ 105 | queue_t *queue_create(void); 106 | 107 | /** 108 | * initializes and allocates a queue 109 | * 110 | * max_elements - maximum number of elements which are allowed in the queue, == 0 for "unlimited" 111 | * 112 | * returns NULL on error, or a pointer to the queue 113 | */ 114 | queue_t *queue_create_limited(uintX_t max_elements); 115 | 116 | /** 117 | * just like queue_create() 118 | * additionally you can specify a comparator function so that your elements are ordered in the queue 119 | * elements will only be ordered if you create the queue with this method 120 | * the compare function should return 0 if both elements are the same, < 0 if the first is smaller 121 | * and > 0 if the second is smaller 122 | * 123 | * asc - sort in ascending order if not 0 124 | * cmp - comparator function, NULL will create an error 125 | * 126 | * returns NULL on error, or a pointer to the queue 127 | */ 128 | queue_t *queue_create_sorted(int8_t asc, int (*cmp)(void *, void *)); 129 | 130 | /** 131 | * see queue_create_limited() and queue_create_sorted() 132 | */ 133 | queue_t *queue_create_limited_sorted(uintX_t max_elements, int8_t asc, int (*cmp)(void *, void *)); 134 | 135 | /** 136 | * releases the memory internally allocated and destroys the queue 137 | * you have to release the memory the elements in the queue use 138 | * 139 | * q - the queue to be destroyed 140 | */ 141 | int8_t queue_destroy(queue_t *q); 142 | 143 | /** 144 | * in addition to queue_destroy(), this function will also free the memory of your elements 145 | * 146 | * q - the queue to be destroyed 147 | * ff - the free function to be used for the elements (free() if NULL) 148 | */ 149 | int8_t queue_destroy_complete(queue_t *q, void (*ff)(void *)); 150 | 151 | /** 152 | * deletes all the elements from the queue, but does not release their memory 153 | * 154 | * q - the queue 155 | */ 156 | int8_t queue_flush(queue_t *q); 157 | 158 | /** 159 | * just like queue_flush, but releases the memory of the elements 160 | * 161 | * q - the queue 162 | * ff - the free function to be used for the elements (free() if NULL) 163 | */ 164 | int8_t queue_flush_complete(queue_t *q, void (*ff)(void *)); 165 | 166 | /** 167 | * just like queue_flush, followed by an queue_put atomically 168 | * 169 | * q - the queue 170 | * ff - the free function to be used for the elements (free() if NULL) 171 | * e - the element 172 | */ 173 | int8_t queue_flush_put(queue_t *q, void (*ff)(void *), void *e); 174 | 175 | /** 176 | * just like queue_flush_complete, followed by an queue_put atomically 177 | * 178 | * q - the queue 179 | * ff - the free function to be used for the elements (free() if NULL) 180 | * e - the element 181 | */ 182 | int8_t queue_flush_complete_put(queue_t *q, void (*ff)(void *), void *e); 183 | 184 | /** 185 | * returns the number of elements in the queue 186 | * 187 | * q - the queue 188 | * 189 | * returns number of elements or UINTX_MAX if an error occured 190 | */ 191 | uintX_t queue_elements(queue_t *q); 192 | 193 | /** 194 | * returns wether the queue is empty 195 | * returns empty if there was an error 196 | * 197 | * q - the queue 198 | * 199 | * returns zero if queue is NOT empty, < 0 => error 200 | */ 201 | int8_t queue_empty(queue_t *q); 202 | 203 | /** 204 | * put a new element at the end of the queue 205 | * will produce an error if you called queue_no_new_data() 206 | * 207 | * q - the queue 208 | * e - the element 209 | * 210 | * returns 0 if everything worked, > 0 if max_elements is reached, < 0 if error occured 211 | */ 212 | int8_t queue_put(queue_t *q, void *e); 213 | 214 | /** 215 | * the same as queue_put(), but will wait if max_elements is reached, 216 | * until queue_set_new_data(, 0) is called or elements are removed 217 | * 218 | * q - the queue 219 | * e - the element 220 | * 221 | * returns 0 if everything worked, < 0 if error occured 222 | */ 223 | int8_t queue_put_wait(queue_t *q, void *e); 224 | 225 | /** 226 | * get the first element of the queue 227 | * 228 | * q - the queue 229 | * e - pointer which will be set to the element 230 | * 231 | * returns 0 if everything worked, > 0 if no elements in queue, < 0 if error occured 232 | */ 233 | int8_t queue_get(queue_t *q, void **e); 234 | 235 | /** 236 | * the same as queue_get(), but will wait if no elements are in the queue, 237 | * until queue_set_new_data(, 0) is called or new elements are added 238 | * 239 | * q - the queue 240 | * e - pointer which will be set to the element 241 | * 242 | * returns 0 if everything worked, < 0 if error occured 243 | */ 244 | int8_t queue_get_wait(queue_t *q, void **e); 245 | 246 | /** 247 | * gets the first element for which the given compare function returns 0 (equal) 248 | * does NOT wait if no elements in the queue 249 | * the compare function should return 0 if both elements are the same, < 0 if the first is smaller 250 | * and > 0 if the second is smaller 251 | * 252 | * q - the queue 253 | * e - pointer which will be set to the element 254 | * cmp - comparator function, NULL will create an error 255 | * cmpel - element with which should be compared 256 | * 257 | * returns 0 if everything worked, < 0 => error, Q_ERR_NUM_ELEMENTS(<0) if no element fulfills the requirement 258 | */ 259 | int8_t queue_get_filtered(queue_t *q, void **e, int (*cmp)(void *, void *), void *cmpel); 260 | 261 | /** 262 | * sets wether the queue will accept new data 263 | * defaults to 1 on creation 264 | * you should use this function when you're done and queue_put_wait() and queue_get_wait() should return 265 | * queue_put()/queue_put_wait() won't have any effect when new data isn't accepted. 266 | * 267 | * q - the queue 268 | * v - 0 new data NOT accepted 269 | */ 270 | int8_t queue_set_new_data(queue_t *q, uint8_t v); 271 | 272 | /** 273 | * returns wether the queue will accept new data 274 | * also returns 0, if there was an error 275 | * 276 | * q - the queue 277 | * 278 | * return 0 if new data is NOT accepted 279 | */ 280 | uint8_t queue_get_new_data(queue_t *q); 281 | 282 | #endif /* __QUEUE_H__ */ 283 | -------------------------------------------------------------------------------- /queue_internal.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 by Tobias Thiel 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "queue.h" 23 | #include "queue_internal.h" 24 | 25 | int8_t queue_lock_internal(queue_t *q) { 26 | if (q == NULL) 27 | return Q_ERR_INVALID; 28 | // all errors are unrecoverable for us 29 | if(0 != pthread_mutex_lock(q->mutex)) 30 | return Q_ERR_LOCK; 31 | return Q_OK; 32 | } 33 | 34 | int8_t queue_unlock_internal(queue_t *q) { 35 | if (q == NULL) 36 | return Q_ERR_INVALID; 37 | // all errors are unrecoverable for us 38 | if(0 != pthread_mutex_unlock(q->mutex)) 39 | return Q_ERR_LOCK; 40 | return Q_OK; 41 | } 42 | 43 | int8_t queue_destroy_internal(queue_t *q, uint8_t fd, void (*ff)(void *)) { 44 | // this method will not immediately return on error, 45 | // it will try to release all the memory that was allocated. 46 | int error = Q_OK; 47 | // make sure no new data comes and wake all waiting threads 48 | error = queue_set_new_data(q, 0); 49 | error = queue_lock_internal(q); 50 | 51 | // release internal element memory 52 | error = queue_flush_internal(q, fd, ff); 53 | 54 | // destroy lock and queue etc 55 | error = pthread_cond_destroy(q->cond_get); 56 | free(q->cond_get); 57 | error = pthread_cond_destroy(q->cond_put); 58 | free(q->cond_put); 59 | 60 | error = queue_unlock_internal(q); 61 | while(EBUSY == (error = pthread_mutex_destroy(q->mutex))) 62 | sleepmilli(100); 63 | free(q->mutex); 64 | 65 | // destroy queue 66 | free(q); 67 | return error; 68 | } 69 | 70 | int8_t queue_flush_internal(queue_t *q, uint8_t fd, void (*ff)(void *)) { 71 | if(q == NULL) 72 | return Q_ERR_INVALID; 73 | 74 | queue_element_t *qe = q->first_el; 75 | queue_element_t *nqe = NULL; 76 | while(qe != NULL) { 77 | nqe = qe->next; 78 | if(fd != 0 && ff == NULL) { 79 | free(qe->data); 80 | } else if(fd != 0 && ff != NULL) { 81 | ff(qe->data); 82 | } 83 | free(qe); 84 | qe = nqe; 85 | } 86 | q->first_el = NULL; 87 | q->last_el = NULL; 88 | q->num_els = 0; 89 | 90 | return Q_OK; 91 | } 92 | 93 | int8_t queue_put_internal(queue_t *q , void *el, int (*action)(pthread_cond_t *, pthread_mutex_t *)) { 94 | if(q == NULL) // queue not valid 95 | return Q_ERR_INVALID; 96 | 97 | if(q->new_data == 0) { // no new data allowed 98 | return Q_ERR_NONEWDATA; 99 | } 100 | 101 | // max_elements already reached? 102 | // if condition _needs_ to be in sync with while loop below! 103 | if(q->num_els == (UINTX_MAX - 1) || (q->max_els != 0 && q->num_els == q->max_els)) { 104 | if(action == NULL) { 105 | return Q_ERR_NUM_ELEMENTS; 106 | } else { 107 | while ((q->num_els == (UINTX_MAX - 1) || (q->max_els != 0 && q->num_els == q->max_els)) && q->new_data != 0) 108 | action(q->cond_put, q->mutex); 109 | if(q->new_data == 0) { 110 | return Q_ERR_NONEWDATA; 111 | } 112 | } 113 | } 114 | 115 | queue_element_t *new_el = (queue_element_t *)malloc(sizeof(queue_element_t)); 116 | if(new_el == NULL) { // could not allocate memory for new elements 117 | return Q_ERR_MEM; 118 | } 119 | new_el->data = el; 120 | new_el->next = NULL; 121 | 122 | if(q->sort == 0 || q->first_el == NULL) { 123 | // insert at the end when we don't want to sort or the queue is empty 124 | if(q->last_el == NULL) 125 | q->first_el = new_el; 126 | else 127 | q->last_el->next = new_el; 128 | q->last_el = new_el; 129 | } else { 130 | // search appropriate place to sort element in 131 | queue_element_t *s = q->first_el; // s != NULL, because of if condition above 132 | queue_element_t *t = NULL; 133 | int asc_first_el = q->asc_order != 0 && q->cmp_el(s->data, el) >= 0; 134 | int desc_first_el = q->asc_order == 0 && q->cmp_el(s->data, el) <= 0; 135 | 136 | if(asc_first_el == 0 && desc_first_el == 0) { 137 | // element will be inserted between s and t 138 | for(s = q->first_el, t = s->next; s != NULL && t != NULL; s = t, t = t->next) { 139 | if(q->asc_order != 0 && q->cmp_el(s->data, el) <= 0 && q->cmp_el(el, t->data) <= 0) { 140 | // asc: s <= e <= t 141 | break; 142 | } else if(q->asc_order == 0 && q->cmp_el(s->data, el) >= 0 && q->cmp_el(el, t->data) >= 0) { 143 | // desc: s >= e >= t 144 | break; 145 | } 146 | } 147 | // actually insert 148 | s->next = new_el; 149 | new_el->next = t; 150 | if(t == NULL) 151 | q->last_el = new_el; 152 | } else if(asc_first_el != 0 || desc_first_el != 0) { 153 | // add at front 154 | new_el->next = q->first_el; 155 | q->first_el = new_el; 156 | } 157 | } 158 | q->num_els++; 159 | // notify only one waiting thread, so that we don't have to check and fall to sleep because we were to slow 160 | pthread_cond_signal(q->cond_get); 161 | 162 | return Q_OK; 163 | } 164 | 165 | int8_t queue_get_internal(queue_t *q, void **e, int (*action)(pthread_cond_t *, pthread_mutex_t *), int (*cmp)(void *, void *), void *cmpel) { 166 | if(q == NULL) { // queue not valid 167 | *e = NULL; 168 | return Q_ERR_INVALID; 169 | } 170 | 171 | // are elements in the queue? 172 | if(q->num_els == 0) { 173 | if(action == NULL) { 174 | *e = NULL; 175 | return Q_ERR_NUM_ELEMENTS; 176 | } else { 177 | while(q->num_els == 0 && q->new_data != 0) 178 | action(q->cond_get, q->mutex); 179 | if (q->num_els == 0 && q->new_data == 0) 180 | return Q_ERR_NONEWDATA; 181 | } 182 | } 183 | 184 | // get first element (which fulfills the requirements) 185 | queue_element_t *el_prev = NULL, *el = q->first_el; 186 | while(cmp != NULL && el != NULL && 0 != cmp(el, cmpel)) { 187 | el_prev = el; 188 | el = el->next; 189 | } 190 | 191 | if(el != NULL && el_prev == NULL) { 192 | // first element is removed 193 | q->first_el = el->next; 194 | if(q->first_el == NULL) 195 | q->last_el = NULL; 196 | q->num_els--; 197 | *e = el->data; 198 | free(el); 199 | } else if(el != NULL && el_prev != NULL) { 200 | // element in the middle is removed 201 | el_prev->next = el->next; 202 | q->num_els--; 203 | *e = el->data; 204 | free(el); 205 | } else { 206 | // element is invalid 207 | *e = NULL; 208 | return Q_ERR_INVALID_ELEMENT; 209 | } 210 | 211 | // notify only one waiting thread 212 | pthread_cond_signal(q->cond_put); 213 | 214 | return Q_OK; 215 | } 216 | -------------------------------------------------------------------------------- /queue_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef __QUEUE_INTERNAL_H__ 2 | #define __QUEUE_INTERNAL_H__ 3 | 4 | /** 5 | * Copyright (C) 2011 by Tobias Thiel 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | /** 31 | * ATTENTION: 32 | * these functions are internal and should not be used directly. 33 | * they may _not_ lock properly, expecting the caller to do so 34 | */ 35 | 36 | /** 37 | * locks the queue 38 | * returns 0 on success, else not usable 39 | */ 40 | int8_t queue_lock_internal(queue_t *q); 41 | 42 | /** 43 | * unlocks the queue 44 | * returns 0 on success, else not usable 45 | */ 46 | int8_t queue_unlock_internal(queue_t *q); 47 | 48 | /** 49 | * adds an element to the queue. 50 | * when action is NULL the function returns with an error code. 51 | * queue _has_ to be locked. 52 | * 53 | * q - the queue 54 | * el - the element 55 | * action - specifies what should be executed if max_elements is reached. 56 | * 57 | * returns < 0 => error, 0 okay 58 | */ 59 | int8_t queue_put_internal(queue_t *q, void *el, int (*action)(pthread_cond_t *, pthread_mutex_t *)); 60 | 61 | /** 62 | * gets the first element in the queue. 63 | * when action is NULL the function returns with an error code. 64 | * queue _has_ to be locked. 65 | * 66 | * q - the queue 67 | * e - element pointer 68 | * action - specifies what should be executed if there are no elements in the queue 69 | * cmp - comparator function, NULL will create an error 70 | * cmpel - element with which should be compared 71 | * 72 | * returns < 0 => error, 0 okay 73 | */ 74 | int8_t queue_get_internal(queue_t *q, void **e, int (*action)(pthread_cond_t *, pthread_mutex_t *), int (*cmp)(void *, void *), void *cmpel); 75 | 76 | /** 77 | * destroys a queue. 78 | * queue will be locked. 79 | * 80 | * q - the queue 81 | * fd - should element data be freed? 0 => No, Otherwise => Yes 82 | * ff - function to release the memory, NULL => free() 83 | */ 84 | int8_t queue_destroy_internal(queue_t *q, uint8_t fd, void (*ff)(void *)); 85 | 86 | /** 87 | * flushes a queue. 88 | * deletes all elements in the queue. 89 | * queue _has_ to be locked. 90 | * 91 | * q - the queue 92 | * fd - should element data be freed? 0 => No, Otherwise => Yes 93 | * ff - function to release the memory, NULL => free() 94 | */ 95 | int8_t queue_flush_internal(queue_t *q, uint8_t fd, void (*ff)(void *)); 96 | 97 | #endif /* __QUEUE_INTERNAL_H__ */ 98 | --------------------------------------------------------------------------------