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