lab 3.3 - add dynamic buffer
This commit is contained in:
parent
6d8cc4f554
commit
3b23e37949
7
buffer_dyn/.gitignore
vendored
Normal file
7
buffer_dyn/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/*.o
|
||||||
|
/demo
|
||||||
|
/test
|
||||||
|
/Makefile
|
||||||
|
/CMakeFiles
|
||||||
|
/cmake_install.cmake
|
||||||
|
/CMakeCache.txt
|
||||||
6
buffer_dyn/CMakeLists.txt
Normal file
6
buffer_dyn/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
project(buffer)
|
||||||
|
add_executable(demo demo.c buffer.c)
|
||||||
|
add_executable(test test.c buffer.c)
|
||||||
|
target_compile_options(demo PRIVATE -std=c18 -Wall -Wextra -Wpedantic -g3 -O0)
|
||||||
|
target_compile_options(test PRIVATE -std=c18 -Wall -Wextra -Wpedantic -g3 -O0)
|
||||||
150
buffer_dyn/buffer.c
Normal file
150
buffer_dyn/buffer.c
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
// implementation for a FIFO-buffer with ints
|
||||||
|
// this very simple FIFO-buffer can only hold one int
|
||||||
|
#define FIFO_SIZE 8
|
||||||
|
|
||||||
|
typedef struct FIFO_element_s {
|
||||||
|
int value;
|
||||||
|
void* nextElement;
|
||||||
|
} FIFO_element_t;
|
||||||
|
|
||||||
|
// shared variables within this file
|
||||||
|
static FIFO_element_t* FIFO_FirstElement_p = NULL;
|
||||||
|
static FIFO_element_t* FIFO_LastElement_p = NULL;
|
||||||
|
static unsigned int FIFO_count = 0;
|
||||||
|
bool FIFO_Full = false;
|
||||||
|
|
||||||
|
/** incrementPointer
|
||||||
|
*
|
||||||
|
* add one and rotates if it overflows the buffersize
|
||||||
|
*/
|
||||||
|
unsigned int incrementPointer(unsigned int p)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
if (p == FIFO_SIZE)
|
||||||
|
{
|
||||||
|
p = 0;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buffer_put(int i)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
if ((FIFO_LastElement_p == NULL) && (FIFO_FirstElement_p == NULL))
|
||||||
|
{
|
||||||
|
// buffer is empty. add first element
|
||||||
|
FIFO_FirstElement_p = malloc(sizeof(FIFO_element_t));
|
||||||
|
if (FIFO_FirstElement_p != NULL)
|
||||||
|
{
|
||||||
|
FIFO_FirstElement_p->value = i;
|
||||||
|
FIFO_FirstElement_p->nextElement = NULL;
|
||||||
|
|
||||||
|
FIFO_LastElement_p = FIFO_FirstElement_p;
|
||||||
|
FIFO_count++;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FIFO_Full = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((FIFO_LastElement_p != NULL) && (FIFO_FirstElement_p != NULL))
|
||||||
|
{
|
||||||
|
// add element to exsiting buffer
|
||||||
|
FIFO_element_t* el = malloc(sizeof(FIFO_element_t));
|
||||||
|
if (el != NULL)
|
||||||
|
{
|
||||||
|
el->value = i;
|
||||||
|
el->nextElement = NULL;
|
||||||
|
FIFO_LastElement_p = el;
|
||||||
|
FIFO_count++;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FIFO_Full = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// buffer is unhealthy. try to clear it and reinit
|
||||||
|
//TODO: this while loop is unsave
|
||||||
|
while (FIFO_FirstElement_p != NULL)
|
||||||
|
{
|
||||||
|
buffer_get(NULL);
|
||||||
|
}
|
||||||
|
if (FIFO_LastElement_p != NULL)
|
||||||
|
{
|
||||||
|
free(FIFO_LastElement_p);
|
||||||
|
FIFO_LastElement_p = NULL;
|
||||||
|
}
|
||||||
|
buffer_put(i);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buffer_get(int *p)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
if (FIFO_FirstElement_p != NULL)
|
||||||
|
{
|
||||||
|
*p = FIFO_FirstElement_p->value;
|
||||||
|
FIFO_element_t* next = FIFO_FirstElement_p->nextElement;
|
||||||
|
free(FIFO_FirstElement_p);
|
||||||
|
FIFO_count--;
|
||||||
|
FIFO_Full = false;
|
||||||
|
if (next == NULL)
|
||||||
|
{
|
||||||
|
// this was the last element in the buffer
|
||||||
|
if (FIFO_LastElement_p == FIFO_FirstElement_p)
|
||||||
|
{
|
||||||
|
// clear last element becouse it's the same
|
||||||
|
FIFO_LastElement_p = NULL;
|
||||||
|
FIFO_count = 0;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
else if (FIFO_LastElement_p != NULL)
|
||||||
|
{
|
||||||
|
// buffer chain is broken; skip to last element
|
||||||
|
FIFO_FirstElement_p = FIFO_LastElement_p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// somehow the last element is NULL
|
||||||
|
FIFO_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// set first element pointer to next element
|
||||||
|
FIFO_FirstElement_p = next;
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buffer_is_full(void)
|
||||||
|
{
|
||||||
|
return FIFO_Full;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buffer_is_empty(void)
|
||||||
|
{
|
||||||
|
return ((FIFO_LastElement_p == NULL) && (FIFO_FirstElement_p == NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int number_of_elements_in_buffer()
|
||||||
|
{
|
||||||
|
return FIFO_count;
|
||||||
|
}
|
||||||
24
buffer_dyn/buffer.h
Normal file
24
buffer_dyn/buffer.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _HR_BroJZ_buffer_
|
||||||
|
#define _HR_BroJZ_buffer_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// interface for a buffer with int's
|
||||||
|
|
||||||
|
// put value i in buffer if buffer is not full
|
||||||
|
// returns true on success or false otherways
|
||||||
|
extern bool buffer_put(int i);
|
||||||
|
|
||||||
|
// get value from buffer and writes it to *p if buffer not empty
|
||||||
|
// returns true on success or false otherways
|
||||||
|
extern bool buffer_get(int *p);
|
||||||
|
|
||||||
|
// returns true when buffer is full or false otherways
|
||||||
|
extern bool buffer_is_full(void);
|
||||||
|
|
||||||
|
// returns true when buffer is empty or false otherways
|
||||||
|
extern bool buffer_is_empty(void);
|
||||||
|
|
||||||
|
extern unsigned int number_of_elements_in_buffer();
|
||||||
|
|
||||||
|
#endif
|
||||||
98
buffer_dyn/demo.c
Normal file
98
buffer_dyn/demo.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
void check_errno(int error)
|
||||||
|
{
|
||||||
|
if (error < 0)
|
||||||
|
{
|
||||||
|
perror("Error");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check(int error)
|
||||||
|
{
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
printf("Error: %s\n", strerror(error));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int msleep(long msec) // millisecond sleep
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = msec / 1000;
|
||||||
|
ts.tv_nsec = (msec % 1000) * 1000000;
|
||||||
|
return nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pthread_mutex_t m;
|
||||||
|
} args;
|
||||||
|
|
||||||
|
void *producer(void *arg) // function for producer thread
|
||||||
|
{
|
||||||
|
args a = *(args *)arg;
|
||||||
|
int i = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
check( pthread_mutex_lock(&a.m) );
|
||||||
|
if (!buffer_is_full())
|
||||||
|
{
|
||||||
|
buffer_put(i);
|
||||||
|
}
|
||||||
|
check( pthread_mutex_unlock(&a.m) );
|
||||||
|
i++;
|
||||||
|
check_errno( msleep(150) );
|
||||||
|
} while(i != 10);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *consumer(void *arg) // function for consumer thread
|
||||||
|
{
|
||||||
|
args a = *(args *)arg;
|
||||||
|
int i;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
check( pthread_mutex_lock(&a.m) );
|
||||||
|
if (buffer_get(&i))
|
||||||
|
{
|
||||||
|
check_errno( printf("%d\n", i) );
|
||||||
|
}
|
||||||
|
check( pthread_mutex_unlock(&a.m) );
|
||||||
|
check_errno( msleep(1000) );
|
||||||
|
} while (i != 9);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
args a;
|
||||||
|
|
||||||
|
pthread_mutexattr_t ma;
|
||||||
|
check( pthread_mutexattr_init(&ma) );
|
||||||
|
check( pthread_mutex_init(&a.m, &ma) );
|
||||||
|
|
||||||
|
pthread_t ptc, ptp;
|
||||||
|
check( pthread_create(&ptc, NULL, consumer, &a) );
|
||||||
|
check( pthread_create(&ptp, NULL, producer, &a) );
|
||||||
|
|
||||||
|
check( pthread_join(ptc, NULL) );
|
||||||
|
check( pthread_join(ptp, NULL) );
|
||||||
|
|
||||||
|
check( pthread_mutex_destroy(&a.m) );
|
||||||
|
check( pthread_mutexattr_destroy(&ma) );
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
54
buffer_dyn/test.c
Normal file
54
buffer_dyn/test.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
static int tests = 0;
|
||||||
|
static int fails = 0;
|
||||||
|
|
||||||
|
#define TEST(condition, ...) \
|
||||||
|
tests++;\
|
||||||
|
if (!(condition))\
|
||||||
|
{\
|
||||||
|
fails++;\
|
||||||
|
printf("Error: ");\
|
||||||
|
printf(__VA_ARGS__);\
|
||||||
|
printf("\n");\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PRINT_TEST_REPORT\
|
||||||
|
printf("%d tests performed: %d succeded, %d failed.\n", tests, tests - fails, fails);
|
||||||
|
|
||||||
|
void test_put_and_get(int test_value)
|
||||||
|
{
|
||||||
|
// test if test_value can be written into the buffer
|
||||||
|
TEST(buffer_put(test_value), "value %d can not be written into the buffer", test_value)
|
||||||
|
TEST(!buffer_is_empty(), "buffer still empty after writing into the buffer")
|
||||||
|
// test if test_value can be retrieved from the buffer
|
||||||
|
int retrieved_value;
|
||||||
|
TEST(buffer_get(&retrieved_value), "retrieving %d from the buffer failed", test_value)
|
||||||
|
TEST(retrieved_value == test_value, "wrong value (%d) retrieved from the buffer, expected %d", retrieved_value, test_value)
|
||||||
|
TEST(buffer_is_empty(), "buffer not empty after writing and retrieving one int to and from the buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// test if the buffer is empty at startup
|
||||||
|
TEST(buffer_is_empty(), "buffer not empty at startup")
|
||||||
|
// test if the buffer is not full at startup
|
||||||
|
TEST(!buffer_is_full(), "buffer full at startup")
|
||||||
|
// test if value 42 can be written into and retrieved from the buffer
|
||||||
|
test_put_and_get(42);
|
||||||
|
// test if value 0 can be written into and retrieved from the buffer
|
||||||
|
test_put_and_get(0);
|
||||||
|
// write to the buffer until it is full
|
||||||
|
while (buffer_put(13)) /* do nothing */;
|
||||||
|
TEST(buffer_is_full(), "buffer not full after put failed")
|
||||||
|
// retrieve from the buffer until it is empty
|
||||||
|
int retrieved_value;
|
||||||
|
while (buffer_get(&retrieved_value)) /* do nothing */;
|
||||||
|
TEST(buffer_is_empty(), "buffer not empty after get failed")
|
||||||
|
TEST(retrieved_value == 13, "retrieved value overridden after get failed, expected 13 but got %d", retrieved_value)
|
||||||
|
|
||||||
|
PRINT_TEST_REPORT
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user