diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/algo.c | 230 | ||||
| -rw-r--r-- | src/buffer.c | 278 | ||||
| -rw-r--r-- | src/file_null.c | 275 | ||||
| -rw-r--r-- | src/file_posix.c | 253 | ||||
| -rw-r--r-- | src/file_win.c | 195 | ||||
| -rw-r--r-- | src/hashmap.c | 199 | ||||
| -rw-r--r-- | src/io.c | 186 | ||||
| -rw-r--r-- | src/queue.c | 135 | ||||
| -rw-r--r-- | src/thread.c | 152 | ||||
| -rw-r--r-- | src/thread_null.c | 280 | ||||
| -rw-r--r-- | src/thread_posix.c | 84 | ||||
| -rw-r--r-- | src/thread_win.c | 164 |
12 files changed, 2266 insertions, 165 deletions
@@ -1,34 +1,72 @@ #include <bh/algo.h> #include <string.h> -#include <stdio.h> -void bh_swap(void *lhs, - void *rhs, +/** + * \defgroup algo Algorithms + * + * Common algorithms (search, swap, heap, etc) + * \{ + */ + +/** + * Swaps \a size bytes between the object pointed to by \a src and object + * pointed to by \a dest. + * + * If the objects overlap, the behavior is undefined. + * + * If either \a src or \a dest is an invalid or null pointer, the behavior is + * undefined. + * + * \param dest Pointer to the memory location to swap to + * \param src Pointer to the memory location to swap from + * \param size Number of bytes to swap + */ +void bh_swap(void *dest, + void *src, size_t size) { int tmp; - /* Swap values in int sized chunks */ + /* Swap bytes in int-sized chunks */ while (size >= sizeof(tmp)) { - memmove(&tmp, lhs, sizeof(tmp)); - memmove(lhs, rhs, sizeof(tmp)); - memmove(rhs, &tmp, sizeof(tmp)); + memmove(&tmp, dest, sizeof(tmp)); + memmove(dest, src, sizeof(tmp)); + memmove(src, &tmp, sizeof(tmp)); - lhs = (char *)lhs + sizeof(tmp); - rhs = (char *)rhs + sizeof(tmp); + dest = (char *)dest + sizeof(tmp); + src = (char *)src + sizeof(tmp); size -= sizeof(tmp); } - /* Swap the rest */ + /* Swap the remaining size */ if (size) { - memmove(&tmp, lhs, size); - memmove(lhs, rhs, size); - memmove(rhs, &tmp, size); + memmove(&tmp, dest, size); + memmove(dest, src, size); + memmove(src, &tmp, size); } } +/** + * Partitions the \a array of \a size elements of \a element bytes, relative to + * the specified \a pivot element. Function pointed by \a equal is used for + * comparison. + * + * If the \a pivot element is part of the partitioned \a array, function will + * behaive the same way, as if \a pivot element wasn't part of the array. + * + * If \a equal indicates two elements as equivalent, their order in the + * resulting partitioned array is unspecified. + * + * \param pivot Pointer to the pivot element + * \param array Pointer to the array to partition + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + * + * \return Pointer to the first element of the second partition. + */ void *bh_partition(void *pivot, void *array, size_t size, @@ -74,6 +112,17 @@ void *bh_partition(void *pivot, return j + element; } +/** + * Sorts the \a array of \a size elements of \a element bytes. Function pointed + * by \a equal is used for comparision. + * + * This sorting does not make any gurantees about time or space complexity. + * + * \param array Pointer to the array to sort + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + */ void bh_sort(void *array, size_t size, size_t element, @@ -83,6 +132,23 @@ void bh_sort(void *array, bh_sort_intro(array, size, element, equal); } + +/** + * Sorts the \a array of \a size elements of \a element bytes with insertion + * sort. Function pointed by \a equal is used for comparision. + * + * This sorting has following time complexity: + * - best case O(n) + * - average case O(n^2) + * - worst case O(n^2) + * + * This sorting has O(1) space complexity. + * + * \param array Pointer to the array to sort + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + */ void bh_sort_insert(void *array, size_t size, size_t element, @@ -109,6 +175,20 @@ void bh_sort_insert(void *array, } +/** + * Sorts the \a array of \a size elements of \a element bytes with shell sort. + * Function pointed by \a equal is used for comparision. + * + * This sorting has the O(n log n) best cast time complexity. Average and worst + * cases are implementation dependant. + * + * This sorting has O(1) space complexity. + * + * \param array Pointer to the array to sort + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + */ void bh_sort_shell(void *array, size_t size, size_t element, @@ -118,7 +198,7 @@ void bh_sort_shell(void *array, const size_t *gap; size_t i, j; - /* Shell sort with A102549 sequence*/ + /* Shell sort with A102549 sequence */ for (gap = gaps; gap < gaps + sizeof(gaps) / sizeof(*gaps); gap++) { for (i = *gap; i < size; i++) @@ -201,6 +281,22 @@ static void bh_sort_intro_r(void *array, } } +/** + * Sorts the \a array of \a size elements of \a element bytes with insertion + * sort. Function pointed by \a equal is used for comparision. + * + * This sorting has following time complexity: + * - best case O(n log n) + * - average case O(n log n) + * - worst case O(n log n) + * + * This sorting has O(log n) space complexity. + * + * \param array Pointer to the array to sort + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + */ void bh_sort_intro(void *array, size_t size, size_t element, @@ -221,6 +317,22 @@ void bh_sort_intro(void *array, bh_sort_intro_r(array, size, element, equal, depth); } +/** + * Sorts the \a array of \a size elements of \a element bytes with insertion + * sort. Function pointed by \a equal is used for comparision. + * + * This sorting has following time complexity: + * - best case O(n) + * - average case O(n log n) + * - worst case O(n log n) + * + * This sorting has O(1) space complexity. + * + * \param array Pointer to the array to sort + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + */ void bh_sort_heap(void *array, size_t size, size_t element, @@ -232,7 +344,15 @@ void bh_sort_heap(void *array, bh_heap_remove(array, size--, element, equal); } - +/** + * Heapifes the \a array of \a size elements of \a element bytes. Function + * pointed by \a equal is used for comparision. + * + * \param array Pointer to the array to heapify + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparision function + */ void bh_heap_make(void *array, size_t size, size_t element, @@ -279,6 +399,15 @@ void bh_heap_make(void *array, } } +/** + * Removes top element from heapified \a array of \a size elements of \a + * elements bytes. Function pointed by \a equal is used for comparasion. + * + * \param array Pointer to the array to remove element from + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparasion function + */ void bh_heap_remove(void *array, size_t size, size_t element, @@ -323,6 +452,16 @@ void bh_heap_remove(void *array, } } +/** + * Inserts new element into heapified \a array of \a size elements of \a + * elements bytes. Function pointed by \a equal is used for comparasion. + * + * \param value Pointer to the value to be inserted + * \param array Pointer to the array to remove element from + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparasion function + */ void bh_heap_insert(void *value, void *array, size_t size, @@ -358,3 +497,64 @@ void bh_heap_insert(void *value, break; } } + +/** + * Replaces top element of the heapified \a array of \a size elements of \a + * elements bytes. Function pointed by \a equal is used for comparasion. + * + * This function is equal to two consecutive operations of remove and insert. + * + * \param value Pointer to the value to be inserted + * \param array Pointer to the array to remove element from + * \param size Number of elements in the array + * \param element Size of each element in the array in bytes + * \param equal Comparasion function + */ +void bh_heap_replace(void *value, + void *array, + size_t size, + size_t element, + bh_equal_cb_t equal) +{ + char *start, *end, *current, *left, *right; + + if (size <= 1) + return; + + /* Calculate start, end and children pointers */ + start = (char *)array; + end = start + size * element; + current = start; + left = start + (current - start) * 2 + element; + right = left + element; + + /* Copy supplied element to the first (top) position */ + memmove(current, value, element); + + /* Iterate until we reach the end */ + while (left < end) + { + char *biggest; + + /* Determine biggest child */ + biggest = left; + if (right < end && equal(left, right) < 0) + biggest = right; + + /* Compare biggest child with current */ + if (equal(current, biggest) < 0) + { + /* Swap content and recalculate children pointers */ + bh_swap(current, biggest, element); + current = biggest; + left = start + (current - start) * 2 + element; + right = left + element; + } + else + break; + } +} + +/** + * \} + */
\ No newline at end of file diff --git a/src/buffer.c b/src/buffer.c new file mode 100644 index 0000000..c66c6ae --- /dev/null +++ b/src/buffer.c @@ -0,0 +1,278 @@ +#include <bh/internal/buffer.h> +#include <stdlib.h> +#include <string.h> + +static const bh_io_table_t bh_buffer_table = { + (int (*)(struct bh_io_s *, int)) bh_buffer_open_base, + (void (*)(struct bh_io_s *)) bh_buffer_close_base, + (int (*)(struct bh_io_s *)) bh_buffer_is_open_base, + (size_t (*)(struct bh_io_s *, char *, size_t)) bh_buffer_read_base, + (size_t (*)(struct bh_io_s *, const char *, size_t)) bh_buffer_write_base, + (void (*)(struct bh_io_s *)) bh_buffer_flush_base, + (int (*)(struct bh_io_s *, bh_off_t, int)) bh_buffer_seek_base, + (bh_off_t (*)(struct bh_io_s *)) bh_buffer_size_base, + (bh_off_t (*)(struct bh_io_s *)) bh_buffer_tell_base, + (bh_off_t (*)(struct bh_io_s *)) bh_buffer_available_base, + (void (*)(struct bh_io_s *)) bh_buffer_clear_base, + (void (*)(struct bh_io_s *)) bh_buffer_destroy +}; + +bh_buffer_t *bh_buffer_new(void) +{ + bh_buffer_t *result; + + /* Allocate and initialize buffer structure */ + result = malloc(sizeof(*result)); + if (result && bh_buffer_init(result)) + { + /* Something went wrong - free allocated memory */ + free(result); + result = NULL; + } + + return result; +} + +void bh_buffer_free(bh_buffer_t *buffer) +{ + bh_buffer_destroy(buffer); + free(buffer); +} + +int bh_buffer_init(bh_buffer_t *buffer) +{ + bh_io_init(&buffer->base, &bh_buffer_table); + + buffer->data = NULL; + buffer->capacity = 0; + buffer->size = 0; + buffer->at = 0; + buffer->mode = 0; + + return BH_OK; +} + +void bh_buffer_destroy(bh_buffer_t *buffer) +{ + bh_buffer_close(buffer); + if (buffer->data) + free(buffer->data); +} + +const char *bh_buffer_data(bh_buffer_t *buffer) +{ + return buffer->data; +} + +void bh_buffer_set_data(bh_buffer_t *buffer, + const char *data, + size_t size) +{ + buffer->size = 0; + bh_buffer_write_base(buffer, data, size); +} + +size_t bh_buffer_capacity(bh_buffer_t *buffer) +{ + return buffer->capacity; +} + +int bh_buffer_reserve(bh_buffer_t *buffer, + size_t size) +{ + char *data = NULL; + + /* New capacity can't be less then current buffer size */ + if (size < buffer->size) + size = buffer->size; + + /* Prevent same size reallocation */ + if (buffer->capacity == size) + return BH_OK; + + /* Allocate new memory for the buffer */ + if (size) + { + data = malloc(size); + if (!data) + return BH_OOM; + + /* Copy data */ + if (buffer->size) + memmove(data, buffer->data, buffer->size); + } + + /* Free previosly allocated memory */ + if (buffer->data) + free(buffer->data); + + /* Update buffer fields */ + buffer->data = data; + buffer->capacity = size; + + return BH_OK; +} + +int bh_buffer_open_base(bh_buffer_t *buffer, + int mode) +{ + /* Check if buffer is already open */ + if (buffer->mode != 0) + return BH_OK; + + /* Update buffer mode field */ + buffer->mode = mode; + + /* Determine open mode */ + switch (mode & BH_IO_MASK) + { + case BH_IO_OPEN: + case BH_IO_CREATE: + case BH_IO_APPEND: + break; + + case BH_IO_TRUNCATE: + buffer->size = 0; + break; + + default: + return BH_NO_IMPL; + } + + return BH_OK; +} + +void bh_buffer_close_base(bh_buffer_t *buffer) +{ + buffer->mode = 0; +} + +int bh_buffer_is_open_base(bh_buffer_t *buffer) +{ + return buffer->mode != 0; +} + +size_t bh_buffer_read_base(bh_buffer_t *buffer, + char *data, + size_t size) +{ + /* Check if buffer openned in read mode */ + if (!(buffer->mode & BH_IO_READ)) + { + buffer->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Calculate maximum available size for reading */ + if (size > buffer->size - buffer->at) + size = buffer->size - buffer->at; + + /* Perform reading */ + if (size) + { + memmove(data, buffer->data + buffer->at, size); + buffer->at += size; + } + + /* Check for end of file */ + if (!size) + buffer->base.flags |= BH_IO_EOF; + else + buffer->base.flags &= ~BH_IO_EOF; + + return size; +} + +size_t bh_buffer_write_base(bh_buffer_t *buffer, + const char *data, + size_t size) +{ + size_t capacity = 0; + + /* Check if buffer is openned in write mode */ + if (!(buffer->mode & BH_IO_WRITE)) + { + buffer->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Adjust at position */ + if ((buffer->mode & BH_IO_MASK) == BH_IO_APPEND) + buffer->at = buffer->size; + + /* Calculate required capacity and check for overflow */ + capacity = buffer->at + size; + if (capacity < size) + { + buffer->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Try to grow buffer */ + if (buffer->capacity < capacity) + bh_buffer_reserve(buffer, capacity); + + /* Calculate maximum write size */ + if (size > buffer->capacity - buffer->at) + size = buffer->capacity - buffer->at; + + /* Perform writing */ + if (size) + { + memmove(buffer->data + buffer->at, data, size); + buffer->at += size; + buffer->size += size; + } + + /* Check for end of file */ + if (!size) + buffer->base.flags |= BH_IO_EOF; + else + buffer->base.flags &= ~BH_IO_EOF; + + return size; +} + +void bh_buffer_flush_base(bh_buffer_t *buffer) +{ + (void)buffer; +} + +int bh_buffer_seek_base(bh_buffer_t *buffer, + bh_off_t pos, + int dir) +{ + switch (dir) + { + case BH_IO_SET: buffer->at = pos; break; + case BH_IO_CURRENT: buffer->at += pos; break; + case BH_IO_END: buffer->at = buffer->size - pos; break; + default: return BH_NO_IMPL; + } + + if (buffer->at > buffer->size) + buffer->at = buffer->size; + + return BH_OK; +} + +bh_off_t bh_buffer_size_base(bh_buffer_t *buffer) +{ + return buffer->size; +} + +bh_off_t bh_buffer_tell_base(bh_buffer_t *buffer) +{ + return buffer->at; +} + +bh_off_t bh_buffer_available_base(bh_buffer_t *buffer) +{ + return buffer->size - buffer->at; +} + +void bh_buffer_clear_base(bh_buffer_t *buffer) +{ + buffer->base.flags &= ~BH_IO_ERROR; +} + diff --git a/src/file_null.c b/src/file_null.c new file mode 100644 index 0000000..75684d8 --- /dev/null +++ b/src/file_null.c @@ -0,0 +1,275 @@ +#include <bh/internal/file.h> + +/** + * \defgroup file File IO + * + * File input/output API + * \{ + */ + +/** + * Creates new file object with specified \a path to file. + * + * \param path Path to the file + * + * \return On success, returns new file object. + * \return On failure, returns null pointer. + */ +bh_file_t *bh_file_new(const char *path) +{ + (void)path; + return NULL; +} + +/** + * Frees the \a file object. + * + * Before freeing the file object, this function ensures that underlying file + * was closed. + * + * \param file Pointer to the file object + */ +void bh_file_free(bh_file_t *file) +{ + (void)file; +} + +/** + * Initializes the \a file object with specified \a path to file. + * + * \param file Pointer to the file object + * \param path Path to the file + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int bh_file_init(bh_file_t *file, + const char *path) +{ + (void)file; + (void)path; + return BH_NO_IMPL; +} + +/** + * Destroyes the \a file object. + * + * Before destroying the file object, this function ensures that underlying + * file was closed. + * + * \param file Pointer to the file object + */ +void bh_file_destroy(bh_file_t *file) +{ + (void)file; +} + +/** + * Opens the \a file object for specified \a mode of operation. + * + * \param file Pointer to the file object + * \param mode Bitmask determining access mode + * + * \return On success, returns zero. + * \return On failure, returns error code. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +int bh_file_open_base(bh_file_t *file, + int mode) +{ + (void)file; + (void)mode; + return BH_NO_IMPL; +} + +/** + * Closes the \a file object. + * + * \param file Pointer to the file object + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +void bh_file_close_base(bh_file_t *file) +{ + (void)file; +} + +/** + * Checks if the \a file is open. + * + * \param file Pointer to the file object + * + * \return If file object is open - returns non-zero. + * \return If file object is closed - returns zero. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +int bh_file_is_open_base(bh_file_t *file) +{ + (void)file; + return 0; +} + +/** + * Reads up to \a size amount of bytes from the \a file object into memory + * buffer pointed by \a data pointer. + * + * \param file Pointer to the file object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * + * \return On success, returns number of bytes successfuly read. + * \return On failure, returns zero. + * + * \note To check for end-of-file or error see bh_io_eof and bh_io_error. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +size_t bh_file_read_base(bh_file_t *file, + char *data, + size_t size) +{ + (void)file; + (void)data; + (void)size; + return 0; +} + +/** + * Writes up to \a size amount of bytes to the \a file object from memory + * buffer pointed by \a data pointer. + * + * \param file Pointer to the file object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * + * \return On success, returns number of bytes successfuly written. + * \return On failure, returns zero. + * + * \note To check for error see bh_io_error. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +size_t bh_file_write_base(bh_file_t *file, + const char *data, + size_t size) +{ + (void)file; + (void)data; + (void)size; + return 0; +} + +/** + * Synchronizes the \a file object (if possible). + * + * In most cases, this function causes any unwritten/buffered data to be + * written. + * + * \param file Pointer to the file object + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +void bh_file_flush_base(bh_file_t *file) +{ + (void)file; +} + +/** + * Seeks the \a file object in the specified direction \a dir and \a offset + * (if possible). + * + * \param file Pointer to the file object + * \param offset Number of bytes to seek in specified direciton + * \param dir Seeking direction + * + * \return On success, returns zero. + * \return On failure, returns error code. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +int bh_file_seek_base(bh_file_t *file, + bh_off_t off, + int dir) +{ + (void)file; + (void)off; + (void)dir; + return BH_NO_IMPL; +} + +/** + * Returns total size of the \a file object (if possible) + * + * \param file Pointer to the file object + * + * \return On success, returns total size of the file object. + * \return On failure, returns -1. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +bh_off_t bh_file_size_base(bh_file_t *file) +{ + (void)file; + return -1; +} + +/** + * Returns current position in the \a file object (if possible). + * + * \param file Pointer to the io object + * + * \return On success, returns current position in the file object. + * \return On failure, returns -1. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +bh_off_t bh_file_tell_base(bh_file_t *file) +{ + (void)file; + return -1; +} + +/** + * Returns available number of bytes to be read from the \a file object. + * + * \param file Pointer to the file object + * + * \return On success, returns number of available bytes to be read. + * \return On failure, returns zero. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +bh_off_t bh_file_available_base(bh_file_t *file) +{ + (void)file; + return 0; +} + +/** + * Clears error of the \a file object. + * + * \param file Pointer to the file object + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +void bh_file_clear_base(bh_file_t *file) +{ + (void)file; +} + +/** + * \} + */ diff --git a/src/file_posix.c b/src/file_posix.c index e69de29..0ffbf5e 100644 --- a/src/file_posix.c +++ b/src/file_posix.c @@ -0,0 +1,253 @@ +#include <bh/internal/file.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +static const bh_io_table_t bh_file_table = { + (int (*)(struct bh_io_s *, int)) bh_file_open_base, + (void (*)(struct bh_io_s *)) bh_file_close_base, + (int (*)(struct bh_io_s *)) bh_file_is_open_base, + (size_t (*)(struct bh_io_s *, char *, size_t)) bh_file_read_base, + (size_t (*)(struct bh_io_s *, const char *, size_t)) bh_file_write_base, + (void (*)(struct bh_io_s *)) bh_file_flush_base, + (int (*)(struct bh_io_s *, bh_off_t, int)) bh_file_seek_base, + (bh_off_t (*)(struct bh_io_s *)) bh_file_size_base, + (bh_off_t (*)(struct bh_io_s *)) bh_file_tell_base, + (bh_off_t (*)(struct bh_io_s *)) bh_file_available_base, + (void (*)(struct bh_io_s *)) bh_file_clear_base, + (void (*)(struct bh_io_s *)) bh_file_destroy +}; + +bh_file_t *bh_file_new(const char *path) +{ + bh_file_t *result; + + /* Allocate and initialize file structure */ + result = malloc(sizeof(*result)); + if (result && bh_file_init(result, path)) + { + /* Something went wrong - free allocated memory */ + free(result); + result = NULL; + } + + return result; +} + +void bh_file_free(bh_file_t *file) +{ + bh_file_destroy(file); + free(file); +} + +int bh_file_init(bh_file_t *file, + const char *path) +{ + /* Ensure that path is not empty */ + if (!path) + return BH_INVALID; + + /* Initialize base io object */ + bh_io_init(&file->base, &bh_file_table); + + /* Initialize file io object */ + file->handle = -1; + file->mode = 0; + file->path = strdup(path); + + return BH_OK; +} + +void bh_file_destroy(bh_file_t *file) +{ + /* Close the file */ + bh_file_close(file); + free(file->path); +} + +int bh_file_open_base(bh_file_t *file, + int mode) +{ + /* Set open mode for 0666 permisions */ + mode_t open_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + int open_flags = 0; + + /* Check if file is already opened */ + if (file->handle != -1) + return BH_OK; + + /* Determine read/write flags */ + if (mode & BH_IO_READ_WRITE) + open_flags |= O_RDWR; + else if (mode & BH_IO_READ) + open_flags |= O_RDONLY; + else if (mode & BH_IO_WRITE) + open_flags |= O_WRONLY; + else + return BH_INVALID; + + /* Determine open mode */ + switch (mode & BH_IO_MASK) + { + case BH_IO_OPEN: break; + case BH_IO_CREATE: open_flags |= O_CREAT | O_EXCL; break; + case BH_IO_APPEND: open_flags |= O_CREAT | O_APPEND; break; + case BH_IO_TRUNCATE: open_flags |= O_CREAT | O_TRUNC; break; + + default: + return BH_NO_IMPL; + } + + /* Open file */ + file->handle = open(file->path, open_flags, open_mode); + + /* Check for errors */ + if (file->handle == -1) + return BH_ERROR; + + return BH_OK; +} + +void bh_file_close_base(bh_file_t *file) +{ + /* Close file if it's open */ + if (file->handle != -1) + close(file->handle); + + /* Set handle to invalid value */ + file->handle = -1; +} + +int bh_file_is_open_base(bh_file_t *file) +{ + /* If handle is not -1 - then file is open */ + return file->handle != -1; +} + +size_t bh_file_read_base(bh_file_t *file, + char *data, + size_t size) +{ + ssize_t readed; + + /* Check if file is open */ + if (file->handle == -1) + { + /* Error occured - set error bit */ + file->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Read data from file */ + readed = read(file->handle, data, size); + if (readed < 0) + { + /* Error occured - set error bit */ + file->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Check for end of file */ + if (!readed) + file->base.flags |= BH_IO_EOF; + else + file->base.flags &= ~BH_IO_EOF; + + /* Return number of readed bytes */ + return readed; +} + +size_t bh_file_write_base(bh_file_t *file, + const char *data, + size_t size) +{ + ssize_t written; + + /* Check if file is open */ + if (file->handle == -1) + { + /* Error occured - set error bit */ + file->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Write data to file */ + written = write(file->handle, data, size); + if (written < 0) + { + /* Error occured - set error bit */ + file->base.flags |= BH_IO_ERROR; + return 0; + } + + /* Check for end of file */ + if (!written) + file->base.flags |= BH_IO_EOF; + else + file->base.flags &= ~BH_IO_EOF; + + /* Return number of written bytes */ + return written; +} + +void bh_file_flush_base(bh_file_t *file) +{ + /* Check if file is open */ + if (file->handle == -1) + return; + + /* Signal OS to flush data from the internal buffers */ + fsync(file->handle); +} + +int bh_file_seek_base(bh_file_t *file, + bh_off_t pos, + int dir) +{ + /* Check if file is open */ + if (file->handle == -1) + return BH_ERROR; + + /* Seek to desired location */ + if (lseek(file->handle, pos, dir) == -1); + return BH_ERROR; + + return BH_OK; +} + +bh_off_t bh_file_size_base(bh_file_t *file) +{ + struct stat sb; + + /* Check if file is open */ + if (file->handle == -1) + return -1; + + /* Get file size from the OS */ + if (fstat(file->handle, &sb)) + return -1; + + return sb.st_size; +} + +bh_off_t bh_file_tell_base(bh_file_t *file) +{ + /* Check if file is open */ + if (file->handle == -1) + return -1; + + /* Get current file position */ + return lseek(file->handle, 0, SEEK_CUR); +} + +bh_off_t bh_file_available_base(bh_file_t *file) +{ + /* Get available bytes for reading */ + return bh_file_size_base(file) - bh_file_tell_base(file); +} + +void bh_file_clear_base(bh_file_t *file) +{ + file->base.flags &= ~BH_IO_ERROR; +} diff --git a/src/file_win.c b/src/file_win.c index e69de29..e31e0de 100644 --- a/src/file_win.c +++ b/src/file_win.c @@ -0,0 +1,195 @@ +#include <bh/internal/file.h> + +static const bh_io_table_t bh_file_table = { + (int (*)(struct bh_io_s *, int)) bh_file_open_base, + (void (*)(struct bh_io_s *)) bh_file_close_base, + (int (*)(struct bh_io_s *)) bh_file_is_open_base, + (size_t (*)(struct bh_io_s *, char *, size_t)) bh_file_read_base, + (size_t (*)(struct bh_io_s *, const char *, size_t)) bh_file_write_base, + (void (*)(struct bh_io_s *)) bh_file_flush_base, + (int (*)(struct bh_io_s *, bh_off_t, int)) bh_file_seek_base, + (bh_off_t (*)(struct bh_io_s *)) bh_file_size_base, + (bh_off_t (*)(struct bh_io_s *)) bh_file_tell_base, + (bh_off_t (*)(struct bh_io_s *)) bh_file_available_base, + (void (*)(struct bh_io_s *)) bh_file_clear_base, + (void (*)(struct bh_io_s *)) bh_file_destroy +}; + +bh_file_t *bh_file_new(const char *path) +{ + bh_file_t *result; + + result = malloc(sizeof(*result)); + if (result && bh_file_init(result, path)) + { + free(result); + result = NULL; + } + + return result; +} + +void bh_file_free(bh_file_t *file) +{ + bh_file_destroy(file); + free(file); +} + +int bh_file_init(bh_file_t *file, + const char *path) +{ + if (!path) + return BH_INVALID; + + file->handle = INVALID_HANDLE_VALUE; + file->mode = 0; + file->path = strdup(path); + file->base.table = &bh_file_table; + + return BH_OK; +} + +int bh_file_open_base(bh_file_t *file, + int mode) +{ + DWORD access = 0, how = 0; + + if (mode & BH_IO_READ) + access |= GENERIC_READ; + if (mode & BH_IO_WRITE) + access |= GENERIC_WRITE; + + switch (mode & BH_IO_MASK) + { + case BH_IO_OPEN: how = OPEN_EXISTING; break; + case BH_IO_CREATE: how = CREATE_ALWAYS; break; + case BH_IO_APPEND: how = OPEN_ALWAYS; break; + + default: + case BH_IO_TRUNCATE: + return BH_NO_IMPL; + } + + file->mode = mode; + file->handle = CreateFileA(file->path, access, FILE_SHARE_READ, NULL, how, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file->handle == INVALID_HANDLE_VALUE) + { + /* Error handling */ + switch (GetLastError()) + { + case ERROR_FILE_EXISTS: return BH_FOUND; + case ERROR_FILE_NOT_FOUND: return BH_NOT_FOUND; + default: return BH_ERROR; + } + } + + return BH_OK; +} + +void bh_file_close_base(bh_file_t *file) +{ + if (file->handle != INVALID_HANDLE_VALUE) + CloseHandle(file->handle); + + file->handle = INVALID_HANDLE_VALUE; +} + +int bh_file_is_open_base(bh_file_t *file) +{ + return file->handle != INVALID_HANDLE_VALUE; +} + +void bh_file_destroy(bh_file_t *file) +{ + bh_file_close(file); + free(file->path); +} + +size_t bh_file_read_base(bh_file_t *file, + char *data, + size_t size) +{ + DWORD readed; + + if (!ReadFile(file->handle, data, (DWORD)size, &readed, NULL)) + { + file->base.flags |= BH_IO_ERROR; + return 0; + } + + if (!readed) + file->base.flags |= BH_IO_EOF; + else + file->base.flags &= ~BH_IO_EOF; + + return readed; +} + +size_t bh_file_write_base(bh_file_t *file, + const char *data, + size_t size) +{ + DWORD written; + + if ((file->mode & BH_IO_MASK) == BH_IO_APPEND) + bh_file_seek(file, 0, BH_IO_END); + + if (!WriteFile(file->handle, data, (DWORD)size, &written, NULL)) + { + file->base.flags |= BH_IO_ERROR; + return 0; + } + + if (!written) + file->base.flags |= BH_IO_EOF; + else + file->base.flags &= ~BH_IO_EOF; + + return written; +} + +void bh_file_flush_base(bh_file_t *file) +{ + FlushFileBuffers(file->handle); +} + +int bh_file_seek_base(bh_file_t *file, + bh_off_t pos, + int dir) +{ + LARGE_INTEGER position; + + position.QuadPart = pos; + if (SetFilePointerEx(file->handle, position, NULL, dir)) + return BH_OK; + + return BH_ERROR; +} + +bh_off_t bh_file_size_base(bh_file_t *file) +{ + return -1; +} + +bh_off_t bh_file_tell_base(bh_file_t *file) +{ + LARGE_INTEGER dummy, position; + + dummy.QuadPart = 0; + + if (SetFilePointerEx(file->handle, dummy, &position, BH_IO_CURRENT)) + return position.QuadPart; + + return -1; +} + +bh_off_t bh_file_available_base(bh_file_t *file) +{ + return 0; +} + +void bh_file_clear_base(bh_file_t *file) +{ + file->base.flags &= ~BH_IO_ERROR; +} diff --git a/src/hashmap.c b/src/hashmap.c index c9fb315..256644a 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -2,6 +2,22 @@ #include <stdlib.h> #include <string.h> +/** + * \defgroup hashmap Hashmap + * + * Data stucture for storing pointers in the hashmap. + * \{ + */ + +/** + * Creates the new hashmap object. + * + * \param equal Comparision function + * \param hash Key hash function + * + * \return On success, returns the pointer to the new hashmap object. + * \return On failure, returns a null pointer. + */ bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal, bh_hash_cb_t hash) { @@ -14,12 +30,26 @@ bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal, return result; } +/** + * Frees the \a hashmap object. + * + * \param hashmap Pointer to the hashmap object to be freed + */ void bh_hashmap_free(bh_hashmap_t *hashmap) { bh_hashmap_destroy(hashmap); free(hashmap); } +/** + * Initializes the \a hashmap object. + * + * \warning This is an internal function. + * + * \param hashmap Pointer to the hashmap object to be initialized + * \param equal Comparision function + * \param hash Key hash function + */ void bh_hashmap_init(bh_hashmap_t *hashmap, bh_equal_cb_t equal, bh_hash_cb_t hash) @@ -30,6 +60,13 @@ void bh_hashmap_init(bh_hashmap_t *hashmap, hashmap->hash = hash; } +/** + * Destroys the \a hashmap object. + * + * \warning This is an internal function. + * + * \param hashmap Pointer to the hashmap object to be destroied + */ void bh_hashmap_destroy(bh_hashmap_t *hashmap) { if (hashmap->capacity) @@ -39,6 +76,11 @@ void bh_hashmap_destroy(bh_hashmap_t *hashmap) } } +/** + * Clears the \a hashmap object. + * + * \param hashmap Pointer to the hashmap object to be cleared + */ void bh_hashmap_clear(bh_hashmap_t *hashmap) { if (hashmap->capacity) @@ -46,8 +88,24 @@ void bh_hashmap_clear(bh_hashmap_t *hashmap) hashmap->size = 0; } +/** + * Reserves the space for \a size elements in the \a hashmap. + * + * This function can both expand and shrink the available space in \a hashmap. + * This function takes into account current hashmap load factor. + * + * \param hashmap Pointer to the hashmap object to be resized in terms of + * capacity + * \param size New capacity of the hashmap + * + * \note Calling this function will invalidate iterators. + * \note Actual hashmap capacity can be bigger then requested. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ int bh_hashmap_reserve(bh_hashmap_t *hashmap, - size_t size) + size_t size) { bh_hashmap_t other; size_t capacity, max_capacity, threshold; @@ -78,7 +136,7 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap, /* Capacity can't be bigger than max capacity and overflow */ if (capacity > max_capacity || capacity < 16) - return -1; + return BH_OOM; } } else @@ -93,7 +151,7 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap, /* Prevent same size reallocation */ if (capacity == hashmap->capacity) - return 0; + return BH_OK; /* Initialize new hashmap */ bh_hashmap_init(&other, hashmap->equal, hashmap->hash); @@ -105,7 +163,6 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap, /* Allocate new memory for the hashmap */ other.data = malloc(sizeof(*other.data) * capacity); - other.psls = malloc(sizeof(size_t) * capacity); other.threshold = threshold; other.capacity = capacity; @@ -117,7 +174,7 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap, free(other.data); if (other.psls) free(other.psls); - return -1; + return BH_OOM; } /* Reset probe sequence lengths */ @@ -140,9 +197,22 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap, /* Swap hashmaps */ bh_hashmap_destroy(hashmap); *hashmap = other; - return 0; + return BH_OK; } +/** + * Inserts the pair of \a key and \a value into the \a hashmap. This function + * allows duplicates to be inserted. + * + * \param hashmap Pointer to the hashmap object + * \param key Key to be inserted + * \param value Value to be inserted + * + * \note Calling this function will invalidate iterators. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ int bh_hashmap_insert(bh_hashmap_t *hashmap, void *key, void *value) @@ -154,7 +224,7 @@ int bh_hashmap_insert(bh_hashmap_t *hashmap, if (hashmap->size + 1 > hashmap->threshold) if (bh_hashmap_reserve(hashmap, hashmap->size + 1)) if (hashmap->size >= hashmap->capacity) - return -1; + return BH_OOM; /* Prepare inserted data and set PSL to 1 */ item.key = key; @@ -187,9 +257,19 @@ int bh_hashmap_insert(bh_hashmap_t *hashmap, hashmap->psls[bucket] = psl; hashmap->size++; - return 0; + return BH_OK; } +/** + * Removes value from the \a hashmap by \a key. + * + * \param hashmap Pointer to the hashmap object + * \param key Key value + * + * \note Calling this function will invalidate iterators. + * \note If hashmap contains several key-value pairs with the same key, only + * one pair will be removed. + */ void bh_hashmap_remove(bh_hashmap_t *hashmap, void *key) { @@ -200,6 +280,19 @@ void bh_hashmap_remove(bh_hashmap_t *hashmap, bh_hashmap_iter_remove(hashmap, iter); } +/** + * Returns the value from the \a hashmap by the specified \a key. + * + * If the \a exists is not null pointer, the value will be stored to indicated, + * whether the \a key exists in the hashmap or not. + * + * \param hashmap Pointer to the hashmap object + * \param key Key value + * \param exists Pointer to the exists flag variable (optional) + * + * \return On success, returns value. + * \return On failure, return null pointer. + */ void *bh_hashmap_at(bh_hashmap_t *hashmap, void *key, int *exists) @@ -221,36 +314,87 @@ void *bh_hashmap_at(bh_hashmap_t *hashmap, return NULL; } +/** + * Checks if the \a hashmap is empty. + * + * \param hashmap Pointer to the hashmap object + * + * \return If hashmap is empty, returns non-zero value + * \return If hashmap is not empty, returns zero value + */ int bh_hashmap_empty(bh_hashmap_t *hashmap) { return !hashmap->size; } +/** + * Returns the size of the \a hashmap. + * + * \param hashmap Pointer to the hashmap object + * + * \return Returns the size of the hashmap. + */ size_t bh_hashmap_size(bh_hashmap_t *hashmap) { return hashmap->size; } +/** + * Returns the capacity of the \a hashmap. + * + * \param hashmap Pointer to the hashmap object + * + * \return Returns the capacity of the hashmap. + */ size_t bh_hashmap_capacity(bh_hashmap_t *hashmap) { return hashmap->capacity; } +/** + * Returns the load factor of the \a hashmap. + * + * \param hashmap Pointer to the hashmap object + * + * \return Returns the load factor of the hashmap. + */ float bh_hashmap_factor(bh_hashmap_t *hashmap) { return hashmap->factor; } +/** + * Sets the load \a factor of the \a hashmap. + * + * \param hashmap Pointer to the hashmap object + * \param factor Load factor value + * + * \note New load factor will be applied on the next reserve/insert operation. + */ void bh_hashmap_set_factor(bh_hashmap_t *hashmap, float factor) { + /* Limit the factor value to [0.15, 1.0] */ factor = (factor > 1.0f) ? (1.0f) : (factor); factor = (factor < 0.15f) ? (0.15f) : (factor); + /* Calculate new threshold value */ hashmap->factor = factor; hashmap->threshold = hashmap->capacity * factor; } +/** + * Returns the iterator to the element in the \a hashmap with specified \a key. + * + * \param hashmap Pointer to the hashmap object + * \param iter Opaque iterator value + * + * \return On success, returns iterator value. + * \return On failure, returns null pointer. + * + * \note If hashmap contains several key-value pairs with the same key, only + * iterator to the one of the pairs will returned. + */ void *bh_hashmap_iter_at(bh_hashmap_t *hashmap, void *key) { @@ -278,6 +422,19 @@ void *bh_hashmap_iter_at(bh_hashmap_t *hashmap, return NULL; } +/** + * Returns the iterator to the next element in the \a hashmap. + * + * \param hashmap Pointer to the hashmap object + * \param iter Opaque iterator value + * + * \return If the \a iter doesn't point to the last element of the hashmap, + * returns next iterator value. + * \return If the \a iter point to the last element of the hashmap, returns + * null pointer. + * \return If the \a iter is the null pointer, returns iterator to the + * first element of the hashmap. + */ void *bh_hashmap_iter_next(bh_hashmap_t *hashmap, void *iter) { @@ -302,6 +459,14 @@ void *bh_hashmap_iter_next(bh_hashmap_t *hashmap, } } +/** + * Removes value from the \a hashmap by iterator \a iter. + * + * \param hashmap Pointer to the hashmap object + * \param iter Iterator value + * + * \note Calling this function will invalidate iterators. + */ void bh_hashmap_iter_remove(bh_hashmap_t *hashmap, void *iter) { @@ -332,12 +497,30 @@ void bh_hashmap_iter_remove(bh_hashmap_t *hashmap, hashmap->psls[bucket] = 0; } +/** + * Returns the key, pointed by the hashmap iterator \a iter. + * + * \param iter Opaque iterator value + * + * \return Returns key, pointed by iterator. + */ void *bh_hashmap_iter_key(void *iter) { return ((bh_hashmap_node_t *)iter)->key; } +/** + * Returns the value, pointed by the hashmap iterator \a iter. + * + * \param iter Opaque iterator value + * + * \return Returns value, pointed by iterator. + */ void *bh_hashmap_iter_value(void *iter) { return ((bh_hashmap_node_t *)iter)->value; } + +/** + * \} + */ @@ -1,6 +1,25 @@ #include <bh/internal/io.h> #include <stdlib.h> +/** + * \defgroup io Input/Output + * + * Input/output device abstraction layer. + * \{ + */ + +/** + * Creates the new io object with specified \a table and \a size. + * + * \param table Pointer to the io table + * \param size Size of the io object + * + * \return On success, returns new semi-initialized io object. + * \return On failure, returns null pointer. + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ bh_io_t *bh_io_new(bh_io_table_t *table, size_t size) { @@ -13,24 +32,91 @@ bh_io_t *bh_io_new(bh_io_table_t *table, return result; } +/** + * Frees the \a io object. + * + * \param io Pointer to the io object to be freed + */ void bh_io_free(bh_io_t *io) { bh_io_destroy(io); free(io); } +/** + * Initializes the \a io object with specified \a table. + * + * \param io Pointer to the io object to be initialized + * \param table Pointer to the io table + */ void bh_io_init(bh_io_t *io, - bh_io_table_t *table) + const bh_io_table_t *table) { io->table = table; io->flags = 0; } +/** + * Destroys the \a io object. + * + * \param io Pointer to the io object to be destroyed + */ void bh_io_destroy(bh_io_t *io) { io->table->destroy(io); } +/** + * Opens the \a io object for specified \a mode of operation. + * + * \param io Pointer to the io object + * \param mode Bitmask determining access mode + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int bh_io_open(bh_io_t *io, + int mode) +{ + return io->table->open(io, mode); +} + +/** + * Closes the \a io object. + * + * \param io Pointer to the io object + */ +void bh_io_close(bh_io_t *io) +{ + io->table->close(io); +} + +/** + * Checks if the \a io is open. + * + * \param io Pointer to the io object + * + * \return If io object is open - returns non-zero. + * \return If io object is closed - returns zero. + */ +int bh_io_is_open(bh_io_t *io) +{ + return io->table->is_open(io); +} + +/** + * Reads up to \a size amount of bytes from the \a io object into memory buffer + * pointed by \a data pointer. + * + * \param io Pointer to the io object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * + * \return On success, returns number of bytes successfuly read. + * \return On failure, returns zero. + * + * \note To check for end-of-file or error see bh_io_eof and bh_io_error. + */ size_t bh_io_read(bh_io_t *io, char *data, size_t size) @@ -38,6 +124,19 @@ size_t bh_io_read(bh_io_t *io, return io->table->read(io, data, size); } +/** + * Writes up to \a size amount of bytes to the \a io object from memory buffer + * pointed by \a data pointer. + * + * \param io Pointer to the io object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * + * \return On success, returns number of bytes successfuly written. + * \return On failure, returns zero. + * + * \note To check for error see bh_io_error. + */ size_t bh_io_write(bh_io_t *io, const char* data, size_t size) @@ -45,39 +144,112 @@ size_t bh_io_write(bh_io_t *io, return io->table->write(io, data, size); } +/** + * Synchronizes the \a io object (if possible). + * + * In most cases, this function causes any unwritten/buffered data to be + * written. + * + * \param io Pointer to the io object + */ void bh_io_flush(bh_io_t *io) { io->table->flush(io); } -void bh_io_seek(bh_io_t *io, - bh_off_t offset, - int dir) +/** + * Seeks the \a io object in the specified direction \a dir and \a offset (if + * possible). + * + * \param io Pointer to the io object + * \param offset Number of bytes to seek in specified direciton + * \param dir Seeking direction + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int bh_io_seek(bh_io_t *io, + bh_off_t offset, + int dir) { - io->table->seek(io, offset, dir); + return io->table->seek(io, offset, dir); } +/** + * Returns total size of the \a io object (if possible) + * + * \param io Pointer to the io object + * + * \return On success, returns total size of the io object. + * \return On failure, returns -1. + */ +bh_off_t bh_io_size(bh_io_t *io) +{ + return io->table->size(io); +} + +/** + * Returns current position in the \a io object (if possible). + * + * \param io Pointer to the io object + * + * \return On success, returns current position in the io object. + * \return On failure, returns -1. + */ bh_off_t bh_io_tell(bh_io_t *io) { return io->table->tell(io); } +/** + * Returns available number of bytes to be read from the \a io object. + * + * \param io Pointer to the io object + * + * \return On success, returns number of available bytes to be read. + * \return On failure, returns zero. + */ bh_off_t bh_io_available(bh_io_t *io) { return io->table->available(io); } +/** + * Checks error flag of the \a io object. + * + * \param io Pointer to the io object + * + * \return If error flag is set, returns non-zero. + * \return If error flag is not set, returns zero. + */ int bh_io_error(bh_io_t *io) { - return (io->flags & BH_IO_ERROR) > 0; + return (io->flags & BH_IO_ERROR) != 0; } +/** + * Checks end-of-file flag of the \a io object. + * + * \param io Pointer to the io object + * + * \return If end-of-file flag is set, returns non-zero. + * \return If end-of-file flag is not set, returns zero. + */ int bh_io_eof(bh_io_t *io) { - return (io->flags & BH_IO_EOF) > 0; + return (io->flags & BH_IO_EOF) != 0; } +/** + * Clears error of the \a io object. + * + * \param io Pointer to the io object + */ void bh_io_clear(bh_io_t *io) { io->table->clear(io); } + +/** + * \} + */ diff --git a/src/queue.c b/src/queue.c index 8d48401..e991769 100644 --- a/src/queue.c +++ b/src/queue.c @@ -2,6 +2,19 @@ #include <stdlib.h> #include <string.h> +/** + * \defgroup queue Queue + * + * Data stucture for storing pointers in queue. + * \{ + */ + +/** + * Creates the new queue object. + * + * \return On success, returns the pointer to the new queue object. + * \return On failure, returns a null pointer. + */ bh_queue_t *bh_queue_new(void) { bh_queue_t *result; @@ -13,23 +26,47 @@ bh_queue_t *bh_queue_new(void) return result; } +/** + * Frees the \a queue object. + * + * \param queue Pointer to the queue object to be freed + */ void bh_queue_free(bh_queue_t *queue) { bh_queue_destroy(queue); free(queue); } +/** + * Initializes the \a queue object. + * + * \warning This is an internal function. + * + * \param queue Pointer to the queue object to be initialized + */ void bh_queue_init(bh_queue_t *queue) { memset(queue, 0, sizeof(*queue)); } +/** + * Destroys the \a queue object. + * + * \warning This is an internal function. + * + * \param queue Pointer to the queue object to be destroyed + */ void bh_queue_destroy(bh_queue_t *queue) { if (queue->capacity) free(queue->data); } +/** + * Clears the \a queue object. + * + * \param queue Pointer to the queue object to be cleared + */ void bh_queue_clear(bh_queue_t *queue) { queue->head = 0; @@ -37,6 +74,20 @@ void bh_queue_clear(bh_queue_t *queue) queue->size = 0; } +/** + * Reserves the space for \a size elements in the \a queue. + * + * This function can both expand and shrink the available space in \a queue. + * + * \param queue Pointer to the queue object to be resized in terms of capacity + * \param size New capacity of the queue + * + * \note Calling this function will invalidate iterators. + * \note Actual hashmap capacity can be bigger then requested. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ int bh_queue_reserve(bh_queue_t *queue, size_t size) { @@ -48,11 +99,11 @@ int bh_queue_reserve(bh_queue_t *queue, /* New capacity should not exceed maximum capacity */ if (size > ((size_t)-1) / sizeof(void *)) - return -1; + return BH_OOM; /* Prevent same size memory reallocation */ if (size == queue->capacity) - return 0; + return BH_OK; /* Prepare new empty queue */ bh_queue_init(&other); @@ -64,7 +115,7 @@ int bh_queue_reserve(bh_queue_t *queue, other.data = malloc(size * sizeof(void *)); other.capacity = size; if (!other.data) - return -1; + return BH_OOM; /* Iterate over old queue and insert data into new queue */ iter = bh_queue_iter_next(queue, NULL); @@ -81,9 +132,20 @@ int bh_queue_reserve(bh_queue_t *queue, /* Copy queue information */ *queue = other; - return 0; + return BH_OK; } +/** + * Inserts the \a value into the \a queue. + * + * \param queue Pointer to the queue object + * \param value Value to be inserted + * + * \note Calling this function will invalidate iterators. + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ int bh_queue_insert(bh_queue_t *queue, void *value) { @@ -95,7 +157,7 @@ int bh_queue_insert(bh_queue_t *queue, /* Check potential size overflow and reserve capacity */ capacity = (queue->capacity) ? (queue->capacity * 2) : (16); if (capacity < queue->capacity || bh_queue_reserve(queue, capacity)) - return -1; + return BH_OOM; } /* Increase queue size and advance tail index */ @@ -104,9 +166,16 @@ int bh_queue_insert(bh_queue_t *queue, if (++queue->tail >= queue->capacity) queue->tail = 0; - return 0; + return BH_OK; } +/** + * Removes front value from the \a queue. + * + * \param queue Pointer to the queue object + * + * \note Calling this function will invalidate iterators. + */ void bh_queue_remove(bh_queue_t *queue) { /* Do nothing if queue is empty */ @@ -119,6 +188,14 @@ void bh_queue_remove(bh_queue_t *queue) queue->head = 0; } +/** + * Returns front value from the \a queue. + * + * \param queue Pointer to the queue object + * + * \return On success, returns front value from the queue. + * \return On failure, returns null pointer. + */ void *bh_queue_front(bh_queue_t *queue) { /* Do nothing if queue is empty */ @@ -129,21 +206,56 @@ void *bh_queue_front(bh_queue_t *queue) return queue->data[queue->head]; } +/** + * Checks if the \a queue is empty. + * + * \param queue Pointer to the queue object + * + * \return If queue is empty, returns non-zero value + * \return If queue is not empty, returns zero value + */ int bh_queue_empty(bh_queue_t *queue) { return !queue->size; } +/** + * Returns the size of the \a queue. + * + * \param queue Pointer to the queue object + * + * \return Returns the size of the queue. + */ size_t bh_queue_size(bh_queue_t *queue) { return queue->size; } +/** + * Returns the capacity of the \a queue. + * + * \param queue Pointer to the queue object + * + * \return Returns the capacity of the queue. + */ size_t bh_queue_capacity(bh_queue_t *queue) { return queue->capacity; } +/** + * Returns the iterator to the next element in the \a queue. + * + * \param queue Pointer to the queue object + * \param iter Opaque iterator value + * + * \return If the \a iter doesn't point to the last element of the queue, + * returns next iterator value. + * \return If the \a iter point to the last element of the queue, returns + * null pointer. + * \return If the \a iter is the null pointer, returns iterator to the + * first element of the queue. + */ void *bh_queue_iter_next(bh_queue_t *queue, void *iter) { @@ -170,7 +282,18 @@ void *bh_queue_iter_next(bh_queue_t *queue, return element; } +/** + * Returns the value, pointed by the queue iterator \a iter. + * + * \param iter Opaque iterator value + * + * \return Returns value, pointed by iterator. + */ void *bh_queue_iter_value(void *iter) { return *(void **)iter; } + +/** + * \} + */ diff --git a/src/thread.c b/src/thread.c index 84bc2c0..2744387 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,6 +1,27 @@ #include <bh/internal/thread.h> #include <stdlib.h> +/** + * \defgroup thread Multithreading + * + * Multithreading API + * \{ + */ + +/** + * Creates new task object with specified function \a func, \a data and \a + * flags. + * + * If the \a flags contain BH_THREAD_CLEANUP, upon successful completion task + * will be automaticly destroyed. + * + * \param func Task's function + * \param data Task's data + * \param flags Task's flags + * + * \return On success, returns new task object. + * \return On failure, returns null pointer. + */ bh_task_t *bh_task_new(void (*func)(void *), void *data, int flags) @@ -14,12 +35,31 @@ bh_task_t *bh_task_new(void (*func)(void *), return result; } +/** + * Frees the \a task object. + * + * \param task Pointer to the task object. + */ void bh_task_free(bh_task_t *task) { bh_task_destroy(task); free(task); } +/** + * Initializes the \a task object with specified function \a func, \a data and + * \a flags. + * + * If the \a flags contain BH_THREAD_CLEANUP, upon successful completion task + * will be automaticly destroyed. + * + * \warning This is an internal function. + * + * \param task Pointer to the task object + * \param func Task's function + * \param data Task's data + * \param flags Task's flags + */ void bh_task_init(bh_task_t *task, void (*func)(void *), void *data, @@ -30,24 +70,53 @@ void bh_task_init(bh_task_t *task, task->flags = flags; } +/** + * Destroyes the \a task object. + * + * \warning This is an internal function. + * + * \param task Pointer to the task object + */ void bh_task_destroy(bh_task_t *task) { (void)task; } +/** + * Reuses the \a task object for different purpose. + * + * \param task Pointer to the task object + * \param func New task's function + * \param data New task's data + */ void bh_task_reuse(bh_task_t *task, void (*func)(void *), void *data) { + /* If the task is done - reinitilize it with new data */ if (task->flags & BH_THREAD_DONE) bh_task_init(task, func, data, task->flags & ~(BH_THREAD_DONE)); } +/** + * Checks if the \a task is done. + * + * \param task Pointer to the task object + * + * \return If the task is done, returns non-zero. + * \return If the task is not done, returns zero. + */ int bh_task_done(bh_task_t *task) { return (task->flags & BH_THREAD_DONE) != 0; } +/** + * Creates the mutex object. + * + * \return On success, returns new mutex object. + * \return On failure, returns null pointer. + */ bh_mutex_t *bh_mutex_new(void) { bh_mutex_t *result; @@ -62,32 +131,58 @@ bh_mutex_t *bh_mutex_new(void) return result; } +/** + * Frees the \a mutex object. + * + * \param mutex Pointer to the mutex object + */ void bh_mutex_free(bh_mutex_t *mutex) { bh_mutex_destroy(mutex); free(mutex); } +/** + * Creates the semaphore object with initial \a count value. + * + * \param count Initial semaphore value + * + * \return On success, returns new semaphore object. + * \return On failure, returns null pointer. + * + * \note Guranteed maximum \a count value is 32767. + */ bh_semaphore_t *bh_semaphore_new(int count) { bh_semaphore_t *result; - + result = malloc(sizeof(*result)); if (result && bh_semaphore_init(result, count)) { free(result); result = NULL; } - + return result; } +/** + * Frees the \a semaphore object. + * + * \param semaphore Pointer to the semaphore object + */ void bh_semaphore_free(bh_semaphore_t *semaphore) { bh_semaphore_destroy(semaphore); free(semaphore); } +/** + * Creates the condition variable object. + * + * \return On success, returns new condition variable object. + * \return On failure, returns null pointer. + */ bh_cond_t *bh_cond_new(void) { bh_cond_t *result; @@ -102,12 +197,26 @@ bh_cond_t *bh_cond_new(void) return result; } +/** + * Frees the condition variable object \a cond. + * + * \param cond Pointer to the condition variable object + */ void bh_cond_free(bh_cond_t *cond) { bh_cond_destroy(cond); free(cond); } +/** + * Adds \a task to the thread \a pool. + * + * \param pool Pointer to the thread pool object + * \param task Pointer to the task object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_thread_pool_add(bh_thread_pool_t *pool, bh_task_t *task) { @@ -119,16 +228,24 @@ int bh_thread_pool_add(bh_thread_pool_t *pool, if (bh_queue_insert(&pool->tasks, task)) { bh_mutex_unlock(&pool->lock); - return -1; + return BH_ERROR; } /* Signal new job */ bh_mutex_unlock(&pool->lock); bh_cond_signal(&pool->new_task); - return 0; + return BH_OK; } +/** + * Waits unitl all task in thread \a pool are complete. + * + * \param pool Pointer to the thread pool. + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_thread_pool_wait(bh_thread_pool_t *pool) { /* Lock and check if there is jobs in the queue */ @@ -160,9 +277,16 @@ int bh_thread_pool_wait(bh_thread_pool_t *pool) /* Unlock */ bh_mutex_unlock(&pool->lock); - return 0; + return BH_OK; } +/** + * Destroyes the thread \a pool object. + * + * \warning This is an internal function. + * + * \param pool Pointer to the thread pool object + */ void bh_thread_pool_destroy(bh_thread_pool_t *pool) { size_t i; @@ -195,12 +319,21 @@ void bh_thread_pool_destroy(bh_thread_pool_t *pool) bh_mutex_destroy(&pool->lock); } +/** + * Frees the thread \a pool object. + * + * \param pool Pointer to the thread pool object + */ void bh_thread_pool_free(bh_thread_pool_t *pool) { bh_thread_pool_destroy(pool); free(pool); } +/** + * \warning This is an internal function. + * \warning Do not use this function directly! + */ void bh_thread_pool_worker(void *arg) { bh_thread_pool_t *pool; @@ -210,26 +343,31 @@ void bh_thread_pool_worker(void *arg) { bh_task_t *task; + /* Wait unitl there is a task or shutdown signal */ bh_mutex_lock(&pool->lock); while (!pool->shutdown && bh_queue_empty(&pool->tasks)) bh_cond_wait(&pool->new_task, &pool->lock); + /* If its shutdown signal - exit the loop */ if (pool->shutdown) { bh_mutex_unlock(&pool->lock); break; } + /* Fetch task from the front of the queue, increase active counter */ task = bh_queue_front(&pool->tasks); bh_queue_remove(&pool->tasks); pool->active++; bh_mutex_unlock(&pool->lock); + /* Do the task, mark as done, and if required - free it */ task->func(task->data); task->flags |= BH_THREAD_DONE; if (task->flags & BH_THREAD_CLEANUP) bh_task_free(task); + /* Decrease active counter and broadcast that we are done */ bh_mutex_lock(&pool->lock); pool->active--; @@ -237,3 +375,7 @@ void bh_thread_pool_worker(void *arg) bh_cond_broadcast(&pool->done_task); } } + +/** + * \} + */
\ No newline at end of file diff --git a/src/thread_null.c b/src/thread_null.c index af73fd6..25d8a88 100644 --- a/src/thread_null.c +++ b/src/thread_null.c @@ -1,116 +1,300 @@ #include <bh/internal/thread.h> -int bh_thread_init(bh_thread_t *thread, - bh_task_t *task) +/** + * \ingroup thread + * \{ + */ + +/** + * Creates the thread object with the given \a task. + * + * \warning This function can be implemented either as a macro or as a function. + * + * \param task Pointer to the task object + * + * \return On success, returns the pointer to the new queue object. + * \return On failure, returns a null pointer. + */ +bh_thread_t *bh_thread_new(bh_task_t *task) { - (void)thread; (void)task; - return -1; + return NULL; } -bh_thread_t *bh_thread_new(bh_task_t *task) +/** + * Initializes the \a thread object with the given \a task. + * + * \warning This is an internal function. + * \warning This function can be implemented either as a macro or as a function. + * + * \param thread Pointer to the thread object + * \param task Pointer to the task object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int bh_thread_init(bh_thread_t *thread, + bh_task_t *task) { + (void)thread; (void)task; - return NULL; + return BH_NO_IMPL; } +/** + * Joins the \a thread. + * + * If thread was created with bh_thread_new, this function also frees the \a + * thread object. + * + * \param thread Pointer to the thread object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_thread_join(bh_thread_t *thread) { (void)thread; - return -1; + return BH_NO_IMPL; } +/** + * Detaches the \a thread. + * + * If thread was created with bh_thread_new, this function also frees the \a + * thread object. + * + * \param thread Pointer to the thread object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_thread_detach(bh_thread_t *thread) { (void)thread; - return -1; + return BH_NO_IMPL; } +/** + * Initializes the \a mutex object. + * + * \warning This is an internal function. + * + * \param mutex Pointer to the mutex object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_mutex_init(bh_mutex_t *mutex) { (void)mutex; - return -1; + return BH_NO_IMPL; } +/** + * Destroyes the \a mutex object. + * + * \param mutex Pointer to the mutex object + * + * \warning This is an internal function. + */ void bh_mutex_destroy(bh_mutex_t *mutex) { (void)mutex; } +/** + * Locks the \a mutex object. + * + * \param mutex Pointer to the mutex object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_mutex_lock(bh_mutex_t *mutex) { (void)mutex; - return -1; + return BH_NO_IMPL; } +/** + * Tries to lock the \a mutex object. + * + * \param mutex Pointer to the mutex object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_mutex_try_lock(bh_mutex_t *mutex) { (void)mutex; - return -1; + return BH_NO_IMPL; } +/** + * Unlocks the \a mutex object. + * + * \param mutex Pointer to the mutex object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_mutex_unlock(bh_mutex_t *mutex) { (void)mutex; - return -1; + return BH_NO_IMPL; } +/** + * Initilizes the \a semaphore object with specified \a count value. + * + * \warning This is an internal function. + * + * \param semaphore Pointer to the semaphore object + * \param count Initial semaphore value + * + * \return On success, returns zero. + * \return On failure, returns error code. + * + * \note Guranteed maximum \a count value is 32767. + */ int bh_semaphore_init(bh_semaphore_t *semaphore, int count) { (void)semaphore; (void)count; - return -1; + return BH_NO_IMPL; } +/** + * Destroyes the \a semaphore object. + * + * \warning This is an internal function. + * + * \param semaphore Pointer to the semaphore object + */ void bh_semaphore_destroy(bh_semaphore_t *semaphore) { (void)semaphore; } +/** + * Posts (increases value of) the \a semaphore. + * + * \param semaphore Pointer to the semaphore object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_semaphore_post(bh_semaphore_t *semaphore) { (void)semaphore; - return -1; + return BH_NO_IMPL; } +/** + * Waits (decreases value of) the \a semaphore. + * + * \param semaphore Pointer to the semaphore object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_semaphore_wait(bh_semaphore_t *semaphore) { (void)semaphore; - return -1; + return BH_NO_IMPL; } +/** + * Waits (decreases value of) the \a semaphore for the specified \a timeout + * amount of milliseconds. + * + * \param semaphore Pointer to the semaphore object + * \param timeout Number of milliseconds to wait for + * + * \return On success, returns zero. + * \return On failure or timeout, returns error code. + */ int bh_semaphore_wait_for(bh_semaphore_t *semaphore, unsigned long timeout) { (void)semaphore; - return -1; + return BH_NO_IMPL; } +/** + * Tries to waits (decrease value of) the \a semaphore. + * + * \param semaphore Pointer to the semaphore object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_semaphore_try_wait(bh_semaphore_t *semaphore) { (void)semaphore; - return -1; + return BH_NO_IMPL; } +/** + * Initializes the condition variable object \a cond. + * + * \warning This is an internal function. + * + * \param cond Pointer to the condition variable + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_cond_init(bh_cond_t *cond) { (void)cond; - return -1; + return BH_NO_IMPL; } +/** + * Destroyes the condition variable object \a cond. + * + * \param cond Pointer to the condition variable object + * + * \return On success, returns zero. + * \return On failure, returns error code. + * + * \warning This is an internal function. + */ void bh_cond_destroy(bh_cond_t *cond) { (void)cond; } +/** + * Waits on the condition variable \a cond with specified \a mutex. + * + * \param cond Pointer to the condition variable object + * \param mutex Pointer to the mutex object + * + * \return On success, returns zero. + * \return ON failure, returns error code. + */ int bh_cond_wait(bh_cond_t *cond, bh_mutex_t *mutex) { (void)cond; (void)mutex; - return -1; + return BH_NO_IMPL; } +/** + * Waits on the condition variable \a cond with specified \a mutex for the + * specified \a timeout amount of milliseconds. + * + * \param cond Pointer to the condition variable object + * \param mutex Pointer to the mutex object + * \param timeout Number of milliseconds to wait for + * + * \return On success, returns zero. + * \return On failure or timeout, returns error code. + */ int bh_cond_wait_for(bh_cond_t *cond, bh_mutex_t *mutex, unsigned long timeout) @@ -118,33 +302,71 @@ int bh_cond_wait_for(bh_cond_t *cond, (void)cond; (void)mutex; (void)timeout; - return -1; + return BH_NO_IMPL; } +/** + * Signals (notifies) one waiting thread on the condition variable \a cond. + * + * \param cond Pointer to the condition variable object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_cond_signal(bh_cond_t *cond) { (void)cond; - return -1; + return BH_NO_IMPL; } +/** + * Signals (notifies) all waiting threads on the condition variable \a cond. + * + * \param cond Pointer to the condition variable object + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ int bh_cond_broadcast(bh_cond_t *cond) { (void)cond; - return -1; + return BH_NO_IMPL; } -int bh_thread_pool_init(bh_thread_pool_t *pool, - size_t size) +/** + * Creates the thread pool object with specified \a size amount of threads. + * + * \param size Number of threads in the thread pool + * + * \return On success, returns pointer to the new thread pool object. + * \return On failure, returns null pointer. + */ +bh_thread_pool_t *bh_thread_pool_new(size_t size) { - (void)pool; (void)size; - return -1; + return NULL; } -bh_thread_pool_t *bh_thread_pool_new(size_t size) +/** + * Initilizes the thread \a pool object with specified \a size amount of + * threads. + * + * \warning This is an internal function. + * + * \param pool Pointer to the thread pool object + * \param size Number of threads in the thread pool + * + * \return On success, returns zero. + * \return On failure, returns error code. + */ +int bh_thread_pool_init(bh_thread_pool_t *pool, + size_t size) { + (void)pool; (void)size; - return NULL; + return BH_NO_IMPL; } - +/** + * \} + */
\ No newline at end of file diff --git a/src/thread_posix.c b/src/thread_posix.c index 21ca2be..2898644 100644 --- a/src/thread_posix.c +++ b/src/thread_posix.c @@ -5,9 +5,9 @@ static void *bh_thread_run(void *arg) { bh_task_t *task; - task = (bh_task_t *)arg; + /* Do the task, mark as done, and if required free it */ task->func(task->data); task->flags |= BH_THREAD_DONE; @@ -21,21 +21,25 @@ int bh_thread_init(bh_thread_t *thread, bh_task_t *task) { thread->allocated = 0; - return pthread_create(&thread->handle, NULL, bh_thread_run, task); + if (pthread_create(&thread->handle, NULL, bh_thread_run, task)) + return BH_ERROR; + + return BH_OK; } bh_thread_t *bh_thread_new(bh_task_t *task) { bh_thread_t *result; + /* Allocate thread object */ result = malloc(sizeof(*result)); - if (result && bh_thread_init(result, task)) { free(result); result = NULL; } + /* Mark thread as allocated for deallocation in join/detach */ if (result) result->allocated = 1; @@ -44,27 +48,35 @@ bh_thread_t *bh_thread_new(bh_task_t *task) int bh_thread_join(bh_thread_t *thread) { - int result = pthread_join(thread->handle, NULL); + /* Join the thread */ + if (pthread_join(thread->handle, NULL)) + return BH_ERROR; + /* If thread is allocated, deallocate it */ if (thread->allocated) free(thread); - return result; + return BH_OK; } int bh_thread_detach(bh_thread_t *thread) { - int result = pthread_detach(thread->handle); + /* Detach the thread */ + if (pthread_detach(thread->handle)) + return BH_ERROR; + /* If thread is allocated, deallocate it */ if (thread->allocated) free(thread); - return result; + return BH_OK; } int bh_mutex_init(bh_mutex_t *mutex) { - return pthread_mutex_init(&mutex->handle, NULL); + if (pthread_mutex_init(&mutex->handle, NULL)) + return BH_ERROR; + return BH_OK; } void bh_mutex_destroy(bh_mutex_t *mutex) @@ -74,22 +86,30 @@ void bh_mutex_destroy(bh_mutex_t *mutex) int bh_mutex_lock(bh_mutex_t *mutex) { - return pthread_mutex_lock(&mutex->handle); + if (pthread_mutex_lock(&mutex->handle)) + return BH_ERROR; + return BH_OK; } int bh_mutex_try_lock(bh_mutex_t *mutex) { - return pthread_mutex_trylock(&mutex->handle); + if (pthread_mutex_trylock(&mutex->handle)) + return BH_ERROR; + return BH_OK; } int bh_mutex_unlock(bh_mutex_t *mutex) { - return pthread_mutex_unlock(&mutex->handle); + if (pthread_mutex_unlock(&mutex->handle)) + return BH_ERROR; + return BH_OK; } int bh_semaphore_init(bh_semaphore_t *semaphore, int count) { - return sem_init(&semaphore->handle, 0, count); + if (sem_init(&semaphore->handle, 0, count)) + return BH_ERROR; + return BH_OK; } void bh_semaphore_destroy(bh_semaphore_t *semaphore) @@ -99,12 +119,16 @@ void bh_semaphore_destroy(bh_semaphore_t *semaphore) int bh_semaphore_post(bh_semaphore_t *semaphore) { - return sem_post(&semaphore->handle); + if (sem_post(&semaphore->handle)) + return BH_ERROR; + return BH_OK; } int bh_semaphore_wait(bh_semaphore_t *semaphore) { - return sem_wait(&semaphore->handle); + if (sem_wait(&semaphore->handle)) + return BH_ERROR; + return BH_OK; } int bh_semaphore_wait_for(bh_semaphore_t *semaphore, @@ -115,17 +139,23 @@ int bh_semaphore_wait_for(bh_semaphore_t *semaphore, ts.tv_sec = timeout / 1000; ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000; - return sem_timedwait(&semaphore->handle, &ts); + if (sem_timedwait(&semaphore->handle, &ts)) + return BH_TIMEOUT; + return BH_OK; } int bh_semaphore_try_wait(bh_semaphore_t *semaphore) { - return sem_trywait(&semaphore->handle); + if (sem_trywait(&semaphore->handle)) + return BH_ERROR; + return BH_OK; } int bh_cond_init(bh_cond_t *cond) { - return pthread_cond_init(&cond->handle, NULL); + if (pthread_cond_init(&cond->handle, NULL)) + return BH_ERROR; + return BH_OK; } void bh_cond_destroy(bh_cond_t *cond) @@ -136,7 +166,9 @@ void bh_cond_destroy(bh_cond_t *cond) int bh_cond_wait(bh_cond_t *cond, bh_mutex_t *mutex) { - return pthread_cond_wait(&cond->handle, &mutex->handle); + if (pthread_cond_wait(&cond->handle, &mutex->handle)) + return BH_ERROR; + return BH_OK; } int bh_cond_wait_for(bh_cond_t *cond, @@ -148,17 +180,23 @@ int bh_cond_wait_for(bh_cond_t *cond, ts.tv_sec = timeout / 1000; ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000; - return pthread_cond_timedwait(&cond->handle, &mutex->handle, &ts); + if (pthread_cond_timedwait(&cond->handle, &mutex->handle, &ts)) + return BH_TIMEOUT; + return BH_OK; } int bh_cond_signal(bh_cond_t *cond) { - return pthread_cond_signal(&cond->handle); + if (pthread_cond_signal(&cond->handle)) + return BH_ERROR; + return BH_OK; } int bh_cond_broadcast(bh_cond_t *cond) { - return pthread_cond_broadcast(&cond->handle); + if (pthread_cond_broadcast(&cond->handle)) + return BH_ERROR; + return BH_OK; } int bh_thread_pool_init(bh_thread_pool_t *pool, @@ -215,7 +253,7 @@ int bh_thread_pool_init(bh_thread_pool_t *pool, } } - return 0; + return BH_OK; queue_fail: free(pool->threads); @@ -230,7 +268,7 @@ task_fail: bh_mutex_destroy(&pool->lock); lock_fail: - return -1; + return BH_ERROR; } bh_thread_pool_t *bh_thread_pool_new(size_t size) diff --git a/src/thread_win.c b/src/thread_win.c index 098e7dd..bb14774 100644 --- a/src/thread_win.c +++ b/src/thread_win.c @@ -3,16 +3,19 @@ static unsigned __stdcall bh_thread_run(void *arg) { bh_thread_data_t data; - + + /* Fetch thread data, store it on stack and free from heap */ data = *(bh_thread_data_t *)arg; free(arg); - + + /* Do the task, mark as done, and if required free it */ data.task->func(data.task->data); data.task->flags |= BH_THREAD_DONE; if (data.task->flags & BH_THREAD_CLEANUP) bh_task_free(data.task); - + + /* Call thread specific end function (deallocate TLS) */ data.end(0); return 0; } @@ -23,24 +26,28 @@ int bh_thread_init_base(bh_thread_t *thread, bh_thread_end_cb_t end) { bh_thread_data_t *data; - + + /* Allocate thread specific data */ data = malloc(sizeof(*data)); if (!data) - return -1; - + return BH_OOM; + + /* Setup thread specific data */ data->task = task; data->end = end; - + + /* Create and setup thread (relative to the callers libc) */ thread->allocated = 0; thread->handle = (HANDLE)_beginthreadex(NULL, 0, bh_thread_run, data, 0, NULL); - + + /* Check for errors */ if (!thread->handle) { free(data); - return -1; + return BH_ERROR; } - - return 0; + + return BH_OK; } bh_thread_t *bh_thread_new_base(bh_task_t *task, @@ -48,46 +55,53 @@ bh_thread_t *bh_thread_new_base(bh_task_t *task, bh_thread_end_cb_t end) { bh_thread_t *result; - + + /* Allocate thread object */ result = malloc(sizeof(*result)); if (result && !bh_thread_init_base(result, task, begin, end)) { free(result); result = NULL; } - + + /* Mark thread as allocated for deallocation in join/detach */ if (result) result->allocated = 1; - + return result; } int bh_thread_join(bh_thread_t *thread) { + /* Join the thread */ WaitForSingleObject(thread->handle, INFINITE); CloseHandle(thread->handle); - + + /* If thread is allocated, deallocate it */ if (thread->allocated) free(thread); - - return 0; + + return BH_OK; } int bh_thread_detach(bh_thread_t *thread) { + /* Detach from thread */ CloseHandle(thread->handle); - + + /* If thread is allocated, deallocate it */ if (thread->allocated) free(thread); - - return 0; + + return BH_OK; } int bh_mutex_init(bh_mutex_t *mutex) { + /* TODO: Is this spincount needed or sane? */ if (!InitializeCriticalSectionAndSpinCount(&mutex->handle, 0x400)) - return -1; - return 0; + return BH_ERROR; + return BH_OK; } void bh_mutex_destroy(bh_mutex_t *mutex) @@ -98,29 +112,30 @@ void bh_mutex_destroy(bh_mutex_t *mutex) int bh_mutex_lock(bh_mutex_t *mutex) { EnterCriticalSection(&mutex->handle); - return 0; + return BH_OK; } int bh_mutex_try_lock(bh_mutex_t *mutex) { if (!TryEnterCriticalSection(&mutex->handle)) - return -1; - return 0; + return BH_ERROR; + return BH_OK; } int bh_mutex_unlock(bh_mutex_t *mutex) { LeaveCriticalSection(&mutex->handle); - return 0; + return BH_OK; } int bh_semaphore_init(bh_semaphore_t *semaphore, int count) { + /* Create semaphore with max value of 32767 (to match POSIX) */ semaphore->handle = CreateSemaphore(NULL, count, 0x7FFF, NULL); if (!semaphore->handle) - return -1; - - return 0; + return BH_ERROR; + + return BH_OK; } void bh_semaphore_destroy(bh_semaphore_t *semaphore) @@ -131,37 +146,38 @@ void bh_semaphore_destroy(bh_semaphore_t *semaphore) int bh_semaphore_post(bh_semaphore_t *semaphore) { if (!ReleaseSemaphore(semaphore->handle, 1, NULL)) - return -1; - return 0; + return BH_ERROR; + return BH_OK; } int bh_semaphore_wait(bh_semaphore_t *semaphore) { if (WaitForSingleObject(semaphore->handle, INFINITE)) - return -1; - return 0; + return BH_ERROR; + return BH_OK; } int bh_semaphore_wait_for(bh_semaphore_t *semaphore, unsigned long timeout) { + /* FIXME: Check if we timed out or errored out */ if (WaitForSingleObject(semaphore->handle, timeout)) - return -1; - return 0; + return BH_TIMEOUT; + return BH_ERROR; } int bh_semaphore_try_wait(bh_semaphore_t *semaphore) { if (WaitForSingleObject(semaphore->handle, 0)) - return -1; - return 0; + return BH_ERROR; + return BH_OK; } #if WINVER >= _WIN32_WINNT_VISTA int bh_cond_init(bh_cond_t *cond) { InitializeConditionVariable(&cond->handle); - return 0; + return BH_OK; } void bh_cond_destroy(bh_cond_t *cond) @@ -179,43 +195,46 @@ int bh_cond_wait_for(bh_cond_t *cond, bh_mutex_t *mutex, unsigned long timeout) { + /* FIXME: Check if we timed out or errored out */ if (!SleepConditionVariableCS(&cond->handle, &mutex->handle, timeout)) - return -1; - return 0; + return BH_TIMEOUT; + return BH_OK; } int bh_cond_signal(bh_cond_t *cond) { WakeConditionVariable(&cond->handle); - return 0; + return BH_OK; } int bh_cond_broadcast(bh_cond_t *cond) { WakeAllConditionVariable(&cond->handle); - return 0; + return BH_OK; } #else /* Condition variable implementation based on BeOS article * http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html - */ + * + * Slow, but correct implementation of CVs. + */ int bh_cond_init(bh_cond_t *cond) { if (bh_mutex_init(&cond->lock)) - return -1; - + return BH_ERROR; + if (bh_semaphore_init(&cond->wait, 0)) { bh_mutex_destroy(&cond->lock); - return -1; + return BH_ERROR; } - + if (bh_semaphore_init(&cond->done, 0)) { bh_semaphore_destroy(&cond->wait); bh_mutex_destroy(&cond->lock); - return -1; + return BH_ERROR; } cond->waiting = 0; cond->signals = 0; @@ -234,27 +253,27 @@ int bh_cond_wait(bh_cond_t *cond, bh_mutex_t *mutex) { int retval; - + bh_mutex_lock(&cond->lock); cond->waiting++; bh_mutex_unlock(&cond->lock); bh_mutex_unlock(mutex); - + retval = bh_semaphore_wait(&cond->wait); - + bh_mutex_lock(&cond->lock); if (cond->signals > 0) { if (retval) bh_semaphore_wait(&cond->wait); - + bh_semaphore_post(&cond->done); cond->signals--; } cond->waiting--; - bh_mutex_unlock(&cond->lock); + bh_mutex_unlock(&cond->lock); bh_mutex_lock(mutex); - + return retval; } @@ -263,70 +282,70 @@ int bh_cond_wait_for(bh_cond_t *cond, unsigned long timeout) { int retval; - + bh_mutex_lock(&cond->lock); cond->waiting++; bh_mutex_unlock(&cond->lock); bh_mutex_unlock(mutex); - + retval = bh_semaphore_wait_for(&cond->wait, timeout); - + bh_mutex_lock(&cond->lock); if (cond->signals > 0) { if (retval) bh_semaphore_wait(&cond->wait); - + bh_semaphore_post(&cond->done); cond->signals--; } cond->waiting--; - bh_mutex_unlock(&cond->lock); + bh_mutex_unlock(&cond->lock); bh_mutex_lock(mutex); - + return retval; } int bh_cond_signal(bh_cond_t *cond) { bh_mutex_lock(&cond->lock); - + if (cond->waiting > cond->signals) { cond->signals++; - + bh_semaphore_post(&cond->wait); bh_mutex_unlock(&cond->lock); bh_semaphore_wait(&cond->done); } else bh_mutex_unlock(&cond->lock); - - return 0; + + return BH_OK; } int bh_cond_broadcast(bh_cond_t *cond) { int i, waiting; - + bh_mutex_lock(&cond->lock); if (cond->waiting > cond->signals) { waiting = cond->waiting - cond->signals; cond->signals = cond->waiting; - + for (i = 0; i < waiting; i++) bh_semaphore_post(&cond->wait); - + bh_mutex_unlock(&cond->lock); - + for (i = 0; i < waiting; i++) bh_semaphore_wait(&cond->done); } else bh_mutex_unlock(&cond->lock); - - return 0; + + return BH_OK; } #endif @@ -386,7 +405,7 @@ int bh_thread_pool_init_base(bh_thread_pool_t *pool, } } - return 0; + return BH_OK; queue_fail: free(pool->threads); @@ -401,7 +420,7 @@ task_fail: bh_mutex_destroy(&pool->lock); lock_fail: - return -1; + return BH_ERROR; } bh_thread_pool_t *bh_thread_pool_new_base(size_t size, @@ -409,6 +428,7 @@ bh_thread_pool_t *bh_thread_pool_new_base(size_t size, bh_thread_end_cb_t end) { bh_thread_pool_t *result; + result = malloc(sizeof(*result)); if (result && bh_thread_pool_init_base(result, size, begin, end)) { |
