cd build make # or 'make dev', for verbose ./rlr --help ./rlr # ./rlr -c 2 [...] ls output make clean # cleans the output/ too 1115 2019 00048 Dimakopoulos Theodoros - N POSIX semaphores - 2 POSIX shared memory segments - An improvised priority-stack - rlr instances can run in parallel ____ ____ ___ ____ _____ ____ ____ | _ \| _ \ / _ \ / ___| ____/ ___/ ___| | |_) | |_) | | | | | | _| \___ \___ \ | __/| _ <| |_| | |___| |___ ___) |__) | |_| |_| \_\\___/ \____|_____|____/____/ Parent Child --------------------------- init > < saw num yes please > < I want you're ready > < thank you yes please > < I want you're ready > < thank you ... < thank you waitpid The parent creates the resounces and counts the lines of the file. 72 | void opening_ceremony(Parent *r) { 73 | *( (int *) r->shmem_youre_ready ) = r->lines_in_file; 74 | 75 | WELL("broadcasting the total lines of the file"); 76 | post_all(r); 77 | 78 | WELL("waiting until they read the number of lines"); 79 | wait_all(r); 80 | 81 | WELL("signaling that they may start requesting"); 82 | post_all(r); 83 | } [ file c/parent/interface.c ] Then, the request loop may begin. The child leaves its message at a specific part of the segment, and notifies. 50 | offset = child->id * MAX_REQUEST_LEN; 51 | return shm + offset; [ file c/both/shmem.c ] 36 | req = write_a_request(child, prev_line); 37 | post_and_wait(child, &time_data); 38 | 39 | err = !isolate_line(isolated_line, 40 | child->shmem_thank_you, req.line_in_segment); 41 | tell_you_got_the_message(child); 42 | 43 | if (err) 44 | print_isolate_line_error(child, req); 45 | else 46 | record_and_wait(child, &time_data, req, isolated_line); [ file c/child/loop.c ] The parent cycles through the "rows" of the children's segments, reading one message per notification. 31 | _sem_wait(s->r->sem_yes_please); 32 | 33 | s->child = copy_and_clear_req(msg_cycler, req_str); 34 | 35 | if (req_says_got_it(req_str)) 36 | handle_req_saying_got_it(s); 37 | else 38 | handle_req_saying_i_want(s, req_str); [ file c/parent/loop.c ] The request is written back-to-front. 96 | for (i = MAX_REQUEST_LEN - 1; i >= 0; i--) 97 | child->shmem_i_want_offset[i] = req_str[i]; [ file c/child/loop.c ] Before writing it, it is guaranteed that there was a null character at position 0. That character should be overriden last, because while the parent does wait for a semaphore, he doesn't know the source of the semaphore post, and he will pick up the first child request he finds to be non-empty. A request might also be an 'I read it' message. 11 | void req_send_got_it(char *shm) { 12 | shm[1] = '\0'; 13 | shm[0] = '$'; 14 | } 15 | 16 | int req_says_got_it(char *msg) { 17 | return msg[0] == '$'; 18 | } [ file c/both/req.c ] When a request is about another file segment, it is stacked, but with priority. It's called 'bubbling' in the code. 53 | x1->file_segment = 1; 54 | x2->file_segment = 2; 55 | x3->file_segment = 1; 56 | s = stack_create(3); 57 | stack_push(s, x1); 58 | stack_push(s, x2); 59 | stack_push(s, x3); 60 | 61 | stack_pop(s, &y3); 62 | stack_pop(s, &y2); 63 | stack_pop(s, &y1); 64 | announce("test_stack_1_2_1_is_2_1_1", 65 | y1->file_segment == 2 && 66 | y2->file_segment == 1 && 67 | y3->file_segment == 1 68 | ); [ file c/tests/stack.c ] Then, multiple requests are popped and everyone interested is notified once the segment changes. 150 | stack_for_all_of_segment(s->r->requests, 151 | update_readers_and_tell_child, &a); [ file c/parent/loop.c ] 85 | if (rand() % 8 == 0) 86 | line_in_file = rand() % child->lines_in_file; 87 | else 88 | line_in_file = little_offset(child, *prev_line); [ file c/child/loop.c ] ___ _ _ ___ ____ _ ______ / _ \| | | |_ _| _ \| |/ / ___| | | | | | | || || |_) | ' /\___ \ | |_| | |_| || || _ <| . \ ___) | \__\_\\___/|___|_| \_\_|\_\____/ 12 | #define WELL(str) PRINT_PREFIX(); printf("%s\n", str) 13 | #define WELLL(printf_expr) PRINT_PREFIX(); \ 14 | printf_expr; printf("\n"); fflush(stdout) [ file c/both/dev_mode.h ] 24 | dev: FLAGS = -DDEV 25 | dev: $(RLR_MAIN_O) main.o 26 | gcc $(RLR_MAIN_O) main.o -o rlr [ file build/Makefile ] 'const' isn't used for primitive types because data eventually reaches a standard function such as readline, which doesn't declare any arguments constant. So it feels awkward. Plus I follow a retro style. The long names aren't retro but the declarations are at the top. Similarly I avoid unsigned types because they might come from the return value of a function. Type conversions remove any guarantee unsigned types are supposed to help with. There are tiny functions because I'm trying out the "Uncle Bob" principles of clean coding. The main point is to skip reading code easily. I do rely on jump-to-definition functionality, though it does break with preprocessor definitions. _____ _____ ____ _____ ____ |_ _| ____/ ___|_ _/ ___| | | | _| \___ \ | | \___ \ | | | |___ ___) || | ___) | |_| |_____|____/ |_| |____/ I tried out some test driven development. It didn't go the best but it was entertaining. Some .c files are different with make test 10 | #ifndef TEST 11 | 12 | int testable_fork() { 13 | return fork(); 14 | } [ file c/before/be_yourself.c ] 8 | static int num_of_forks; 9 | 10 | int testable_fork() { 11 | num_of_forks++; 12 | return fork(); 13 | } [ file c/tests/fork.c ] The specific example is from a test that was actually written after making the forks work. There were bugs and any part of the code was suspicious, so I thought to secure the forks. The tests aren't very sophisticated, they 'announce' the situation. Also they never deallocate. 11 | announce("errors_on_letter", -1 == req_parse("<2a,4>")); 12 | announce("parses_one_digit", 2 == req_parse("<2,4>")); [ file c/tests/req.c ] Code that has to do with the loops of the parent and child is spread out in multiple files. I'm sorry about it, it evolved gradually this way. Most of it, however, is in the files called interface and loop. Some is in the be_yourself files in the before folder. The main reasons are, for one, to separate the pre-fork parent, the post-fork parent and the children as much as possible, and secondly to help with testing. Last update: Mon Dec 19 10:09:44 EET 2022
TheodoreAlenas/uni-os-tdd
Tried test driven development for an operating systems University project written in C.
C