Add documentation, expand error handling, implement file and buffer io

This commit is contained in:
2024-06-03 22:11:05 +03:00
parent fdbabab0e0
commit 79874622a2
37 changed files with 5531 additions and 986 deletions

View File

@@ -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;
}
}
/**
* \}
*/

278
src/buffer.c Normal file
View File

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

275
src/file_null.c Normal file
View File

@@ -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;
}
/**
* \}
*/

View File

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

View File

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

View File

@@ -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;
}
/**
* \}
*/

186
src/io.c
View File

@@ -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);
}
/**
* \}
*/

View File

@@ -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;
}
/**
* \}
*/

View File

@@ -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);
}
}
/**
* \}
*/

View File

@@ -1,116 +1,300 @@
#include <bh/internal/thread.h>
int bh_thread_init(bh_thread_t *thread,
bh_task_t *task)
{
(void)thread;
(void)task;
return -1;
}
/**
* \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)task;
return NULL;
}
/**
* 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 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;
}
int bh_thread_pool_init(bh_thread_pool_t *pool,
size_t size)
{
(void)pool;
(void)size;
return -1;
return BH_NO_IMPL;
}
/**
* 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)size;
return NULL;
}
/**
* 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 BH_NO_IMPL;
}
/**
* \}
*/

View File

@@ -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)

View File

@@ -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))
{