├── 1 ├── CMakeLists.txt └── libcoro.h ├── 2 ├── CMakeLists.txt ├── parser.h ├── solution.cpp └── test_parser.py ├── 3 ├── CMakeLists.txt └── userfs.cpp ├── 4 ├── CMakeLists.txt └── thread_pool.cpp ├── 5 ├── chat.cpp ├── chat.h ├── CMakeLists.txt ├── chat_server_exe.cpp ├── chat_client.cpp ├── chat_client.h ├── chat_client_exe.cpp ├── chat_server.h └── chat_server.cpp ├── examples ├── cpp20_coroutines │ ├── Makefile │ └── README.md ├── reuse_port_addr │ ├── client.c │ ├── server.c │ └── README.md └── coro_alloca.c ├── utils ├── heap_help │ ├── heap_help.h │ └── README.md ├── unit.cpp ├── unit.h └── unitpp.h ├── lecture_examples ├── 10_users │ ├── 2_not_existing_chown.sh │ ├── 4_setuid.c │ ├── 8_pgrp.c │ ├── 6_access_mask.c │ ├── 7_umask.c │ ├── 3_group.sh │ ├── 1_useradd.sh │ ├── 5_seteuid.c │ ├── 9_client.c │ └── 9_daemon.c ├── 2_processes │ ├── 1_5_exit.c │ ├── 13_core_dump.c │ ├── 6_stdout.c │ ├── 8_dup.c │ ├── 7_basic_append.c │ ├── 5_argv_env.c │ ├── 9_fork_dup.c │ ├── 11_advanced_pipe.c │ ├── 12_exec.c │ ├── 4_shared_mem.c │ ├── 10_pipe.c │ ├── 3_fs_proc.c │ ├── 10_5_pipe_close.c │ ├── 2_proc_memory.c │ ├── 1_createprocess_win.c │ └── 1_fork.c ├── 5_files │ ├── 1_dirent.c │ └── 2_fstat.c ├── 7_ipc │ ├── 4_fifo_client.c │ ├── 4_fifo_server.c │ ├── 5_key_t.c │ ├── 10_sem_mutex.h │ ├── 12_sem_posix.c │ ├── 13_socketpair.c │ ├── 6_msgget.c │ ├── 15_sock_client.c │ ├── 7_msgsend_client.c │ ├── 7_msgsend_server_flag.c │ ├── 11_shm.c │ ├── 7_msgsend_server.c │ ├── 1_simple_sort.c │ ├── 9_sem_event_cons.c │ ├── 15_sock_server.c │ ├── 14_sock_dgram.c │ ├── 9_sem_event_source.c │ ├── 2_parallel_sort.c │ ├── 8_sem.c │ └── 3_mem_sort.c ├── 6_threads │ ├── 2_non_volatile.c │ ├── 3_volatile.c │ ├── 9_clone_vs_pthread.c │ ├── 17_thread_stacks.c │ ├── 4_not_atomic.c │ ├── 5_atomic.c │ ├── 15_gcc_thread.c │ ├── 6_bad_lock.c │ ├── 7_spin_lock.c │ ├── clone.h │ ├── 16_pthread_key.c │ ├── 1_clone.c │ ├── 8_futex.c │ ├── 10_cthreads_1.c │ ├── 8_6_spinlock_acq_rel.c │ ├── 8_5_random_generator.h │ ├── 10_cthreads_2.c │ ├── 11_cthread_bad_detach.c │ ├── 8_5_cpu_reordering.c │ ├── 12_cthread_detach.c │ ├── 14_condvar.c │ ├── 13_simple_condvar_2.c │ └── 13_simple_condvar_1.c ├── 3_memory │ ├── 2_catch_sigsegv.c │ └── 1_compact_struct.c ├── 4_signals │ ├── 11_setjmp_problem.c │ ├── 3_intr_malloc.c │ ├── 2_pause_hangs.c │ ├── 1_basic_signal.c │ ├── 4_sigprocmask.c │ ├── 5_sigaction_info.c │ ├── 7_sigaction_jmp.c │ ├── 8_sigaction_sigjmp.c │ ├── 9_sigsuspend.c │ ├── 12_libcoro.h │ ├── 6_sigaction_mask.c │ ├── 12_libcoro_example.c │ └── 10_sigaltstack.c ├── 9_aio │ ├── 1_nonblock.c │ ├── 2_flock.c │ ├── 4_client.c │ ├── 3_fcntl_lock.c │ ├── 5_server_poll.c │ ├── 4_server_select.c │ ├── 7_server_epoll.c │ └── 6_server_kqueue.c └── 8_net │ ├── 1_socket_protocol.c │ ├── 2_getaddrinfo.c │ ├── 3_client.c │ └── 3_server.c ├── advanced └── boost_chat │ ├── chat.cpp │ ├── Makefile │ ├── chat_server.h │ ├── chat.h │ ├── chat_client.h │ ├── chat_server_exe.cpp │ ├── chat_client_exe.cpp │ └── task_eng.txt ├── leaderboard.md ├── allcups ├── DockAllcups.dockerfile └── test.sh ├── exam ├── rules_eng.txt └── questions_eng.txt └── README.md /examples/cpp20_coroutines/Makefile: -------------------------------------------------------------------------------- 1 | all: iocoro.cpp iocoro.h main.cpp 2 | g++ iocoro.cpp main.cpp --std=c++20 3 | -------------------------------------------------------------------------------- /utils/heap_help/heap_help.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | uint64_t 6 | heaph_get_alloc_count(void); 7 | -------------------------------------------------------------------------------- /lecture_examples/10_users/2_not_existing_chown.sh: -------------------------------------------------------------------------------- 1 | touch test.txt 2 | ls -l 3 | # User with id 2000 shall not exist. 4 | chown 2000 test.txt 5 | ls -l 6 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/1_5_exit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void 5 | _start() 6 | { 7 | printf("hello, world\n"); 8 | exit(0); 9 | } 10 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/13_core_dump.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | int a = 1; 4 | int b = 2; 5 | char buffer[100]; 6 | buffer[0] = 3; 7 | buffer[50] = 4; 8 | buffer[10000] = 5; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/6_stdout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | char buf[] = "write to 1\n"; 7 | write(STDOUT_FILENO, buf, sizeof(buf)); 8 | printf("stdout fileno = %d\n", STDOUT_FILENO); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /5/chat.cpp: -------------------------------------------------------------------------------- 1 | #include "chat.h" 2 | 3 | #include 4 | 5 | int 6 | chat_events_to_poll_events(int mask) 7 | { 8 | int res = 0; 9 | if ((mask & CHAT_EVENT_INPUT) != 0) 10 | res |= POLLIN; 11 | if ((mask & CHAT_EVENT_OUTPUT) != 0) 12 | res |= POLLOUT; 13 | return res; 14 | } 15 | -------------------------------------------------------------------------------- /utils/unit.cpp: -------------------------------------------------------------------------------- 1 | #include "unit.h" 2 | 3 | #include 4 | 5 | bool 6 | doCmdMaxPoints(int argc, char **argv) 7 | { 8 | for (int i = 0; i < argc; ++i) 9 | { 10 | if (strcmp(argv[i], "--max_points") == 0) 11 | { 12 | return true; 13 | } 14 | } 15 | return false; 16 | } 17 | -------------------------------------------------------------------------------- /lecture_examples/10_users/4_setuid.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | 5 | int 6 | main() 7 | { 8 | uid_t uid, euid, suid; 9 | getresuid(&uid, &euid, &suid); 10 | printf("uid = %d, euid = %d, suid = %d\n", (int) uid, (int) euid, 11 | (int) suid); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/8_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | int fd = open("tmp.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 8 | int fd2 = dup(fd); 9 | dprintf(fd2, "1 "); 10 | close(fd2); 11 | dprintf(fd, "2 "); 12 | close(fd); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /advanced/boost_chat/chat.cpp: -------------------------------------------------------------------------------- 1 | #include "chat.h" 2 | 3 | event::event() : m_is_set(false) {} 4 | 5 | void 6 | event::send() 7 | { 8 | std::unique_lock lock(m_mutex); 9 | m_is_set = true; 10 | } 11 | 12 | void 13 | event::recv() 14 | { 15 | std::unique_lock lock(m_mutex); 16 | while (not m_is_set) 17 | m_cond.wait(lock); 18 | } 19 | -------------------------------------------------------------------------------- /leaderboard.md: -------------------------------------------------------------------------------- 1 | # Leaderboard 2 | 3 | People who scored > 100 points during the course, sorted by the points. 4 | 5 | ``` 6 | 134 - Nikolai Nechaev 7 | 130 - Artem Starikov 8 | 116 - Selin Maksim @Maksim3318 9 | 113 - Mark Chausov 10 | 111 - Artem Bulgakov 11 | 109 - Dmitrii Alekhin 12 | 108 - Stepchenko Alexander @GeoCHiP 13 | 107 - Ilya Siluyanov 14 | 101 - Ruslan Gilvanov 15 | ``` 16 | -------------------------------------------------------------------------------- /lecture_examples/10_users/8_pgrp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int 5 | main(int argc, const char **argv) 6 | { 7 | pid_t id = getpgrp(); 8 | pid_t pid = getpid(); 9 | int i = 0; 10 | if (argv[1][0] == 'w') 11 | printf("1\n"); 12 | else 13 | scanf("%d", &i); 14 | fprintf(stderr, "group = %d, pid = %d\n", 15 | (int) id, (int) pid); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/7_basic_append.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | int fd = open("tmp.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 8 | pid_t pid = getpid(); 9 | printf("press any key to write my pid %d\n", (int) pid); 10 | getchar(); 11 | 12 | dprintf(fd, "%d ", (int) pid); 13 | close(fd); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /lecture_examples/10_users/6_access_mask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main() 9 | { 10 | mode_t mode = S_IRUSR | S_IXUSR; 11 | int fd = open("tmp.txt", O_CREAT | O_RDWR, mode); 12 | if (fd == -1) 13 | printf("error = %s\n", strerror(errno)); 14 | else 15 | close(fd); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /lecture_examples/10_users/7_umask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main() 9 | { 10 | mode_t mode = S_IRWXU | S_IRWXO | S_IRWXG; 11 | int fd = open("tmp.txt", O_CREAT | O_RDWR, mode); 12 | if (fd == -1) 13 | printf("error = %s\n", strerror(errno)); 14 | else 15 | close(fd); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /lecture_examples/10_users/3_group.sh: -------------------------------------------------------------------------------- 1 | # Create a new grop. 2 | groupadd testgroup 3 | tail -n 1 /etc/group 4 | 5 | # Fill the group. 6 | useradd -m sysproguser 7 | useradd -m sysproguser2 8 | usermod -aG testgroup sysproguser 9 | usermod -aG testgroup sysproguser2 10 | 11 | tail -n 3 /etc/group 12 | 13 | # Cleanup. 14 | groupdel testgroup 15 | tail -n 3 /etc/group 16 | userdel -r sysproguser 17 | userdel -r sysproguser2 18 | -------------------------------------------------------------------------------- /lecture_examples/5_files/1_dirent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | DIR *dir = opendir("."); 7 | struct dirent *dirent = readdir(dir); 8 | while (dirent != NULL) { 9 | printf("name = %s, inode number = %d, type = %d\n", 10 | dirent->d_name, (int) dirent->d_ino, 11 | (int) dirent->d_type); 12 | dirent = readdir(dir); 13 | } 14 | closedir(dir); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/4_fifo_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | int fd = open("/tmp/fifo_server", O_WRONLY); 12 | pid_t pid = getpid(); 13 | write(fd, &pid, sizeof(pid)); 14 | printf("my pid %d is sent to server\n", (int) pid); 15 | close(fd); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/5_argv_env.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv, char **env) 5 | { 6 | char *path = getenv("PATH"); 7 | printf("env: %p\n", path); 8 | printf("argv: %p\n", argv); 9 | int a; 10 | printf("stack: %p\n", &a); 11 | void *m = malloc(100); 12 | printf("heap: %p\n", m); 13 | free(m); 14 | 15 | int i = 0; 16 | while (env[i] != NULL) 17 | printf("%s\n", env[i++]); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/9_fork_dup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int fd = open("tmp.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 9 | int fd2 = dup(fd); 10 | if (fork() == 0) { 11 | close(fd); 12 | dprintf(fd2, "%d ", (int) getpid()); 13 | close(fd2); 14 | return 0; 15 | } 16 | close(fd2); 17 | wait(NULL); 18 | dprintf(fd, "%d ", (int) getpid()); 19 | close(fd); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/2_non_volatile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void * 7 | thread_f(void *arg) 8 | { 9 | *((bool *)arg) = true; 10 | return NULL; 11 | } 12 | 13 | int 14 | main() 15 | { 16 | const bool is_finished = false; 17 | pthread_t tid; 18 | pthread_create(&tid, NULL, thread_f, 19 | (void *) &is_finished); 20 | while (! is_finished) 21 | sched_yield(); 22 | 23 | pthread_join(tid, NULL); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /lecture_examples/3_memory/2_catch_sigsegv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static jmp_buf jmp; 6 | 7 | void 8 | process_signal(int code) 9 | { 10 | printf("Process SIGSEGV, code = %d, SIGSEGV = %d\n", code, SIGSEGV); 11 | longjmp(jmp, 1); 12 | } 13 | 14 | int 15 | main() 16 | { 17 | char *p = NULL; 18 | signal(SIGSEGV, process_signal); 19 | printf("Before SIGSEGV\n"); 20 | if (setjmp(jmp) == 0) 21 | *p = 100; 22 | printf("After SIGSEGV\n"); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/3_volatile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void * 7 | thread_f(void *arg) 8 | { 9 | *((bool *)arg) = true; 10 | return NULL; 11 | } 12 | 13 | int 14 | main() 15 | { 16 | const volatile bool is_finished = false; 17 | pthread_t tid; 18 | pthread_create(&tid, NULL, thread_f, 19 | (void *) &is_finished); 20 | while (! is_finished) 21 | sched_yield(); 22 | 23 | pthread_join(tid, NULL); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/11_advanced_pipe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int to_child[2]; 9 | pipe(to_child); 10 | char buf[16]; 11 | dup2(to_child[0], 0); 12 | if (fork() == 0) { 13 | close(to_child[1]); 14 | int n; 15 | scanf("%d", &n); 16 | printf("%d: read %d\n", (int) getpid(), n); 17 | return 0; 18 | } 19 | close(to_child[0]); 20 | write(to_child[1], "100", sizeof("100")); 21 | wait(NULL); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /lecture_examples/10_users/1_useradd.sh: -------------------------------------------------------------------------------- 1 | # Get root role. 2 | sudo su 3 | 4 | # Create a user and check that it worked. 5 | useradd -m -c "comment" sysproguser 6 | tail -n 1 /etc/passwd 7 | tail -n 1 /etc/shadow 8 | 9 | # Change password of a new user. 10 | passwd sysproguser 11 | su vladislav # your original user, before executing this script. 12 | su sysproguser 13 | 14 | # Delete the user 15 | exit 16 | userdel -r sysproguser 17 | # If this command says that there are working processes, then 18 | # kill them and repeat the command. 19 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/12_exec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int to_child[2]; 9 | pipe(to_child); 10 | char buf[16]; 11 | dup2(to_child[0], 0); 12 | if (fork() == 0) { 13 | close(to_child[1]); 14 | return execlp("python3", "python3", "-i", NULL); 15 | } 16 | close(to_child[0]); 17 | const char cmd[] = "print(100 + 200)"; 18 | write(to_child[1], cmd, sizeof(cmd)); 19 | close(to_child[1]); 20 | wait(NULL); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /allcups/DockAllcups.dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:noble 2 | RUN export DEBIAN_FRONTEND=noninteractive && \ 3 | TZ=Etc/UTC && \ 4 | apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | tzdata unzip jq python3 clang make 7 | RUN ln /usr/bin/clang /usr/bin/gcc 8 | RUN mkdir /sysprog 9 | RUN mkdir /sysprog/solution 10 | 11 | WORKDIR /sysprog 12 | 13 | ENV RESOURCES_DIR_MOUNT='/tmp/data' 14 | ENV SOLUTION_MOUNT='/opt/client/input' 15 | ENV RESULT_LOCATION='/opt/results/output.json' 16 | 17 | CMD ["/tmp/data/allcups/test.sh"] 18 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/4_fifo_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | mkfifo("/tmp/fifo_server", S_IRWXU | S_IRWXO); 13 | int fd = open("/tmp/fifo_server", O_RDONLY); 14 | while (1) { 15 | pid_t new_client; 16 | if (read(fd, &new_client, sizeof(pid_t)) > 0) 17 | printf("new client %d\n", (int) new_client); 18 | else 19 | sched_yield(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/5_key_t.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void 7 | try_key(const char *path, int id) 8 | { 9 | key_t key = ftok(path, id); 10 | printf("key(\"%s\", %d) = ", path, id); 11 | if (key == -1) 12 | printf("%s\n", strerror(errno)); 13 | else 14 | printf("%d\n", (int) key); 15 | } 16 | 17 | int main(int argc, const char **argv) 18 | { 19 | try_key("/not/exising/path", 100); 20 | try_key(argv[0], 100); 21 | try_key(argv[0], 101); 22 | try_key(argv[0], 101 + 1024); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/11_setjmp_problem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static volatile jmp_buf buf; 7 | static volatile bool stop = false; 8 | static volatile int total = 0; 9 | 10 | static void 11 | testf2(void) 12 | { 13 | setjmp(buf); 14 | if (++total > 10) { 15 | printf("Bad exit\n"); 16 | exit(-1); 17 | } 18 | } 19 | 20 | static void 21 | testf(void) 22 | { 23 | testf2(); 24 | } 25 | 26 | int 27 | main(void) 28 | { 29 | testf(); 30 | if (! stop) { 31 | stop = true; 32 | printf("Аfter first call\n"); 33 | longjmp(buf, 1); 34 | } 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/10_sem_mutex.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct semaphore { 4 | int counter; 5 | pthread_mutex_t mutex; 6 | pthread_cond_t cond; 7 | }; 8 | 9 | static inline void 10 | semaphore_get(struct semaphore *sem) 11 | { 12 | pthread_mutex_lock(&sem->mutex); 13 | while (sem->counter == 0) 14 | pthread_cond_wait(&sem->cond, &sem->mutex); 15 | sem->counter--; 16 | pthread_mutex_unlock(&sem->mutex); 17 | } 18 | 19 | static inline void 20 | semaphore_put(struct semaphore *sem) 21 | { 22 | pthread_mutex_lock(&sem->mutex); 23 | sem->counter++; 24 | pthread_cond_signal(&sem->cond); 25 | pthread_mutex_unlock(&sem->mutex); 26 | } 27 | -------------------------------------------------------------------------------- /examples/reuse_port_addr/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int 12 | main(void) 13 | { 14 | struct sockaddr_in in; 15 | in.sin_family = AF_INET; 16 | inet_aton("127.0.0.1", &in.sin_addr); 17 | in.sin_port = htons(3333); 18 | int s = socket(AF_INET, SOCK_STREAM, 0); 19 | if (connect(s, (struct sockaddr *) &in, sizeof(in)) < 0) 20 | printf("connect error: %s\n", strerror(errno)); 21 | else 22 | printf("connect success\n"); 23 | close(s); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/4_shared_mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | char *shared_mem = (char *) mmap(NULL, 100, PROT_READ | PROT_WRITE, 9 | MAP_ANON | MAP_SHARED, -1, 0); 10 | char *private_mem = (char *) malloc(100); 11 | shared_mem[0] = 55; 12 | private_mem[0] = 55; 13 | if (fork() == 0) { 14 | shared_mem[0] = 56; 15 | private_mem[0] = 56; 16 | goto exit; 17 | } 18 | wait(NULL); 19 | printf("shared: %d, private: %d\n", (int) shared_mem[0], 20 | (int) private_mem[0]); 21 | exit: 22 | munmap(shared_mem, 100); 23 | free(private_mem); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/3_intr_malloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int alarm_cnt = 0; 10 | 11 | static void 12 | do_malloc(void) 13 | { 14 | for (int i = 0; i < 10; ++i) 15 | free(malloc(1024 * 1024 * i)); 16 | } 17 | 18 | static void 19 | on_new_signal(int signum) 20 | { 21 | printf("Interrupted %d times\n", ++alarm_cnt); 22 | do_malloc(); 23 | } 24 | 25 | int 26 | main(void) 27 | { 28 | printf("my pid: %d\n", (int) getpid()); 29 | signal(SIGUSR1, on_new_signal); 30 | while (true) 31 | do_malloc(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/9_clone_vs_pthread.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "clone.h" 10 | 11 | volatile bool is_finished = false; 12 | 13 | volatile pthread_t child_id; 14 | 15 | int 16 | thread_f(void *arg) 17 | { 18 | child_id = pthread_self(); 19 | is_finished = true; 20 | return 0; 21 | } 22 | 23 | int 24 | main() 25 | { 26 | void *stack; 27 | thread_create_clone(thread_f, NULL, &stack); 28 | 29 | while (! is_finished) 30 | sched_yield(); 31 | 32 | pthread_join(child_id, NULL); 33 | free(stack); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /advanced/boost_chat/Makefile: -------------------------------------------------------------------------------- 1 | CXX_FLAGS = -Wextra -Werror -Wall --std=c++17 2 | 3 | all: lib exe test 4 | 5 | lib: chat.cpp chat_client.cpp chat_server.cpp 6 | g++ $(CXX_FLAGS) -c chat.cpp -o chat.o 7 | g++ $(CXX_FLAGS) -c chat_client.cpp -o chat_client.o 8 | g++ $(CXX_FLAGS) -c chat_server.cpp -o chat_server.o 9 | 10 | exe: lib chat_client_exe.cpp chat_server_exe.cpp 11 | g++ $(CXX_FLAGS) chat_client_exe.cpp chat.o chat_client.o -o client -lpthread 12 | g++ $(CXX_FLAGS) chat_server_exe.cpp chat.o chat_server.o -o server -lpthread 13 | 14 | test: lib 15 | g++ $(CXX_FLAGS) test.cpp chat.o chat_client.o chat_server.o -o test \ 16 | -I ../../utils -lpthread 17 | 18 | clean: 19 | rm *.o 20 | rm client server test 21 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/17_thread_stacks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile bool finish = false; 7 | volatile bool ptr_is_set = false; 8 | volatile int *a_ptr = NULL; 9 | 10 | void * 11 | thread_f(void *arg) 12 | { 13 | int a = 100; 14 | printf("child stack top = %p\n", &a); 15 | a_ptr = &a; 16 | ptr_is_set = true; 17 | while (! finish) {} 18 | return NULL; 19 | } 20 | 21 | int 22 | main() 23 | { 24 | pthread_t t; 25 | printf("main stack top = %p\n", &t); 26 | pthread_create(&t, NULL, thread_f, NULL); 27 | while (! ptr_is_set) {} 28 | printf("foreign a = %d\n", *a_ptr); 29 | finish = true; 30 | pthread_join(t, NULL); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/4_not_atomic.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "clone.h" 8 | 9 | volatile bool is_finished = false; 10 | volatile int counter = 0; 11 | 12 | int 13 | thread_f(void *arg) 14 | { 15 | for (int i = 0; i < 100000; ++i) 16 | counter = counter + 1; 17 | is_finished = true; 18 | return 0; 19 | } 20 | 21 | int 22 | main() 23 | { 24 | void *stack; 25 | thread_create_clone(thread_f, NULL, &stack); 26 | for (int i = 0; i < 100000; ++i) 27 | counter = counter + 1; 28 | while (! is_finished) 29 | sched_yield(); 30 | printf("counter = %d\n", counter); 31 | free(stack); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/2_pause_hangs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static bool is_sig_received = false; 8 | 9 | static void 10 | on_new_signal(int signum) 11 | { 12 | printf("Received a signal\n"); 13 | is_sig_received = true; 14 | } 15 | 16 | static void 17 | interrupt(void) 18 | { 19 | getchar(); 20 | } 21 | 22 | int 23 | main(void) 24 | { 25 | printf("my pid: %d\n", (int) getpid()); 26 | signal(SIGUSR1, on_new_signal); 27 | printf("Installed a handler\n"); 28 | interrupt(); 29 | while (! is_sig_received) { 30 | interrupt(); 31 | printf("Wait for a signal\n"); 32 | pause(); 33 | } 34 | printf("Finish\n"); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/10_pipe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() 7 | { 8 | int to_parent[2]; 9 | int to_child[2]; 10 | pipe(to_child); 11 | pipe(to_parent); 12 | char buf[16]; 13 | if (fork() == 0) { 14 | close(to_parent[0]); 15 | close(to_child[1]); 16 | read(to_child[0], buf, sizeof(buf)); 17 | printf("%d: read %s\n", (int) getpid(), buf); 18 | write(to_parent[1], "hello2", sizeof("hello2")); 19 | return 0; 20 | } 21 | close(to_parent[1]); 22 | close(to_child[0]); 23 | write(to_child[1], "hello1", sizeof("hello")); 24 | read(to_parent[0], buf, sizeof(buf)); 25 | printf("%d: read %s\n", (int) getpid(), buf); 26 | wait(NULL); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/5_atomic.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "clone.h" 8 | 9 | volatile bool is_finished = false; 10 | volatile int counter = 0; 11 | 12 | int 13 | thread_f(void *arg) 14 | { 15 | for (int i = 0; i < 100000; ++i) 16 | __sync_add_and_fetch(&counter, 1); 17 | is_finished = true; 18 | return 0; 19 | } 20 | 21 | int 22 | main() 23 | { 24 | void *stack; 25 | thread_create_clone(thread_f, NULL, &stack); 26 | for (int i = 0; i < 100000; ++i) 27 | __sync_add_and_fetch(&counter, 1); 28 | while (! is_finished) 29 | sched_yield(); 30 | printf("counter = %d\n", counter); 31 | free(stack); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /advanced/boost_chat/chat_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "chat.h" 4 | 5 | #include 6 | 7 | namespace boost { namespace asio { class io_context; } } 8 | 9 | class chat_server_ctx; 10 | 11 | using chat_server_on_msg_f = std::function msg)>; 12 | 13 | class chat_server final 14 | { 15 | public: 16 | chat_server( 17 | boost::asio::io_context& ioCtx); 18 | ~chat_server(); 19 | 20 | chat_errcode 21 | start( 22 | uint16_t port); 23 | 24 | uint16_t 25 | port() const; 26 | 27 | void 28 | recv_async( 29 | chat_server_on_msg_f&& cb); 30 | 31 | void 32 | feed_async( 33 | std::string_view text); 34 | 35 | private: 36 | const std::shared_ptr m_ctx; 37 | }; 38 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/1_basic_signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void 8 | on_new_signal(int signum) 9 | { 10 | switch(signum) { 11 | case SIGINT: 12 | printf("caught sigint\n"); 13 | break; 14 | case SIGUSR1: 15 | printf("caught usr1\n"); 16 | break; 17 | case SIGUSR2: 18 | printf("caught usr2\n"); 19 | break; 20 | default: 21 | printf("caught unknown signal\n"); 22 | exit(-1); 23 | } 24 | } 25 | 26 | int 27 | main(void) 28 | { 29 | printf("my pid: %d\n", (int) getpid()); 30 | signal(SIGINT, on_new_signal); 31 | signal(SIGUSR1, on_new_signal); 32 | signal(SIGUSR2, on_new_signal); 33 | while(true) pause(); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /lecture_examples/3_memory/1_compact_struct.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct complex_struct { 4 | int id; 5 | double a; 6 | long d; 7 | char buf[10]; 8 | char *long_buf; 9 | }; 10 | 11 | struct complex_struct * 12 | complex_struct_bad_new(int long_buf_len) 13 | { 14 | struct complex_struct *ret = 15 | (struct complex_struct *) malloc(sizeof(*ret)); 16 | ret->long_buf = (char *) malloc(long_buf_len); 17 | return ret; 18 | } 19 | 20 | struct complex_struct * 21 | complex_struct_good_new(int long_buf_len) 22 | { 23 | struct complex_struct *ret; 24 | int size = sizeof(*ret) + long_buf_len; 25 | ret = (struct complex_struct *) malloc(size); 26 | ret->long_buf = (char *) ret + sizeof(*ret); 27 | return ret; 28 | } 29 | 30 | int main() 31 | { 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/4_sigprocmask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static int cnt = 0; 7 | 8 | static void 9 | on_new_signal(int signum) 10 | { 11 | printf("Processed a signal %dth time\n", ++cnt); 12 | } 13 | 14 | int 15 | main(void) 16 | { 17 | printf("Start\n"); 18 | signal(SIGINT, on_new_signal); 19 | sigset_t oldm, newm, pendingm; 20 | sigemptyset(&newm); 21 | sigaddset(&newm, SIGINT); 22 | printf("Block SIGINT\n"); 23 | sigprocmask(SIG_BLOCK, &newm, &oldm); 24 | getchar(); 25 | sigpending(&pendingm); 26 | if (sigismember(&pendingm, SIGINT)) 27 | printf("SIGINT is pending\n"); 28 | printf("Unblock SIGINT\n"); 29 | sigprocmask(SIG_SETMASK, &oldm, NULL); 30 | while(cnt < 4) 31 | pause(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/15_gcc_thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static __thread int a = -1; 7 | 8 | volatile int ready_to_print = 0; 9 | volatile bool print = false; 10 | 11 | void * 12 | thread_f(void *arg) 13 | { 14 | a = (int) arg; 15 | __sync_fetch_and_add(&ready_to_print, 1); 16 | while (! print) {} 17 | printf("a = %d\n", a); 18 | return NULL; 19 | } 20 | 21 | int 22 | main() 23 | { 24 | pthread_t t1, t2; 25 | a = 0; 26 | pthread_create(&t1, NULL, thread_f, (void *) 1); 27 | pthread_create(&t2, NULL, thread_f, (void *) 2); 28 | while (__sync_fetch_and_add(&ready_to_print, 0) != 2) {} 29 | print = true; 30 | pthread_join(t1, NULL); 31 | pthread_join(t2, NULL); 32 | printf("main a = %d\n", a); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/3_fs_proc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | int fd_me = open("3_fs_proc.c", O_RDONLY); 12 | char *shared_mem = (char *) mmap(NULL, 100, PROT_READ, 13 | MAP_FILE | MAP_SHARED, fd_me, 0); 14 | char buf[128]; 15 | 16 | sprintf(buf, "/proc/%d/maps", (int) getpid()); 17 | int fd = open(buf, O_RDONLY); 18 | printf("print %s\n", buf); 19 | if (fd == -1) { 20 | printf("exit %s\n", strerror(errno)); 21 | exit(1); 22 | } 23 | int nbyte; 24 | while ((nbyte = read(fd, buf, sizeof(buf))) > 0) 25 | printf("%.*s", nbyte, buf); 26 | printf("\n"); 27 | close(fd); 28 | munmap(shared_mem, 100); 29 | close(fd_me); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /advanced/boost_chat/chat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum 8 | { 9 | CHAT_RECV_BUF_SIZE = 128, 10 | }; 11 | 12 | enum chat_errcode 13 | { 14 | CHAT_ERR_NONE = 0, 15 | CHAT_ERR_INVALID_ARGUMENT, 16 | CHAT_ERR_TIMEOUT, 17 | CHAT_ERR_PORT_BUSY, 18 | CHAT_ERR_NO_ADDR, 19 | CHAT_ERR_ALREADY_STARTED, 20 | CHAT_ERR_NOT_IMPLEMENTED, 21 | CHAT_ERR_NOT_STARTED, 22 | CHAT_ERR_SYS, 23 | CHAT_ERR_CANCELED, 24 | }; 25 | 26 | struct chat_message 27 | { 28 | // Author's name. 29 | std::string m_author; 30 | std::string m_data; 31 | 32 | // 33 | }; 34 | 35 | struct event 36 | { 37 | public: 38 | event(); 39 | 40 | void 41 | send(); 42 | void 43 | recv(); 44 | 45 | private: 46 | std::mutex m_mutex; 47 | std::condition_variable m_cond; 48 | bool m_is_set; 49 | }; -------------------------------------------------------------------------------- /lecture_examples/6_threads/6_bad_lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct complex_thing { 7 | bool lock; 8 | char string[128]; 9 | }; 10 | 11 | volatile struct complex_thing thing; 12 | 13 | volatile bool start = false; 14 | 15 | void * 16 | thread_f(void *arg) 17 | { 18 | int id = (int) arg; 19 | while (! start) {}; 20 | if (! thing.lock) { 21 | thing.lock = true; 22 | for (int i = 0; i < sizeof(thing.string) - 1; ++i) 23 | thing.string[i] = 'a' + id; 24 | } 25 | return NULL; 26 | } 27 | 28 | int 29 | main() 30 | { 31 | pthread_t tid[6]; 32 | for (int i = 0; i < 6; ++i) 33 | pthread_create(&tid[i], NULL, thread_f, (void *) i); 34 | start = true; 35 | for (int i = 0; i < 6; ++i) 36 | pthread_join(tid[i], NULL); 37 | printf("%s\n", thing.string); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /exam/rules_eng.txt: -------------------------------------------------------------------------------- 1 | ## Rules of the exam 2 | 3 | - You get 3 random questions from the list. 4 | - We talk one on one. 5 | - Each question gives you between 0 and 5 points. Max total is 15. 6 | - The exam is passed with 8 points or more. 7 | - Cheating nullifies the question on which it happens. 8 | - I can ask about arbitrary follow-up details for each question. 9 | - If after the exam your total point count for the whole course is still less than minimal, but the exam was passed, then your course mark is lifted to the minimal passing value. 10 | - The exam is mandatory for everyone who has not reached 100 points or above. For those who did perform >= 100, the exam is just an optional opportunity to gain even more points. 11 | - If the exam is not passed (points < 8), then it must be retaken. Even if the points gained during the course are higher than the passing threshold. 12 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/1_nonblock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void 8 | make_fd_nonblocking(int fd) 9 | { 10 | int old_flags = fcntl(fd, F_GETFL); 11 | fcntl(fd, F_SETFL, old_flags | O_NONBLOCK); 12 | } 13 | 14 | int 15 | main(int argc, const char **argv) 16 | { 17 | make_fd_nonblocking(STDIN_FILENO); 18 | int value = 0; 19 | int rc = scanf("%d", &value); 20 | printf("scanf rc = %d\n", rc); 21 | printf("scanf error = %s\n", strerror(errno)); 22 | rc = read(STDIN_FILENO, &value, sizeof(value)); 23 | printf("read rc = %d\n", rc); 24 | printf("read error = %s\n", strerror(errno)); 25 | 26 | while (1) { 27 | rc = scanf("%d", &value); 28 | if (rc > 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) 29 | break; 30 | } 31 | printf("value = %d\n", value); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /advanced/boost_chat/chat_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "chat.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace boost { namespace asio { class io_context; } } 9 | 10 | using chat_client_on_connect_f = std::function; 11 | using chat_client_on_msg_f = std::function msg)>; 12 | 13 | class chat_client_peer; 14 | 15 | class chat_client final 16 | { 17 | public: 18 | chat_client(boost::asio::io_context& ioCtx, std::string_view name); 19 | ~chat_client(); 20 | 21 | void 22 | connect_async( 23 | std::string_view endpoint, 24 | chat_client_on_connect_f&& cb); 25 | 26 | void 27 | recv_async( 28 | chat_client_on_msg_f&& c); 29 | 30 | void 31 | feed_async( 32 | std::string_view text); 33 | 34 | private: 35 | const std::shared_ptr m_conn; 36 | }; 37 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/5_sigaction_info.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void 7 | on_new_signal(int signum, siginfo_t *info, void *context) 8 | { 9 | printf("Segfault: signum = %d, si_signo = %d, si_addr = %p, si_code = \n", 10 | signum, info->si_signo, info->si_addr); 11 | switch (info->si_code) { 12 | case SEGV_MAPERR: 13 | printf("not mapped\n"); 14 | break; 15 | case SEGV_ACCERR: 16 | printf("no permission\n"); 17 | break; 18 | default: 19 | printf("%d\n", info->si_code); 20 | break; 21 | } 22 | exit(-1); 23 | } 24 | 25 | int 26 | main(void) 27 | { 28 | struct sigaction act; 29 | act.sa_sigaction = on_new_signal; 30 | sigemptyset(&act.sa_mask); 31 | act.sa_flags = SA_SIGINFO; 32 | sigaction(SIGSEGV, &act, NULL); 33 | char *ptr = (char *) 10; 34 | *ptr = 100; 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/7_spin_lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct complex_thing { 7 | bool lock; 8 | char string[128]; 9 | }; 10 | 11 | volatile struct complex_thing thing; 12 | 13 | volatile bool start = false; 14 | 15 | void * 16 | thread_f(void *arg) 17 | { 18 | int id = (int) arg; 19 | while (! start) {}; 20 | if (__sync_bool_compare_and_swap(&thing.lock, 0, 1)) { 21 | for (int i = 0; i < sizeof(thing.string) - 1; ++i) 22 | thing.string[i] = 'a' + id; 23 | __sync_bool_compare_and_swap(&thing.lock, 1, 0); 24 | } 25 | return NULL; 26 | } 27 | 28 | int 29 | main() 30 | { 31 | pthread_t tid[6]; 32 | for (int i = 0; i < 6; ++i) 33 | pthread_create(&tid[i], NULL, thread_f, (void *) i); 34 | start = true; 35 | for (int i = 0; i < 6; ++i) 36 | pthread_join(tid[i], NULL); 37 | printf("%s\n", thing.string); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/10_5_pipe_close.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main(int argc, const char **argv) 9 | { 10 | int channel[2]; 11 | int need_close = argc > 1 && strcmp(argv[1], "close") == 0; 12 | pipe(channel); 13 | if (fork() == 0) { 14 | char buf[16]; 15 | printf("child: started\n"); 16 | if (need_close) { 17 | printf("child: close output channel\n"); 18 | close(channel[1]); 19 | } 20 | while (read(channel[0], buf, sizeof(buf)) > 0) 21 | printf("child: read %s\n", buf); 22 | printf("child: EOF\n"); 23 | return 0; 24 | } 25 | write(channel[1], "100", 3); 26 | printf("parent: written 100\n"); 27 | if (need_close) { 28 | printf("parent: close output channel\n"); 29 | close(channel[1]); 30 | } 31 | printf("parent: waiting for child termination ...\n"); 32 | wait(NULL); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/clone.h: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | 5 | static inline int 6 | thread_create_clone(int (*func)(void *), void *arg, void **stack) 7 | { 8 | int stack_size = 65 * 1024; 9 | *stack = malloc(stack_size); 10 | void *stack_top = (char *) *stack + stack_size; 11 | int flags = CLONE_VM | CLONE_FS | CLONE_FILES | 12 | CLONE_SIGHAND | CLONE_THREAD; 13 | return clone(func, stack_top, flags, arg); 14 | } 15 | 16 | static inline int 17 | thread_create_clone_tid(int (*func)(void *), void *arg, void **stack, 18 | pid_t *tid) 19 | { 20 | int stack_size = 65 * 1024; 21 | *stack = malloc(stack_size); 22 | void *stack_top = (char *) *stack + stack_size; 23 | int flags = CLONE_VM | CLONE_FS | CLONE_FILES | 24 | CLONE_SIGHAND | CLONE_THREAD | CLONE_CHILD_CLEARTID | 25 | CLONE_CHILD_SETTID; 26 | return clone(func, stack_top, flags, arg, NULL, NULL, tid); 27 | } 28 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/2_proc_memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int uninitialized; 5 | 6 | const char *str = "const char *str"; 7 | const char str2[] = "const char str2[]"; 8 | 9 | void 10 | another_function(void) 11 | { 12 | char array[128]; 13 | memset(array, 0, sizeof(array)); 14 | printf("called another function, stack is %p\n", array); 15 | } 16 | 17 | void 18 | test_stack(void) 19 | { 20 | int a; 21 | printf("stack top in test_stack: %p\n", &a); 22 | const char *str3 = "const char *str3"; 23 | const char str4[] = "const char str4[]"; 24 | char str5[] = "char str5[]"; 25 | char b = 'x'; 26 | char c = 'x'; 27 | char d = 'x'; 28 | int e = 32; 29 | int f = 64; 30 | int g = 128; 31 | printf("a = %d\n", a); 32 | a = 10; 33 | } 34 | 35 | int 36 | main(void) 37 | { 38 | int a = 20; 39 | printf("stack top in main: %p\n", &a); 40 | test_stack(); 41 | test_stack(); 42 | another_function(); 43 | test_stack(); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /lecture_examples/5_files/2_fstat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | struct stat st; 7 | stat(argv[1], &st); 8 | printf("inode = %d, protection = %d, links = "\ 9 | "%d, uid = %u, size = %d, blocks = "\ 10 | "%d\n", (int)st.st_ino, (int)st.st_mode, 11 | (int)st.st_nlink, (unsigned)st.st_uid, 12 | (int)st.st_size, (int)st.st_blocks); 13 | if ((st.st_mode & S_IFDIR) == S_IFDIR) 14 | printf("the file is directory\n"); 15 | if ((st.st_mode & S_IFREG) == S_IFREG) 16 | printf("the file is regular\n"); 17 | if ((st.st_mode & S_IFLNK) == S_IFLNK) 18 | printf("the file is symbolic link\n"); 19 | 20 | if ((st.st_mode & S_IRUSR) == S_IRUSR) 21 | printf("can read it\n"); 22 | if ((st.st_mode & S_IWUSR) == S_IWUSR) 23 | printf("can write it\n"); 24 | if ((st.st_mode & S_IXUSR) == S_IXUSR) 25 | printf("can execute it\n"); 26 | 27 | printf("my uid: %d\n", (int)getuid()); 28 | return 0; 29 | } -------------------------------------------------------------------------------- /utils/unit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define unit_test_start() \ 8 | printf("\t-------- %s started --------\n", __func__) 9 | 10 | #define unit_test_finish() \ 11 | printf("\t-------- %s done --------\n", __func__) 12 | 13 | #define unit_fail_if(cond) do { \ 14 | if (cond) { \ 15 | printf("Test failed, line %d\n", __LINE__); \ 16 | exit(-1); \ 17 | } \ 18 | } while (0) 19 | 20 | #define unit_assert(cond) unit_fail_if(!(cond)) 21 | 22 | #define unit_msg(...) do { \ 23 | printf("# "); \ 24 | printf(__VA_ARGS__); \ 25 | printf("\n"); \ 26 | } while (0) 27 | 28 | #define unit_check(cond, msg) do { \ 29 | if (! (cond)) { \ 30 | printf("not ok - %s\n", (msg)); \ 31 | unit_fail_if(true); \ 32 | } else { \ 33 | printf("ok - %s\n", (msg)); \ 34 | } \ 35 | } while(0) 36 | 37 | bool doCmdMaxPoints(int argc, char **argv); 38 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/16_pthread_key.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | volatile int ready_to_print = 0; 7 | volatile bool print = false; 8 | 9 | pthread_key_t key; 10 | 11 | void * 12 | thread_f(void *arg) 13 | { 14 | pthread_setspecific(key, arg); 15 | __sync_fetch_and_add(&ready_to_print, 1); 16 | while (! print) {} 17 | int tmp = (int) pthread_getspecific(key); 18 | printf("value = %d\n", tmp); 19 | return NULL; 20 | } 21 | 22 | int 23 | main() 24 | { 25 | pthread_t t1, t2; 26 | pthread_key_create(&key, NULL); 27 | pthread_setspecific(key, (const void *) 0); 28 | 29 | pthread_create(&t1, NULL, thread_f, (void *) 1); 30 | pthread_create(&t2, NULL, thread_f, (void *) 2); 31 | while (__sync_fetch_and_add(&ready_to_print, 0) != 2) {} 32 | print = true; 33 | pthread_join(t1, NULL); 34 | pthread_join(t2, NULL); 35 | int tmp = (int) pthread_getspecific(key); 36 | printf("main value = %d\n", tmp); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/1_clone.c: -------------------------------------------------------------------------------- 1 | #include "clone.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | pid_t 8 | gettid() 9 | { 10 | return syscall(SYS_gettid); 11 | } 12 | 13 | volatile bool is_finished = false; 14 | 15 | int 16 | thread_f(void *arg) 17 | { 18 | pid_t thread_id = gettid(); 19 | pid_t pid = getpid(); 20 | printf("pid %d, tid %d: new thread, arg = %d\n", 21 | (int)pid, (int)thread_id, *((int *) arg)); 22 | is_finished = true; 23 | return 0; 24 | } 25 | 26 | int 27 | main() 28 | { 29 | pid_t thread_id = gettid(); 30 | pid_t pid = getpid(); 31 | printf("pid %d, tid %d: main thread\n", 32 | (int)pid, (int)thread_id); 33 | void *stack; 34 | int arg = 100; 35 | int ret = thread_create_clone(thread_f, (void *) &arg, &stack); 36 | printf("pid %d, tid %d: clone result = %d\n", 37 | (int)pid, (int)thread_id, ret); 38 | while (! is_finished) 39 | sched_yield(); 40 | free(stack); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/12_sem_posix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define WORKER_COUNT 10 10 | 11 | sem_t *sem; 12 | 13 | void * 14 | worker_f(void *arg) 15 | { 16 | int id = (int) arg; 17 | while (1) { 18 | sem_wait(sem); 19 | printf("Thread %d got the sem\n", id); 20 | sleep(3); 21 | sem_post(sem); 22 | sleep(1); 23 | } 24 | } 25 | 26 | int 27 | main() 28 | { 29 | retry:; 30 | pthread_t workers[WORKER_COUNT]; 31 | sem = sem_open("/my_sem", O_CREAT | O_EXCL, O_RDWR, 5); 32 | if (sem == SEM_FAILED) { 33 | if (errno == EEXIST) { 34 | sem_unlink("/my_sem"); 35 | goto retry; 36 | } 37 | printf("error = %s\n", strerror(errno)); 38 | return -1; 39 | } 40 | for (int i = 0; i < WORKER_COUNT; ++i) 41 | pthread_create(&workers[i], NULL, worker_f, (void *) i); 42 | getchar(); 43 | sem_close(sem); 44 | sem_unlink("/my_sem"); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /lecture_examples/8_net/1_socket_protocol.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void 9 | try_protocol(int type, int protocol, const char *protocol_name) 10 | { 11 | int sock = socket(AF_INET, type, protocol); 12 | if (sock == -1) { 13 | printf("%s: error = %s\n", protocol_name, strerror(errno)); 14 | } else { 15 | printf("%s: success\n", protocol_name); 16 | close(sock); 17 | } 18 | } 19 | 20 | void 21 | try_type(int type, const char *type_name) 22 | { 23 | printf("\nTry %s type\n", type_name); 24 | try_protocol(type, IPPROTO_TCP, "TCP"); 25 | try_protocol(type, IPPROTO_IP, "IP"); 26 | try_protocol(type, IPPROTO_SCTP, "SCTP"); 27 | try_protocol(type, IPPROTO_RAW, "RAW"); 28 | try_protocol(type, IPPROTO_UDP, "UDP"); 29 | } 30 | 31 | int 32 | main() 33 | { 34 | try_type(SOCK_DGRAM, "DGRAM"); 35 | try_type(SOCK_RAW, "RAW"); 36 | try_type(SOCK_STREAM, "STREAM"); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/1_createprocess_win.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) 9 | { 10 | int my_pid = GetCurrentProcessId(); 11 | if (argc != 1) { 12 | printf("I am child %d\n", my_pid); 13 | return 100; 14 | } 15 | STARTUPINFO si; 16 | PROCESS_INFORMATION pi; 17 | memset(&si, 0, sizeof(si)); 18 | si.cb = sizeof(si); 19 | memset(&pi, 0, sizeof(pi)); 20 | char cmd[128]; 21 | sprintf(cmd, "%s child", argv[0]); 22 | 23 | if (CreateProcess(NULL, cmd, NULL, NULL, false, 0, NULL, NULL, 24 | &si, &pi) == 0) { 25 | printf("error = %d\n", (int) GetLastError()); 26 | exit(-1); 27 | } 28 | printf("I am parent %d, child's pid is %d\n", my_pid, 29 | (int) pi.dwProcessId); 30 | WaitForSingleObject(pi.hProcess, INFINITE); 31 | DWORD exit_code; 32 | GetExitCodeProcess(pi.hProcess, &exit_code); 33 | printf("Child exit code %d\n", (int) exit_code); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/7_sigaction_jmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int finished = 0; 10 | static jmp_buf buf; 11 | 12 | static int 13 | is_usr1_blocked(void) 14 | { 15 | sigset_t old; 16 | sigprocmask(0, NULL, &old); 17 | return sigismember(&old, SIGUSR1); 18 | } 19 | 20 | static void 21 | on_new_signal(int signum) 22 | { 23 | printf("Process signal, usr1 block = %d\n", is_usr1_blocked()); 24 | ++finished; 25 | longjmp(buf, 1); 26 | } 27 | 28 | int 29 | main(void) 30 | { 31 | struct sigaction act; 32 | act.sa_handler = on_new_signal; 33 | sigemptyset(&act.sa_mask); 34 | act.sa_flags = 0; 35 | sigaction(SIGUSR1, &act, NULL); 36 | printf("Before raise, usr1 block = %d\n", is_usr1_blocked()); 37 | if (setjmp(buf) == 0) 38 | raise(SIGUSR1); 39 | while (finished != 1) 40 | sched_yield(); 41 | printf("After raise, usr1 block = %d\n", is_usr1_blocked()); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /lecture_examples/10_users/5_seteuid.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | uid_t uid, euid, suid; 9 | 10 | void 11 | show_uids() 12 | { 13 | getresuid(&uid, &euid, &suid); 14 | printf("uid = %d, euid = %d, suid = %d\n", (int) uid, (int) euid, 15 | (int) suid); 16 | } 17 | 18 | int 19 | main(int argc, const char **argv) 20 | { 21 | uid_t new_uid1 = atoi(argv[1]); 22 | uid_t new_uid2 = atoi(argv[2]); 23 | uid_t new_uid3 = atoi(argv[3]); 24 | show_uids(); 25 | printf("Set EUID to %d\n", (int) new_uid1); 26 | if (seteuid(new_uid1) == -1) 27 | printf("error = %s\n", strerror(errno)); 28 | 29 | show_uids(); 30 | printf("Set EUID to %d\n", (int) new_uid2); 31 | if (seteuid(new_uid2) == -1) 32 | printf("error = %s\n", strerror(errno)); 33 | 34 | show_uids(); 35 | printf("Set UID to %d\n", (int) new_uid3); 36 | if (setuid(new_uid3) == -1) 37 | printf("error = %s\n", strerror(errno)); 38 | 39 | show_uids(); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/8_sigaction_sigjmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int finished = 0; 10 | static sigjmp_buf buf; 11 | 12 | static int 13 | is_usr1_blocked(void) 14 | { 15 | sigset_t old; 16 | sigprocmask(0, NULL, &old); 17 | return sigismember(&old, SIGUSR1); 18 | } 19 | 20 | static void 21 | on_new_signal(int signum) 22 | { 23 | printf("Process signal, usr1 block = %d\n", is_usr1_blocked()); 24 | ++finished; 25 | siglongjmp(buf, 1); 26 | } 27 | 28 | int 29 | main(void) 30 | { 31 | struct sigaction act; 32 | act.sa_handler = on_new_signal; 33 | sigemptyset(&act.sa_mask); 34 | act.sa_flags = 0; 35 | sigaction(SIGUSR1, &act, NULL); 36 | printf("Before raise, usr1 block = %d\n", is_usr1_blocked()); 37 | if (sigsetjmp(buf, 1) == 0) 38 | raise(SIGUSR1); 39 | while (finished != 1) 40 | sched_yield(); 41 | printf("After raise, usr1 block = %d\n", is_usr1_blocked()); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/9_sigsuspend.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static int finished = 0; 8 | 9 | static int 10 | is_int_blocked(void) 11 | { 12 | sigset_t old; 13 | sigprocmask(0, NULL, &old); 14 | return sigismember(&old, SIGINT); 15 | } 16 | 17 | static void 18 | on_new_signal(int signum) 19 | { 20 | printf("Process signal, int block = %d\n", is_int_blocked()); 21 | ++finished; 22 | } 23 | 24 | int 25 | main(void) 26 | { 27 | printf("Before main, int block = %d\n", is_int_blocked()); 28 | sigset_t block, old; 29 | sigemptyset(&block); 30 | sigaddset(&block, SIGINT); 31 | sigprocmask(SIG_BLOCK, &block, &old); 32 | 33 | struct sigaction act; 34 | act.sa_handler = on_new_signal; 35 | sigemptyset(&act.sa_mask); 36 | act.sa_flags = 0; 37 | sigaction(SIGINT, &act, NULL); 38 | sigemptyset(&block); 39 | while (finished != 1) 40 | sigsuspend(&block); 41 | sigprocmask(SIG_SETMASK, &old, NULL); 42 | printf("After main, int block = %d\n", is_int_blocked()); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /lecture_examples/2_processes/1_fork.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() 8 | { 9 | printf("I am process %d\n", (int) getpid()); 10 | char *mem = (char *) calloc(1, 100); 11 | pid_t child_pid = fork(); 12 | pid_t my_pid = getpid(); 13 | if (child_pid == 0) { 14 | printf("%d: I am child, fork returned %d\n", 15 | (int) my_pid, (int) child_pid); 16 | printf("%d: child is terminated with code 100\n", 17 | (int) my_pid); 18 | printf("%d: memory values are set to 1\n", (int) my_pid); 19 | memset(mem, 1, 100); 20 | return 100; 21 | } 22 | printf("%d: I am parent, fork returned %d\n", 23 | (int) my_pid, (int) child_pid); 24 | int stat; 25 | pid_t wait_result = wait(&stat); 26 | printf("%d: wait returned %d and stat %d\n", (int) my_pid, 27 | (int) wait_result, stat); 28 | printf("%d: memory values are %d\n", (int) my_pid, (int) mem[0]); 29 | printf("%d: returned child code was %d\n", (int) my_pid, 30 | WEXITSTATUS(stat)); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(Shell CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(COMMON_FLAGS 7 | -Wextra 8 | -Werror 9 | -Wall 10 | -Wno-gnu-folding-constant 11 | -g 12 | ) 13 | add_compile_options(${COMMON_FLAGS}) 14 | 15 | option(ENABLE_LEAK_CHECKS 16 | "Enable memory leak checks with heap_help" 17 | OFF) 18 | 19 | option(ENABLE_GLOB_SEARCH 20 | "Enable compilation of all the files, not just the preselected ones" 21 | OFF) 22 | 23 | set(UTILS_DIR ${CMAKE_SOURCE_DIR}/../utils) 24 | set(UTILS_SOURCES) 25 | 26 | if(ENABLE_LEAK_CHECKS) 27 | list(APPEND UTILS_SOURCES ${UTILS_DIR}/heap_help/heap_help.cpp) 28 | include_directories(${UTILS_DIR}/heap_help) 29 | endif() 30 | 31 | if(NOT ENABLE_GLOB_SEARCH) 32 | set(TEST_SOURCES 33 | solution.cpp 34 | parser.cpp 35 | ${UTILS_SOURCES} 36 | ) 37 | add_executable(mybash ${TEST_SOURCES}) 38 | else() 39 | file(GLOB TEST_SOURCES *.cpp) 40 | list(APPEND TEST_SOURCES ${UTILS_SOURCES}) 41 | add_executable(test ${TEST_SOURCES}) 42 | endif() 43 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/12_libcoro.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBCORO_INCLUDED 2 | #define LIBCORO_INCLUDED 3 | 4 | #include 5 | 6 | struct coro; 7 | typedef int (*coro_f)(void *); 8 | 9 | /** Make current context scheduler. */ 10 | void 11 | coro_sched_init(void); 12 | 13 | /** 14 | * Block until any coroutine has finished. It is returned. NULl, 15 | * if no coroutines. 16 | */ 17 | struct coro * 18 | coro_sched_wait(void); 19 | 20 | /** Currently working coroutine. */ 21 | struct coro * 22 | coro_this(void); 23 | 24 | /** 25 | * Create a new coroutine. It is not started, just added to the 26 | * scheduler. 27 | */ 28 | struct coro * 29 | coro_new(coro_f func, void *func_arg); 30 | 31 | /** Return status of the coroutine. */ 32 | int 33 | coro_status(const struct coro *c); 34 | 35 | /** Check if the coroutine has finished. */ 36 | bool 37 | coro_is_finished(const struct coro *c); 38 | 39 | /** Free coroutine stack and it itself. */ 40 | void 41 | coro_delete(struct coro *c); 42 | 43 | /** Switch to another not finished coroutine. */ 44 | void 45 | coro_yield(void); 46 | 47 | #endif /* LIBCORO_INCLUDED */ 48 | -------------------------------------------------------------------------------- /lecture_examples/8_net/2_getaddrinfo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int 8 | main() 9 | { 10 | struct addrinfo *addr, *iter; 11 | int rc = getaddrinfo("yandex.ru", NULL, NULL, &addr); 12 | if (rc != 0) { 13 | printf("Error = %s\n", gai_strerror(rc)); 14 | return -1; 15 | } 16 | printf("Families: inet = %d, inet6 = %d\n", AF_INET, AF_INET6); 17 | printf("Socket types: dgram = %d, stream = %d, raw = %d\n", SOCK_DGRAM, 18 | SOCK_STREAM, SOCK_RAW); 19 | printf("Protocols: tcp = %d, udp = %d\n\n", IPPROTO_TCP, IPPROTO_UDP); 20 | for (iter = addr; iter != NULL; iter = iter->ai_next) { 21 | printf("family = %d, socktype = %d, protocol = %d", 22 | iter->ai_family, iter->ai_socktype, iter->ai_protocol); 23 | if (iter->ai_family == AF_INET) { 24 | char buf[128]; 25 | struct sockaddr_in *tmp = 26 | (struct sockaddr_in *)iter->ai_addr; 27 | inet_ntop(AF_INET, &tmp->sin_addr, buf, sizeof(buf)); 28 | printf(", ip = %s", buf); 29 | } 30 | printf("\n"); 31 | } 32 | freeaddrinfo(addr); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/2_flock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | main() 10 | { 11 | int fd = open("tmp.txt", O_CREAT | O_RDWR, S_IRWXU); 12 | if (fd == -1) { 13 | printf("error = %s\n", strerror(errno)); 14 | return -1; 15 | } 16 | char cmd[100]; 17 | while (scanf("%s", cmd) > 0) { 18 | int rc; 19 | if (strcmp(cmd, "excl") == 0) { 20 | rc = flock(fd, LOCK_EX); 21 | } else if (strcmp(cmd, "exclnb") == 0) { 22 | rc = flock(fd, LOCK_EX | LOCK_NB); 23 | } else if (strcmp(cmd, "shared") == 0) { 24 | rc = flock(fd, LOCK_SH); 25 | } else if (strcmp(cmd, "sharednb") == 0) { 26 | rc = flock(fd, LOCK_SH | LOCK_NB); 27 | } else if (strcmp(cmd, "unlock") == 0) { 28 | rc = flock(fd, LOCK_UN); 29 | } else if (strcmp(cmd, "write") == 0) { 30 | rc = write(fd, "data", 4); 31 | } else { 32 | printf("unknown command\n"); 33 | continue; 34 | } 35 | if (rc == -1) 36 | printf("error = %s\n", strerror(errno)); 37 | else 38 | printf("ok\n"); 39 | } 40 | close(fd); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/13_socketpair.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define MAX_MSG_SIZE 1024 * 1024 9 | 10 | void 11 | worker(int sock) 12 | { 13 | char buffer[MAX_MSG_SIZE]; 14 | ssize_t size; 15 | while ((size = read(sock, buffer, sizeof(buffer))) > 0) 16 | printf("Client received %d\n", (int) size); 17 | close(sock); 18 | } 19 | 20 | int 21 | main(int argc, const char **argv) 22 | { 23 | if (argc < 2) { 24 | printf("No socket type\n"); 25 | return -1; 26 | } 27 | int socket_type; 28 | if (strcmp(argv[1], "stream")) 29 | socket_type = SOCK_STREAM; 30 | else 31 | socket_type = SOCK_DGRAM; 32 | int sockets[2]; 33 | socketpair(AF_UNIX, socket_type, 0, sockets); 34 | if (fork() == 0) { 35 | close(sockets[0]); 36 | worker(sockets[1]); 37 | return 0; 38 | } 39 | close(sockets[1]); 40 | char buffer[MAX_MSG_SIZE]; 41 | int size; 42 | while (scanf("%d", &size) > 0) { 43 | printf("Server sent %d\n", size); 44 | write(sockets[0], buffer, size); 45 | } 46 | close(sockets[0]); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(UserFS CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(COMMON_FLAGS 7 | -Wextra 8 | -Werror 9 | -Wall 10 | -Wno-gnu-folding-constant 11 | -g 12 | ) 13 | add_compile_options(${COMMON_FLAGS}) 14 | 15 | option(ENABLE_LEAK_CHECKS 16 | "Enable memory leak checks with heap_help" 17 | OFF) 18 | 19 | option(ENABLE_GLOB_SEARCH 20 | "Enable compilation of all the files, not just the preselected ones" 21 | OFF) 22 | 23 | set(UTILS_DIR ${CMAKE_SOURCE_DIR}/../utils) 24 | set(UTILS_SOURCES ${UTILS_DIR}/unit.cpp) 25 | 26 | include_directories(${UTILS_DIR}) 27 | 28 | if(ENABLE_LEAK_CHECKS) 29 | list(APPEND UTILS_SOURCES ${UTILS_DIR}/heap_help/heap_help.cpp) 30 | include_directories(${UTILS_DIR}/heap_help) 31 | endif() 32 | 33 | if(NOT ENABLE_GLOB_SEARCH) 34 | set(TEST_SOURCES 35 | userfs.cpp 36 | test.cpp 37 | ${UTILS_SOURCES} 38 | ) 39 | add_executable(test ${TEST_SOURCES}) 40 | else() 41 | file(GLOB TEST_SOURCES *.cpp) 42 | list(APPEND TEST_SOURCES ${UTILS_SOURCES}) 43 | add_executable(test ${TEST_SOURCES}) 44 | endif() 45 | -------------------------------------------------------------------------------- /1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(CoroBus CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(COMMON_FLAGS 7 | -Wextra 8 | -Werror 9 | -Wall 10 | -Wno-gnu-folding-constant 11 | -g 12 | ) 13 | add_compile_options(${COMMON_FLAGS}) 14 | 15 | option(ENABLE_LEAK_CHECKS 16 | "Enable memory leak checks with heap_help" 17 | OFF) 18 | 19 | option(ENABLE_GLOB_SEARCH 20 | "Enable compilation of all the files, not just the preselected ones" 21 | OFF) 22 | 23 | set(UTILS_DIR ${CMAKE_SOURCE_DIR}/../utils) 24 | set(UTILS_SOURCES ${UTILS_DIR}/unit.cpp) 25 | 26 | include_directories(${UTILS_DIR}) 27 | 28 | if(ENABLE_LEAK_CHECKS) 29 | list(APPEND UTILS_SOURCES ${UTILS_DIR}/heap_help/heap_help.cpp) 30 | include_directories(${UTILS_DIR}/heap_help) 31 | endif() 32 | 33 | if(NOT ENABLE_GLOB_SEARCH) 34 | set(TEST_SOURCES 35 | libcoro.cpp 36 | corobus.cpp 37 | test.cpp 38 | ${UTILS_SOURCES} 39 | ) 40 | add_executable(test ${TEST_SOURCES}) 41 | else() 42 | file(GLOB TEST_SOURCES *.cpp) 43 | list(APPEND TEST_SOURCES ${UTILS_SOURCES}) 44 | add_executable(test ${TEST_SOURCES}) 45 | endif() 46 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/6_msgget.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, const char **argv) 11 | { 12 | key_t key = ftok(argv[0], 0); 13 | int queue_id = msgget(key, IPC_CREAT | S_IRWXU | S_IRWXO); 14 | printf("key = %d, queue_id = %d\n", (int) key, queue_id); 15 | 16 | struct msqid_ds queue_stat; 17 | msgctl(queue_id, IPC_STAT, &queue_stat); 18 | printf("message count = %d\n", (int) queue_stat.msg_qnum); 19 | printf("max queue bytes = %d\n", (int) queue_stat.msg_qbytes); 20 | 21 | int rc = msgget(key, IPC_CREAT | IPC_EXCL); 22 | if (rc == -1) 23 | printf("second msgget returned '%s'\n", strerror(errno)); 24 | 25 | msgctl(queue_id, IPC_RMID, NULL); 26 | 27 | #ifdef IPC_INFO 28 | printf("\nSystem wide settings:\n"); 29 | struct msginfo info; 30 | msgctl(0, IPC_INFO, (struct msqid_ds *) &info); 31 | 32 | printf("max message size = %d\n", info.msgmax); 33 | printf("max queue bytes = %d\n", info.msgmnb); 34 | printf("max number of message queues = %d\n", info.msgmni); 35 | #endif 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /utils/unitpp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class UnitTestCaseGuard 6 | { 7 | public: 8 | UnitTestCaseGuard( 9 | std::string_view name) 10 | : m_name(name) 11 | { 12 | std::cout << "\t-------- " << name << " started --------\n"; 13 | } 14 | 15 | ~UnitTestCaseGuard() 16 | { 17 | std::cout << "\t-------- " << m_name << " done --------\n"; 18 | } 19 | 20 | private: 21 | const std::string m_name; 22 | }; 23 | 24 | #define unit_test_start() UnitTestCaseGuard test_case_guard(__func__) 25 | 26 | #define unit_assert(cond) do { \ 27 | if (cond) { \ 28 | std::cout <<"Test failed, line " << __LINE__ << "\n"; \ 29 | exit(-1); \ 30 | } \ 31 | } while (0) 32 | 33 | #define unit_msg(...) do { \ 34 | std::cout << "# " << __VA_ARGS__ << '\n'; \ 35 | } while (0) 36 | 37 | #define unit_check(cond, msg) do { \ 38 | if (not (cond)) { \ 39 | std::cout << "not ok - " << msg << '\n'; \ 40 | unit_assert(false); \ 41 | } else { \ 42 | std::cout << "ok - " << msg << '\n'; \ 43 | } \ 44 | } while(0) 45 | -------------------------------------------------------------------------------- /5/chat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Here you should specify which features do you want to implement via macros: 5 | * If you want to enable author name support, do: 6 | * 7 | * #define NEED_AUTHOR 1 8 | * 9 | * To enable server-feed from admin do: 10 | * 11 | * #define NEED_SERVER_FEED 1 12 | * 13 | * It is important to define these macros here, in the header, because it is 14 | * used by tests. 15 | */ 16 | #define NEED_AUTHOR 0 17 | #define NEED_SERVER_FEED 0 18 | 19 | #include 20 | 21 | enum chat_errcode { 22 | CHAT_ERR_INVALID_ARGUMENT = 1, 23 | CHAT_ERR_TIMEOUT, 24 | CHAT_ERR_PORT_BUSY, 25 | CHAT_ERR_NO_ADDR, 26 | CHAT_ERR_ALREADY_STARTED, 27 | CHAT_ERR_NOT_IMPLEMENTED, 28 | CHAT_ERR_NOT_STARTED, 29 | CHAT_ERR_SYS, 30 | }; 31 | 32 | enum chat_events { 33 | CHAT_EVENT_INPUT = 1, 34 | CHAT_EVENT_OUTPUT = 2, 35 | }; 36 | 37 | struct chat_message { 38 | #if NEED_AUTHOR 39 | /** Author's name. */ 40 | std::string author; 41 | #endif 42 | /** 0-terminate text. */ 43 | std::string data; 44 | 45 | /* PUT HERE OTHER MEMBERS */ 46 | }; 47 | 48 | /** Convert chat_events mask to events suitable for poll(). */ 49 | int 50 | chat_events_to_poll_events(int mask); 51 | -------------------------------------------------------------------------------- /4/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(ThreadPool CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(COMMON_FLAGS 7 | -Wextra 8 | -Werror 9 | -Wall 10 | -Wno-gnu-folding-constant 11 | -g 12 | ) 13 | add_compile_options(${COMMON_FLAGS}) 14 | 15 | option(ENABLE_LEAK_CHECKS 16 | "Enable memory leak checks with heap_help" 17 | OFF) 18 | 19 | option(ENABLE_GLOB_SEARCH 20 | "Enable compilation of all the files, not just the preselected ones" 21 | OFF) 22 | 23 | set(UTILS_DIR ${CMAKE_SOURCE_DIR}/../utils) 24 | set(UTILS_SOURCES ${UTILS_DIR}/unit.cpp) 25 | 26 | include_directories(${UTILS_DIR}) 27 | 28 | if(ENABLE_LEAK_CHECKS) 29 | list(APPEND UTILS_SOURCES ${UTILS_DIR}/heap_help/heap_help.cpp) 30 | include_directories(${UTILS_DIR}/heap_help) 31 | endif() 32 | 33 | if(NOT ENABLE_GLOB_SEARCH) 34 | set(TEST_SOURCES 35 | thread_pool.cpp 36 | test.cpp 37 | ${UTILS_SOURCES} 38 | ) 39 | add_executable(test ${TEST_SOURCES}) 40 | else() 41 | file(GLOB TEST_SOURCES *.cpp) 42 | list(APPEND TEST_SOURCES ${UTILS_SOURCES}) 43 | add_executable(test ${TEST_SOURCES}) 44 | endif() 45 | 46 | target_link_libraries(test pthread) 47 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/6_sigaction_mask.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static int finished = 0; 9 | static int id = 0; 10 | 11 | static int 12 | is_usr1_blocked(void) 13 | { 14 | sigset_t old; 15 | sigprocmask(0, NULL, &old); 16 | return sigismember(&old, SIGUSR1); 17 | } 18 | 19 | static void 20 | on_new_signal(int signum) 21 | { 22 | int my_id = ++id; 23 | printf("Begin processing %d, usr1 block = %d\n", my_id, 24 | is_usr1_blocked()); 25 | if (my_id == 1) 26 | raise(signum); 27 | printf("End processing %d\n", my_id); 28 | ++finished; 29 | } 30 | 31 | int 32 | main(int argc, char **argv) 33 | { 34 | struct sigaction act; 35 | act.sa_handler = on_new_signal; 36 | sigemptyset(&act.sa_mask); 37 | act.sa_flags = 0; 38 | for (int i = 1; i < argc; ++i) { 39 | if (strcmp(argv[1], "nodefer") == 0) 40 | act.sa_flags |= SA_NODEFER; 41 | } 42 | sigaction(SIGUSR1, &act, NULL); 43 | printf("Before raises, usr1 block = %d\n", is_usr1_blocked()); 44 | raise(SIGUSR1); 45 | while (finished != 2) 46 | sched_yield(); 47 | printf("After raises, usr1 block = %d\n", is_usr1_blocked()); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/15_sock_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int 12 | main() 13 | { 14 | int sock = socket(AF_UNIX, SOCK_STREAM, 0); 15 | if (sock == -1) { 16 | printf("error = %s\n", strerror(errno)); 17 | return -1; 18 | } 19 | struct sockaddr_un addr; 20 | addr.sun_family = AF_UNIX; 21 | sprintf(addr.sun_path, "%s", "sock_server"); 22 | 23 | if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 24 | printf("error = %s\n", strerror(errno)); 25 | return -1; 26 | } 27 | int number; 28 | while (scanf("%d", &number) > 0) { 29 | if (send(sock, &number, sizeof(number), 0) == -1) { 30 | printf("error = %s\n", strerror(errno)); 31 | continue; 32 | } 33 | printf("Sent %d\n", number); 34 | number = 0; 35 | int rc = recv(sock, &number, sizeof(number), 0); 36 | if (rc == 0) { 37 | printf("Closed connection\n"); 38 | break; 39 | } 40 | if (rc == -1) 41 | printf("error = %s\n", strerror(errno)); 42 | else 43 | printf("Received %d\n", number); 44 | } 45 | close(sock); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/7_msgsend_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | pack_msg(char *msg, long type, const char *data, int data_size) 10 | { 11 | char *pos = msg; 12 | memcpy(pos, &type, sizeof(type)); 13 | pos += sizeof(type); 14 | memcpy(pos, &data_size, sizeof(data_size)); 15 | pos += sizeof(data_size); 16 | memcpy(pos, data, data_size); 17 | pos += data_size; 18 | return pos - msg; 19 | } 20 | 21 | void 22 | send_msg(int queue_id, long type, const char *data, int data_size) 23 | { 24 | char msg[512]; 25 | int size = pack_msg(msg, type, data, data_size); 26 | msgsnd(queue_id, msg, size, 0); 27 | printf("sent %d bytes\n", size); 28 | } 29 | 30 | int 31 | main(int argc, const char **argv) 32 | { 33 | key_t key = ftok("./server", 0); 34 | int queue_id = msgget(key, 0); 35 | printf("connected to queue %d\n", queue_id); 36 | 37 | const char *text = "hello, world"; 38 | send_msg(queue_id, 100, text, strlen(text) + 1); 39 | 40 | text = "hello, world of type 50"; 41 | send_msg(queue_id, 50, text, strlen(text) + 1); 42 | 43 | text = "hello, world of type 60"; 44 | send_msg(queue_id, 60, text, strlen(text) + 1); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /lecture_examples/10_users/9_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int 14 | main(int argc, const char **argv) 15 | { 16 | int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 17 | if (sock == -1) { 18 | printf("error = %s\n", strerror(errno)); 19 | return -1; 20 | } 21 | struct sockaddr_in addr; 22 | addr.sin_family = AF_INET; 23 | addr.sin_port = htons(12345); 24 | inet_aton("127.0.0.1", &addr.sin_addr); 25 | if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 26 | printf("connect error = %s\n", strerror(errno)); 27 | close(sock); 28 | return -1; 29 | } 30 | int number; 31 | while (scanf("%d", &number) > 0) { 32 | if (write(sock, &number, sizeof(number)) == -1) { 33 | printf("error = %s\n", strerror(errno)); 34 | continue; 35 | } 36 | printf("Sent %d\n", number); 37 | number = 0; 38 | int rc = read(sock, &number, sizeof(number)); 39 | if (rc == 0) { 40 | printf("Closed connection\n"); 41 | break; 42 | } 43 | if (rc == -1) 44 | printf("error = %s\n", strerror(errno)); 45 | else 46 | printf("Received %d\n", number); 47 | } 48 | close(sock); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/4_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int 14 | main(int argc, const char **argv) 15 | { 16 | int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 17 | if (sock == -1) { 18 | printf("error = %s\n", strerror(errno)); 19 | return -1; 20 | } 21 | struct sockaddr_in addr; 22 | addr.sin_family = AF_INET; 23 | addr.sin_port = htons(12345); 24 | inet_aton("127.0.0.1", &addr.sin_addr); 25 | if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 26 | printf("connect error = %s\n", strerror(errno)); 27 | close(sock); 28 | return -1; 29 | } 30 | int number; 31 | while (scanf("%d", &number) > 0) { 32 | if (send(sock, &number, sizeof(number), 0) == -1) { 33 | printf("error = %s\n", strerror(errno)); 34 | continue; 35 | } 36 | printf("Sent %d\n", number); 37 | number = 0; 38 | int rc = recv(sock, &number, sizeof(number), 0); 39 | if (rc == 0) { 40 | printf("Closed connection\n"); 41 | break; 42 | } 43 | if (rc == -1) 44 | printf("error = %s\n", strerror(errno)); 45 | else 46 | printf("Received %d\n", number); 47 | } 48 | close(sock); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/8_futex.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int 11 | futex_wait(int *futex, int val) 12 | { 13 | return syscall(SYS_futex, futex, FUTEX_WAIT, val, 14 | NULL, NULL, 0); 15 | } 16 | 17 | int 18 | futex_wake(int *futex) 19 | { 20 | return syscall(SYS_futex, futex, FUTEX_WAKE, 1, 21 | NULL, NULL, 0); 22 | } 23 | 24 | void 25 | futex_lock(int *futex) 26 | { 27 | while (__sync_val_compare_and_swap(futex, 0, 1) != 0) 28 | futex_wait(futex, 1); 29 | } 30 | 31 | void 32 | futex_unlock(int *futex) 33 | { 34 | __sync_bool_compare_and_swap(futex, 1, 0); 35 | futex_wake(futex); 36 | } 37 | 38 | volatile int counter = 0; 39 | volatile bool start = false; 40 | volatile int futex = 0; 41 | 42 | void * 43 | thread_f(void *futex) 44 | { 45 | while (! start) {}; 46 | 47 | for (int i = 0; i < 100000; ++i) { 48 | futex_lock((int *) futex); 49 | counter = counter + 1; 50 | futex_unlock((int *) futex); 51 | } 52 | return NULL; 53 | } 54 | 55 | int 56 | main() 57 | { 58 | pthread_t tid[6]; 59 | int futex = 0; 60 | for (int i = 0; i < 6; ++i) 61 | pthread_create(&tid[i], NULL, thread_f, (void *) &futex); 62 | start = true; 63 | for (int i = 0; i < 6; ++i) 64 | pthread_join(tid[i], NULL); 65 | printf("%d\n", counter); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(Chat CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(COMMON_FLAGS 7 | -Wextra 8 | -Werror 9 | -Wall 10 | -Wno-gnu-folding-constant 11 | -g 12 | ) 13 | add_compile_options(${COMMON_FLAGS}) 14 | 15 | option(ENABLE_LEAK_CHECKS 16 | "Enable memory leak checks with heap_help" 17 | OFF) 18 | 19 | option(ENABLE_GLOB_SEARCH 20 | "Enable compilation of all the files, not just the preselected ones" 21 | OFF) 22 | 23 | set(UTILS_DIR ${CMAKE_SOURCE_DIR}/../utils) 24 | set(UTILS_SOURCES ${UTILS_DIR}/unit.cpp) 25 | 26 | include_directories(${UTILS_DIR}) 27 | 28 | if(ENABLE_LEAK_CHECKS) 29 | list(APPEND UTILS_SOURCES ${UTILS_DIR}/heap_help/heap_help.cpp) 30 | include_directories(${UTILS_DIR}/heap_help) 31 | endif() 32 | 33 | 34 | if(NOT ENABLE_GLOB_SEARCH) 35 | add_library(chat STATIC 36 | ${UTILS_SOURCES} 37 | chat.cpp 38 | chat_client.cpp 39 | chat_server.cpp 40 | ) 41 | 42 | add_executable(test test.cpp) 43 | target_link_libraries(test chat pthread) 44 | 45 | add_executable(client chat_client_exe.cpp) 46 | target_link_libraries(client chat pthread) 47 | 48 | add_executable(server chat_server_exe.cpp) 49 | target_link_libraries(server chat pthread) 50 | else() 51 | file(GLOB TEST_SOURCES *.cpp) 52 | list(APPEND TEST_SOURCES ${UTILS_SOURCES}) 53 | add_executable(test ${TEST_SOURCES}) 54 | endif() 55 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/10_cthreads_1.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "clone.h" 9 | 10 | typedef int (*cthread_f)(void *); 11 | 12 | struct cthread { 13 | int returned_code; 14 | cthread_f func; 15 | void *arg; 16 | void *stack; 17 | bool is_finished; 18 | }; 19 | 20 | int 21 | cthread_runner(void *arg) 22 | { 23 | struct cthread *thread = (struct cthread *) arg; 24 | thread->returned_code = thread->func(thread->arg); 25 | thread->is_finished = true; 26 | return 0; 27 | } 28 | 29 | void 30 | cthread_create(struct cthread *result, cthread_f func, 31 | void *arg) 32 | { 33 | result->returned_code = 0; 34 | result->func = func; 35 | result->arg = arg; 36 | result->is_finished = false; 37 | thread_create_clone(cthread_runner, (void *) result, 38 | &result->stack); 39 | } 40 | 41 | int 42 | cthread_join(volatile struct cthread *thread) 43 | { 44 | while (! thread->is_finished) 45 | sched_yield(); 46 | free(thread->stack); 47 | return thread->returned_code; 48 | } 49 | 50 | int 51 | func(void *arg) 52 | { 53 | printf("arg = %d\n", *((int *) arg)); 54 | return 200; 55 | } 56 | 57 | int 58 | main() 59 | { 60 | struct cthread thread; 61 | int arg = 100; 62 | cthread_create(&thread, func, (void *) &arg); 63 | int retcode = cthread_join(&thread); 64 | printf("thread is joined with retcode = %d\n", retcode); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/7_msgsend_server_flag.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int 11 | unpack_msg(const char *msg, int size, long *type, const char **data, 12 | int *data_size) 13 | { 14 | const char *pos = msg; 15 | memcpy(type, pos, sizeof(*type)); 16 | pos += sizeof(type); 17 | memcpy(data_size, pos, sizeof(*data_size)); 18 | pos += sizeof(*data_size); 19 | *data = pos; 20 | pos += *data_size; 21 | return pos - msg; 22 | } 23 | 24 | void 25 | recv_msg(int queue_id, long type) 26 | { 27 | char msg[512]; 28 | ssize_t recv; 29 | while(1) { 30 | recv = msgrcv(queue_id, msg, sizeof(msg), type, IPC_NOWAIT); 31 | if (recv != -1) 32 | break; 33 | if (errno != ENOMSG) { 34 | printf("error = %s\n", strerror(errno)); 35 | return; 36 | } 37 | } 38 | printf("received %d\n", (int) recv); 39 | const char *data; 40 | int data_size; 41 | unpack_msg(msg, recv, &type, &data, &data_size); 42 | printf("type = %ld, size = %d, str = %s\n", type, data_size, data); 43 | } 44 | 45 | int main(int argc, const char **argv) 46 | { 47 | key_t key = ftok("./server", 0); 48 | int queue_id = msgget(key, IPC_CREAT | S_IRWXU | S_IRWXO); 49 | printf("connected to queue %d\n", queue_id); 50 | 51 | recv_msg(queue_id, 0); 52 | 53 | recv_msg(queue_id, 60); 54 | 55 | recv_msg(queue_id, -200); 56 | 57 | msgctl(queue_id, IPC_RMID, NULL); 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /2/parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct parser; 11 | 12 | enum parser_error { 13 | PARSER_ERR_NONE, 14 | PARSER_ERR_PIPE_WITH_NO_LEFT_ARG, 15 | PARSER_ERR_PIPE_WITH_LEFT_ARG_NOT_A_COMMAND, 16 | PARSER_ERR_AND_WITH_NO_LEFT_ARG, 17 | PARSER_ERR_AND_WITH_LEFT_ARG_NOT_A_COMMAND, 18 | PARSER_ERR_OR_WITH_NO_LEFT_ARG, 19 | PARSER_ERR_OR_WITH_LEFT_ARG_NOT_A_COMMAND, 20 | PARSER_ERR_OUTOUT_REDIRECT_BAD_ARG, 21 | PARSER_ERR_TOO_LATE_ARGUMENTS, 22 | PARSER_ERR_ENDS_NOT_WITH_A_COMMAND, 23 | }; 24 | 25 | struct command { 26 | std::string exe; 27 | std::vector args; 28 | }; 29 | 30 | enum expr_type { 31 | EXPR_TYPE_COMMAND, 32 | EXPR_TYPE_PIPE, 33 | EXPR_TYPE_AND, 34 | EXPR_TYPE_OR, 35 | }; 36 | 37 | struct expr { 38 | enum expr_type type = EXPR_TYPE_COMMAND; 39 | /** Valid if the type is COMMAND. */ 40 | std::optional cmd; 41 | }; 42 | 43 | enum output_type { 44 | OUTPUT_TYPE_STDOUT, 45 | OUTPUT_TYPE_FILE_NEW, 46 | OUTPUT_TYPE_FILE_APPEND, 47 | }; 48 | 49 | struct command_line { 50 | std::list exprs; 51 | enum output_type out_type = OUTPUT_TYPE_STDOUT; 52 | /** Non-empty if the out type is FILE. */ 53 | std::string out_file; 54 | bool is_background = false; 55 | }; 56 | 57 | struct parser * 58 | parser_new(void); 59 | 60 | void 61 | parser_feed(struct parser *p, const char *str, uint32_t len); 62 | 63 | enum parser_error 64 | parser_pop_next(struct parser *p, struct command_line **out); 65 | 66 | void 67 | parser_delete(struct parser *p); 68 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/12_libcoro_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "12_libcoro.h" 6 | 7 | #define coro_count 3 8 | static struct coro *coros[coro_count]; 9 | 10 | static void 11 | recursive(int deep, int id) 12 | { 13 | printf("%d: recursive step %d\n", id, deep); 14 | coro_yield(); 15 | if (deep < 2) 16 | recursive(deep + 1, id); 17 | printf("%d: finish recursive step %d\n", id, deep); 18 | } 19 | 20 | static int 21 | coro_func(void *ptr) 22 | { 23 | int id = (int) ptr; 24 | printf("%d: coro is started\n", id); 25 | coro_yield(); 26 | recursive(0, id); 27 | return id; 28 | } 29 | 30 | static int 31 | coro_tree_func(void *ptr) 32 | { 33 | int id = (int) ptr; 34 | printf("%d: coro line is started\n", id); 35 | coro_yield(); 36 | if (id > 1) { 37 | printf("%d: coro line end\n", id); 38 | } else { 39 | printf("%d: coro line next\n", id); 40 | coro_new(coro_tree_func, (void *) (id + 1)); 41 | } 42 | coro_yield(); 43 | return id; 44 | } 45 | 46 | int 47 | main(void) 48 | { 49 | printf("Start main\n"); 50 | coro_sched_init(); 51 | for (int i = 0; i < coro_count; ++i) 52 | coros[i] = coro_new(coro_func, (void *) i); 53 | struct coro *c; 54 | while ((c = coro_sched_wait()) != NULL) { 55 | printf("Finished %d\n", coro_status(c)); 56 | coro_delete(c); 57 | } 58 | printf("Finished simple\n"); 59 | 60 | coro_new(coro_tree_func, (void *) 0); 61 | while ((c = coro_sched_wait()) != NULL) { 62 | printf("Finished %d\n", coro_status(c)); 63 | coro_delete(c); 64 | } 65 | printf("Finish main\n"); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/8_6_spinlock_acq_rel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * Swap to 0 to look at performance difference between ACQ+REL vs 9 | * full barrier. 10 | */ 11 | #define USE_ACQUIRE_RELEASE 1 12 | 13 | #if USE_ACQUIRE_RELEASE 14 | 15 | static void 16 | spin_lock(volatile bool *lock) 17 | { 18 | while (__atomic_test_and_set(lock, __ATOMIC_ACQUIRE)) 19 | {} 20 | } 21 | 22 | static void 23 | spin_unlock(volatile bool *lock) 24 | { 25 | __atomic_clear(lock, __ATOMIC_RELEASE); 26 | } 27 | 28 | #else 29 | 30 | static void 31 | spin_lock(volatile bool *lock) 32 | { 33 | while (! __sync_bool_compare_and_swap(lock, 0, 1)) 34 | {} 35 | } 36 | 37 | static void 38 | spin_unlock(volatile bool *lock) 39 | { 40 | __sync_bool_compare_and_swap(lock, 1, 0); 41 | } 42 | 43 | #endif 44 | 45 | static volatile bool start = false; 46 | static uint64_t counter = 0; 47 | static volatile bool lock = false; 48 | 49 | void * 50 | thread_f(void *arg) 51 | { 52 | while (! start) {}; 53 | for (int i = 0; i < 10000000; ++i) { 54 | spin_lock(&lock); 55 | ++counter; 56 | spin_unlock(&lock); 57 | } 58 | return NULL; 59 | } 60 | 61 | int 62 | main() 63 | { 64 | pthread_t tid[6]; 65 | for (int i = 0; i < 6; ++i) 66 | pthread_create(&tid[i], NULL, thread_f, (void *) i); 67 | clock_t ts1 = clock(); 68 | start = true; 69 | for (int i = 0; i < 6; ++i) 70 | pthread_join(tid[i], NULL); 71 | clock_t ts2 = clock(); 72 | printf("counter = %llu, time = %lf\n", counter, 73 | ((double) (ts2 - ts1)) / CLOCKS_PER_SEC); 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/11_shm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int 15 | main() 16 | { 17 | key_t key = ftok("./a.out", 0); 18 | int id = shmget(key, 1024, IPC_CREAT | S_IRWXU | S_IRWXO); 19 | printf("Created mem with id %d\n", id); 20 | char *mem = shmat(id, NULL, 0); 21 | int align = __alignof__(pthread_mutex_t); 22 | pthread_mutex_t *mutex = (pthread_mutex_t *) 23 | (mem + align - (uint64_t)mem % align); 24 | volatile char *data = (char *) mutex + sizeof(*mutex); 25 | *data = 0; 26 | pthread_mutexattr_t attr; 27 | pthread_mutexattr_init(&attr); 28 | pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); 29 | pthread_mutex_init(mutex, &attr); 30 | pthread_mutexattr_destroy(&attr); 31 | printf("Created mutex between processes\n"); 32 | 33 | if (fork() == 0) { 34 | printf("Child holds a lock\n"); 35 | pthread_mutex_lock(mutex); 36 | *data = 1; 37 | printf("And the child dies\n"); 38 | exit(1); 39 | } 40 | printf("Parent waits for 1 in shmem\n"); 41 | wait(NULL); 42 | assert(*data == 1); 43 | printf("Parent tries to lock\n"); 44 | if (pthread_mutex_lock(mutex) == EOWNERDEAD) { 45 | printf("Owner is dead, restore\n"); 46 | pthread_mutex_consistent(mutex); 47 | } 48 | printf("Destroy mutex\n"); 49 | pthread_mutex_unlock(mutex); 50 | pthread_mutex_destroy(mutex); 51 | 52 | printf("Free shmem\n"); 53 | shmdt(mem); 54 | shmctl(id, IPC_RMID, NULL); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/8_5_random_generator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * --------------------------------------------------------------- 5 | * Mersenne Twister 6 | * A thread-safe random number generator with good randomness in a 7 | * small number of instructions. It is used it to introduce random 8 | * timing delays. The built-in generator, random() function, gives 9 | * strange results, it makes reordering happening much less 10 | * frequent. Perhaps, the generator below is just faster. 11 | * --------------------------------------------------------------- 12 | */ 13 | #define MT_IA 397 14 | #define MT_LEN 624 15 | 16 | struct mersenne_twister 17 | { 18 | unsigned int buffer[MT_LEN]; 19 | int index; 20 | }; 21 | 22 | static unsigned int 23 | mersenne_twister_generate(struct mersenne_twister *twister) 24 | { 25 | int i = twister->index; 26 | int i2 = twister->index + 1; 27 | int j = twister->index + MT_IA; 28 | if (i2 >= MT_LEN) 29 | i2 = 0; 30 | if (j >= MT_LEN) 31 | j -= MT_LEN; 32 | 33 | unsigned int s = (twister->buffer[i] & 0x80000000) | 34 | (twister->buffer[i2] & 0x7fffffff); 35 | unsigned int r = twister->buffer[j] ^ (s >> 1) ^ ((s & 1) * 0x9908B0DF); 36 | twister->buffer[twister->index] = r; 37 | twister->index = i2; 38 | 39 | r ^= (r >> 11); 40 | r ^= (r << 7) & 0x9d2c5680UL; 41 | r ^= (r << 15) & 0xefc60000UL; 42 | r ^= (r >> 18); 43 | return r; 44 | } 45 | 46 | static void 47 | mersenne_twister_create(struct mersenne_twister *twister, unsigned int seed) 48 | { 49 | for (int i = 0; i < MT_LEN; i++) 50 | twister->buffer[i] = seed; 51 | twister->index = 0; 52 | for (int i = 0; i < MT_LEN * 100; i++) 53 | mersenne_twister_generate(twister); 54 | } 55 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/7_msgsend_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int 11 | unpack_msg(const char *msg, int size, long *type, const char **data, 12 | int *data_size) 13 | { 14 | const char *pos = msg; 15 | memcpy(type, pos, sizeof(*type)); 16 | pos += sizeof(type); 17 | memcpy(data_size, pos, sizeof(*data_size)); 18 | pos += sizeof(*data_size); 19 | *data = pos; 20 | pos += *data_size; 21 | return pos - msg; 22 | } 23 | 24 | void 25 | recv_msg(int queue_id, long type) 26 | { 27 | char msg[512]; 28 | ssize_t recv = msgrcv(queue_id, msg, sizeof(msg), type, 0); 29 | printf("received %d\n", (int) recv); 30 | const char *data; 31 | int data_size; 32 | unpack_msg(msg, recv, &type, &data, &data_size); 33 | printf("type = %ld, size = %d, str = %s\n", type, data_size, data); 34 | } 35 | 36 | int main(int argc, const char **argv) 37 | { 38 | key_t key = ftok("./server", 0); 39 | int queue_id = msgget(key, IPC_CREAT | S_IRWXU | S_IRWXO); 40 | printf("connected to queue %d\n", queue_id); 41 | 42 | while (1) { 43 | struct msqid_ds queue_stat; 44 | if (msgctl(queue_id, IPC_STAT, &queue_stat) != 0) { 45 | printf("error = %s\n", strerror(errno)); 46 | msgctl(queue_id, IPC_RMID, NULL); 47 | return -1; 48 | } 49 | if (queue_stat.msg_qnum > 0) { 50 | printf("message count = %d\n", 51 | (int) queue_stat.msg_qnum); 52 | break; 53 | } 54 | sched_yield(); 55 | } 56 | 57 | recv_msg(queue_id, 0); 58 | 59 | recv_msg(queue_id, 60); 60 | 61 | recv_msg(queue_id, -200); 62 | 63 | msgctl(queue_id, IPC_RMID, NULL); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /lecture_examples/8_net/3_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int 14 | main(int argc, const char **argv) 15 | { 16 | int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 17 | if (sock == -1) { 18 | printf("error = %s\n", strerror(errno)); 19 | return -1; 20 | } 21 | struct addrinfo *addr; 22 | struct addrinfo filter; 23 | memset(&filter, 0, sizeof(filter)); 24 | filter.ai_family = AF_INET; 25 | filter.ai_socktype = SOCK_STREAM; 26 | int rc = getaddrinfo(argv[1], argv[2], &filter, &addr); 27 | if (rc != 0) { 28 | printf("addrinfo error = %s\n", gai_strerror(rc)); 29 | close(sock); 30 | return -1; 31 | } 32 | if (addr == NULL) { 33 | printf("not found a server\n"); 34 | freeaddrinfo(addr); 35 | close(sock); 36 | return -1; 37 | } 38 | rc = connect(sock, addr->ai_addr, addr->ai_addrlen); 39 | freeaddrinfo(addr); 40 | if (rc != 0) { 41 | printf("connect error = %s\n", strerror(errno)); 42 | close(sock); 43 | return -1; 44 | } 45 | int number; 46 | while (scanf("%d", &number) > 0) { 47 | if (send(sock, &number, sizeof(number), 0) == -1) { 48 | printf("error = %s\n", strerror(errno)); 49 | continue; 50 | } 51 | printf("Sent %d\n", number); 52 | number = 0; 53 | int rc = recv(sock, &number, sizeof(number), 0); 54 | if (rc == 0) { 55 | printf("Closed connection\n"); 56 | break; 57 | } 58 | if (rc == -1) 59 | printf("error = %s\n", strerror(errno)); 60 | else 61 | printf("Received %d\n", number); 62 | } 63 | close(sock); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/1_simple_sort.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct worker { 10 | int *array; 11 | int size; 12 | }; 13 | 14 | int 15 | cmp(const void *a, const void *b) 16 | { 17 | return *(int *)a - *(int *)b; 18 | } 19 | 20 | void 21 | sorter(struct worker *worker, const char *filename) 22 | { 23 | FILE *file = fopen(filename, "r"); 24 | int size = 0; 25 | int capacity = 1024; 26 | int *array = malloc(capacity * sizeof(int)); 27 | while (fscanf(file, "%d", &array[size]) > 0) { 28 | ++size; 29 | if (size == capacity) { 30 | capacity *= 2; 31 | array = realloc(array, capacity * sizeof(int)); 32 | } 33 | } 34 | qsort(array, size, sizeof(int), cmp); 35 | fclose(file); 36 | worker->array = array; 37 | worker->size = size; 38 | } 39 | 40 | int 41 | main(int argc, const char **argv) 42 | { 43 | struct timespec ts; 44 | clock_gettime(CLOCK_MONOTONIC, &ts); 45 | uint64_t start_ns = ts.tv_sec * 1000000000 + ts.tv_nsec; 46 | int nfiles = argc - 1; 47 | struct worker *workers = malloc(sizeof(struct worker) * nfiles); 48 | struct worker *w = workers; 49 | int total_size = 0; 50 | for (int i = 0; i < nfiles; ++i, ++w) { 51 | sorter(w, argv[i + 1]); 52 | total_size += w->size; 53 | } 54 | int *total_array = malloc(total_size * sizeof(int)); 55 | int *pos = total_array; 56 | w = workers; 57 | for (int i = 0; i < nfiles; ++i, ++w) { 58 | memcpy(pos, w->array, w->size * sizeof(int)); 59 | pos += w->size; 60 | } 61 | clock_gettime(CLOCK_MONOTONIC, &ts); 62 | uint64_t end_ns = ts.tv_sec * 1000000000 + ts.tv_nsec; 63 | double sec = (end_ns - start_ns) / 1000000000.0; 64 | printf("presort time = %lfs\n", sec); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/9_sem_event_cons.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int 13 | unpack_msg(const char *msg, int size, long *type, const char **data, 14 | int *data_size) 15 | { 16 | const char *pos = msg; 17 | memcpy(type, pos, sizeof(*type)); 18 | pos += sizeof(type); 19 | memcpy(data_size, pos, sizeof(*data_size)); 20 | pos += sizeof(*data_size); 21 | *data = pos; 22 | pos += *data_size; 23 | return pos - msg; 24 | } 25 | 26 | void 27 | recv_msg(int queueid, long type) 28 | { 29 | char msg[512]; 30 | ssize_t recv = msgrcv(queueid, msg, sizeof(msg), type, 0); 31 | printf("Received %d\n", (int) recv); 32 | const char *data; 33 | int data_size; 34 | unpack_msg(msg, recv, &type, &data, &data_size); 35 | printf("Type = %ld, size = %d, str = %s\n", type, data_size, data); 36 | } 37 | 38 | void 39 | read_data(int queueid) 40 | { 41 | for (int i = 0; i < 3; ++i) 42 | recv_msg(queueid, i + 1); 43 | } 44 | 45 | void 46 | unlock_events(int semid) 47 | { 48 | struct sembuf op; 49 | op.sem_flg = SEM_UNDO; 50 | op.sem_num = 3; 51 | op.sem_op = 1; 52 | semop(semid, &op, 1); 53 | } 54 | 55 | void 56 | lock_events(int semid) 57 | { 58 | struct sembuf op[4]; 59 | for (int i = 0; i < 4; ++i) { 60 | op[i].sem_flg = SEM_UNDO; 61 | op[i].sem_num = i; 62 | op[i].sem_op = -1; 63 | } 64 | semop(semid, op, 4); 65 | } 66 | 67 | int 68 | main() 69 | { 70 | key_t key = ftok("./event_gen", 0); 71 | int semid = semget(key, 4, 0); 72 | int queueid = msgget(key, 0); 73 | 74 | while(1) { 75 | lock_events(semid); 76 | read_data(queueid); 77 | unlock_events(semid); 78 | } 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /1/libcoro.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct coro; 6 | typedef void *(*coro_f)(void *); 7 | 8 | /** Initialize the coroutines engine. */ 9 | void 10 | coro_sched_init(void); 11 | 12 | /** 13 | * Run the coroutines processing while there are any runnable 14 | * ones. 15 | */ 16 | void 17 | coro_sched_run(void); 18 | 19 | /** 20 | * Destroy the coroutines engine. All coros must be finished by 21 | * now. 22 | */ 23 | void 24 | coro_sched_destroy(void); 25 | 26 | /** Get the currently working coroutine. */ 27 | struct coro * 28 | coro_this(void); 29 | 30 | /** 31 | * Create a new coroutine. The function won't yield. The coroutine 32 | * will start execution automatically on the next iteration of the 33 | * scheduler. 34 | * 35 | * Whatever the callback function returns, will be returned from 36 | * coro_join(). 37 | */ 38 | struct coro * 39 | coro_new(coro_f func, void *func_arg); 40 | 41 | /** 42 | * Join a coroutine. When joined, its resources are freed, and the 43 | * result of its callback function is returned. Each coroutine 44 | * must be joined. Otherwise it leaks. 45 | */ 46 | void * 47 | coro_join(struct coro *coro); 48 | 49 | /** 50 | * Pause the current coroutine until its explicitly woken up with 51 | * coro_wakeup(). Can be used to wait for some event, which will 52 | * wakeup this coro when happens. 53 | */ 54 | void 55 | coro_suspend(void); 56 | 57 | /** 58 | * Pause the current coroutine until the next iteration of the 59 | * scheduler. Can be used to let the other coroutines work for a 60 | * bit. 61 | */ 62 | void 63 | coro_yield(void); 64 | 65 | /** 66 | * Wakeup a coroutine. If it was suspended, then it is going to be 67 | * continued on the next iteration of the scheduler. Otherwise 68 | * this function is a nop. 69 | */ 70 | void 71 | coro_wakeup(struct coro *coro); 72 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/10_cthreads_2.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "clone.h" 10 | 11 | typedef int (*cthread_f)(void *); 12 | 13 | struct cthread { 14 | int returned_code; 15 | cthread_f func; 16 | void *arg; 17 | void *stack; 18 | bool is_finished; 19 | jmp_buf jmp; 20 | }; 21 | 22 | int 23 | cthread_runner(void *arg) 24 | { 25 | struct cthread *thread = (struct cthread *) arg; 26 | if (setjmp(thread->jmp) == 0) { 27 | thread->returned_code = 28 | thread->func(thread->arg); 29 | } 30 | thread->is_finished = true; 31 | return 0; 32 | } 33 | 34 | void 35 | cthread_create(struct cthread *result, cthread_f func, 36 | void *arg) 37 | { 38 | result->returned_code = 0; 39 | result->func = func; 40 | result->arg = arg; 41 | result->is_finished = false; 42 | thread_create_clone(cthread_runner, (void *) result, 43 | &result->stack); 44 | } 45 | 46 | void 47 | cthread_exit(struct cthread *thread, int retcode) 48 | { 49 | thread->returned_code = retcode; 50 | longjmp(thread->jmp, 1); 51 | } 52 | 53 | int 54 | cthread_join(volatile struct cthread *thread) 55 | { 56 | while (! thread->is_finished) 57 | sched_yield(); 58 | free(thread->stack); 59 | return thread->returned_code; 60 | } 61 | 62 | int 63 | func(void *arg) 64 | { 65 | printf("func is started\n"); 66 | struct cthread *me = (struct cthread *) arg; 67 | cthread_exit(me, 300); 68 | printf("func returned\n"); 69 | return 0; 70 | } 71 | 72 | int 73 | main() 74 | { 75 | struct cthread thread; 76 | cthread_create(&thread, func, (void *) &thread); 77 | int retcode = cthread_join(&thread); 78 | printf("thread is joined with retcode = %d\n", retcode); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /examples/reuse_port_addr/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int 13 | create_server(bool reuse_addr, bool reuse_port) 14 | { 15 | struct sockaddr_in in; 16 | in.sin_family = AF_INET; 17 | inet_aton("127.0.0.1", &in.sin_addr); 18 | in.sin_port = htons(3333); 19 | int s = socket(AF_INET, SOCK_STREAM, 0); 20 | assert(s >= 0); 21 | int rc, value = 1; 22 | if (reuse_addr) { 23 | printf("reuse addr is set\n"); 24 | rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, 25 | sizeof(int)); 26 | assert(rc == 0); 27 | } 28 | if (reuse_port) { 29 | printf("reuse port is set\n"); 30 | rc = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &value, 31 | sizeof(int)); 32 | assert(rc == 0); 33 | } 34 | if (bind(s, (struct sockaddr *) &in, sizeof(in)) != 0) { 35 | printf("bind error: %s\n", strerror(errno)); 36 | close(s); 37 | return -1; 38 | } 39 | printf("bind success\n"); 40 | if (listen(s, 128) < 0) { 41 | printf("listen error: %s\n", strerror(errno)); 42 | close(s); 43 | return -1; 44 | } 45 | printf("listen success\n"); 46 | return s; 47 | } 48 | 49 | int 50 | main(int argc, char **argv) 51 | { 52 | bool reuse_addr = false, reuse_port = false; 53 | for (int i = 1; i < argc; ++i) { 54 | if (strcmp(argv[i], "reuse_addr") == 0) 55 | reuse_addr = true; 56 | else if (strcmp(argv[i], "reuse_port") == 0) 57 | reuse_port = true; 58 | } 59 | int s = create_server(reuse_addr, reuse_port); 60 | if (s < 0) 61 | return -1; 62 | int c = accept(s, NULL, NULL); 63 | if (c < 0) { 64 | printf("accept error: %s\n", strerror(errno)); 65 | } else { 66 | printf("accept success\n"); 67 | close(c); 68 | } 69 | close(s); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /lecture_examples/4_signals/10_sigaltstack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int finished = 0; 10 | 11 | #define handle_error() ({printf("error = %s\n", strerror(errno)); exit(-1); }) 12 | 13 | static void 14 | on_new_signal(int signum) 15 | { 16 | volatile int local_var; 17 | printf("Process signal, stack = %p\n", &local_var); 18 | ++finished; 19 | } 20 | 21 | static void 22 | stack_create(int size) 23 | { 24 | int page_size = getpagesize(); 25 | size = size + (page_size - size % page_size); 26 | if (size < SIGSTKSZ) 27 | size = SIGSTKSZ; 28 | stack_t s; 29 | s.ss_sp = malloc(size); 30 | printf("Page size = %d, new stack begin = %p, stack end = %p\n", 31 | page_size, s.ss_sp, s.ss_sp + size); 32 | s.ss_flags = 0; 33 | s.ss_size = size; 34 | if (sigaltstack(&s, NULL) != 0) 35 | handle_error(); 36 | } 37 | 38 | static void 39 | stack_destroy(void) 40 | { 41 | stack_t s; 42 | if (sigaltstack(NULL, &s) != 0) 43 | handle_error(); 44 | s.ss_flags = SS_DISABLE; 45 | if (sigaltstack(&s, NULL) != 0) 46 | handle_error(); 47 | printf("Destroy stack %p\n", s.ss_sp); 48 | free(s.ss_sp); 49 | } 50 | 51 | static void 52 | wait_signal(void) 53 | { 54 | int need = finished + 1; 55 | raise(SIGUSR1); 56 | while (finished < need) 57 | sched_yield(); 58 | } 59 | 60 | int 61 | main(int argc, char **argv) 62 | { 63 | struct sigaction act; 64 | printf("Main, stack = %p\n", &act); 65 | act.sa_handler = on_new_signal; 66 | sigemptyset(&act.sa_mask); 67 | 68 | act.sa_flags = SA_ONSTACK; 69 | stack_create(1024 * 64); 70 | if (sigaction(SIGUSR1, &act, NULL) != 0) 71 | handle_error(); 72 | wait_signal(); 73 | stack_destroy(); 74 | 75 | act.sa_flags = 0; 76 | if (sigaction(SIGUSR1, &act, NULL) != 0) 77 | handle_error(); 78 | wait_signal(); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /5/chat_server_exe.cpp: -------------------------------------------------------------------------------- 1 | #include "chat.h" 2 | #include "chat_server.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int 10 | port_from_str(const char *str, uint16_t *port) 11 | { 12 | errno = 0; 13 | char *end = NULL; 14 | long res = strtol(str, &end, 10); 15 | if (res == 0 && errno != 0) 16 | return -1; 17 | if (*end != 0) 18 | return -1; 19 | if (res > UINT16_MAX || res < 0) 20 | return -1; 21 | *port = (uint16_t)res; 22 | return 0; 23 | } 24 | 25 | int 26 | main(int argc, char **argv) 27 | { 28 | if (argc < 2) { 29 | printf("Expected a port to listen on\n"); 30 | return -1; 31 | } 32 | uint16_t port = 0; 33 | int rc = port_from_str(argv[1], &port); 34 | if (rc != 0) { 35 | printf("Invalid port\n"); 36 | return -1; 37 | } 38 | struct chat_server *serv = chat_server_new(); 39 | rc = chat_server_listen(serv, port); 40 | if (rc != 0) { 41 | printf("Couldn't listen: %d\n", rc); 42 | chat_server_delete(serv); 43 | return -1; 44 | } 45 | #if NEED_SERVER_FEED 46 | /* 47 | * If want +5 points, then do similarly to the client_exe - create 2 48 | * pollfd objects, wait on STDIN_FILENO and on 49 | * chat_server_get_descriptor(), process events, etc. 50 | */ 51 | #else 52 | /* 53 | * The basic implementation without server messages. Just serving 54 | * clients. 55 | */ 56 | while (true) { 57 | int rc = chat_server_update(serv, -1); 58 | if (rc != 0) { 59 | printf("Update error: %d\n", rc); 60 | break; 61 | } 62 | /* Flush all the pending messages to the standard output. */ 63 | struct chat_message *msg; 64 | while ((msg = chat_server_pop_next(serv)) != NULL) { 65 | #if NEED_AUTHOR 66 | printf("%s: %s\n", msg->author.c_str(), msg->data.c_str()); 67 | #else 68 | printf("%s\n", msg->data.c_str()); 69 | #endif 70 | delete msg; 71 | } 72 | } 73 | #endif 74 | chat_server_delete(serv); 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /2/solution.cpp: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static void 8 | execute_command_line(const struct command_line *line) 9 | { 10 | /* REPLACE THIS CODE WITH ACTUAL COMMAND EXECUTION */ 11 | 12 | assert(line != NULL); 13 | printf("================================\n"); 14 | printf("Command line:\n"); 15 | printf("Is background: %d\n", (int)line->is_background); 16 | printf("Output: "); 17 | if (line->out_type == OUTPUT_TYPE_STDOUT) { 18 | printf("stdout\n"); 19 | } else if (line->out_type == OUTPUT_TYPE_FILE_NEW) { 20 | printf("new file - \"%s\"\n", line->out_file.c_str()); 21 | } else if (line->out_type == OUTPUT_TYPE_FILE_APPEND) { 22 | printf("append file - \"%s\"\n", line->out_file.c_str()); 23 | } else { 24 | assert(false); 25 | } 26 | printf("Expressions:\n"); 27 | for (const expr &e : line->exprs) { 28 | if (e.type == EXPR_TYPE_COMMAND) { 29 | printf("\tCommand: %s", e.cmd->exe.c_str()); 30 | for (const std::string& arg : e.cmd->args) 31 | printf(" %s", arg.c_str()); 32 | printf("\n"); 33 | } else if (e.type == EXPR_TYPE_PIPE) { 34 | printf("\tPIPE\n"); 35 | } else if (e.type == EXPR_TYPE_AND) { 36 | printf("\tAND\n"); 37 | } else if (e.type == EXPR_TYPE_OR) { 38 | printf("\tOR\n"); 39 | } else { 40 | assert(false); 41 | } 42 | } 43 | } 44 | 45 | int 46 | main(void) 47 | { 48 | const size_t buf_size = 1024; 49 | char buf[buf_size]; 50 | int rc; 51 | struct parser *p = parser_new(); 52 | while ((rc = read(STDIN_FILENO, buf, buf_size)) > 0) { 53 | parser_feed(p, buf, rc); 54 | struct command_line *line = NULL; 55 | while (true) { 56 | enum parser_error err = parser_pop_next(p, &line); 57 | if (err == PARSER_ERR_NONE && line == NULL) 58 | break; 59 | if (err != PARSER_ERR_NONE) { 60 | printf("Error: %d\n", (int)err); 61 | continue; 62 | } 63 | execute_command_line(line); 64 | delete line; 65 | } 66 | } 67 | parser_delete(p); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/15_sock_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void * 13 | worker_f(void *arg) 14 | { 15 | printf("New client created\n"); 16 | int client_sock = (int) arg; 17 | while(1) { 18 | int buffer = 0; 19 | ssize_t size = recv(client_sock, &buffer, sizeof(buffer), 0); 20 | if (size == -1) { 21 | printf("error = %s\n", strerror(errno)); 22 | continue; 23 | } 24 | if (size == 0) { 25 | printf("Closed connection\n"); 26 | break; 27 | } 28 | printf("Received %d\n", buffer); 29 | buffer++; 30 | if (send(client_sock, &buffer, sizeof(buffer), 0) == -1) 31 | printf("error = %s\n", strerror(errno)); 32 | else 33 | printf("Sent %d\n", buffer); 34 | } 35 | close(client_sock); 36 | return NULL; 37 | } 38 | 39 | int 40 | main(int argc, const char **argv) 41 | { 42 | int sock = socket(AF_UNIX, SOCK_STREAM, 0); 43 | if (sock == -1) { 44 | printf("error = %s\n", strerror(errno)); 45 | return -1; 46 | } 47 | struct sockaddr_un addr; 48 | addr.sun_family = AF_UNIX; 49 | sprintf(addr.sun_path, "%s", "sock_server"); 50 | if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 51 | printf("error = %s\n", strerror(errno)); 52 | return -1; 53 | } 54 | if (listen(sock, 128) == -1) { 55 | printf("error = %s\n", strerror(errno)); 56 | return -1; 57 | } 58 | pthread_attr_t attr; 59 | pthread_attr_init(&attr); 60 | pthread_attr_setdetachstate(&attr, 1); 61 | while(1) { 62 | pthread_t worker_thread; 63 | int client_sock = accept(sock, NULL, NULL); 64 | if (client_sock == -1) { 65 | printf("error = %s\n", strerror(errno)); 66 | continue; 67 | } 68 | int rc = pthread_create(&worker_thread, &attr, worker_f, 69 | (void *) client_sock); 70 | if (rc != 0) { 71 | printf("error = %s\n", strerror(rc)); 72 | close(client_sock); 73 | } 74 | } 75 | pthread_attr_destroy(&attr); 76 | close(sock); 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/14_sock_dgram.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void 9 | initiator_f(int sock, struct sockaddr_un *next_addr) 10 | { 11 | int number; 12 | while(scanf("%d", &number) > 0) { 13 | if (sendto(sock, &number, sizeof(number), 0, 14 | (struct sockaddr *) next_addr, 15 | sizeof(*next_addr)) == -1) { 16 | printf("error = %s\n", strerror(errno)); 17 | continue; 18 | } 19 | printf("Sent %d\n", number); 20 | int buffer = 0; 21 | ssize_t size = recv(sock, &buffer, sizeof(buffer), 0); 22 | if (size == -1) 23 | printf("error = %s\n", strerror(errno)); 24 | else 25 | printf("Received %d\n", buffer); 26 | } 27 | } 28 | 29 | void 30 | worker_f(int sock, struct sockaddr_un *next_addr) 31 | { 32 | while(1) { 33 | int buffer = 0; 34 | ssize_t size = recv(sock, &buffer, sizeof(buffer), 0); 35 | if (size == -1) { 36 | printf("error = %s\n", strerror(errno)); 37 | continue; 38 | } 39 | printf("Received %d\n", buffer); 40 | buffer++; 41 | if (sendto(sock, &buffer, sizeof(buffer), 0, 42 | (struct sockaddr *) next_addr, 43 | sizeof(*next_addr)) == -1) 44 | printf("error = %s\n", strerror(errno)); 45 | else 46 | printf("Sent %d\n", buffer); 47 | } 48 | } 49 | 50 | int 51 | main(int argc, const char **argv) 52 | { 53 | if (argc < 2) { 54 | printf("No id\n"); 55 | return -1; 56 | } 57 | int myid = atoi(argv[1]); 58 | int sock = socket(AF_UNIX, SOCK_DGRAM, 0); 59 | if (sock == -1) { 60 | printf("error = %s\n", strerror(errno)); 61 | return -1; 62 | } 63 | struct sockaddr_un addr; 64 | addr.sun_family = AF_UNIX; 65 | sprintf(addr.sun_path, "sock%d", myid); 66 | if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 67 | printf("error = %s\n", strerror(errno)); 68 | return -1; 69 | } 70 | 71 | struct sockaddr_un next_addr; 72 | next_addr.sun_family = AF_UNIX; 73 | sprintf(next_addr.sun_path, "sock%d", (myid + 1) % 3); 74 | 75 | ssize_t size; 76 | int buffer; 77 | if (myid == 0) 78 | initiator_f(sock, &next_addr); 79 | else 80 | worker_f(sock, &next_addr); 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /lecture_examples/8_net/3_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void * 15 | worker_f(void *arg) 16 | { 17 | printf("New client created\n"); 18 | int client_sock = (int)(uintptr_t)arg; 19 | while(1) { 20 | int buffer = 0; 21 | ssize_t size = recv(client_sock, &buffer, sizeof(buffer), 0); 22 | if (size == -1) { 23 | printf("error = %s\n", strerror(errno)); 24 | continue; 25 | } 26 | if (size == 0) { 27 | printf("Closed connection\n"); 28 | break; 29 | } 30 | printf("Received %d\n", buffer); 31 | buffer++; 32 | if (send(client_sock, &buffer, sizeof(buffer), 0) == -1) 33 | printf("error = %s\n", strerror(errno)); 34 | else 35 | printf("Sent %d\n", buffer); 36 | } 37 | close(client_sock); 38 | return NULL; 39 | } 40 | 41 | int 42 | main(int argc, const char **argv) 43 | { 44 | int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 45 | if (sock == -1) { 46 | printf("error = %s\n", strerror(errno)); 47 | return -1; 48 | } 49 | struct sockaddr_in addr; 50 | addr.sin_family = AF_INET; 51 | addr.sin_port = htons(12345); 52 | inet_aton("127.0.0.1", &addr.sin_addr); 53 | 54 | if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 55 | printf("bind error = %s\n", strerror(errno)); 56 | return -1; 57 | } 58 | if (listen(sock, 128) == -1) { 59 | printf("listen error = %s\n", strerror(errno)); 60 | return -1; 61 | } 62 | pthread_attr_t attr; 63 | pthread_attr_init(&attr); 64 | pthread_attr_setdetachstate(&attr, 1); 65 | while(1) { 66 | pthread_t worker_thread; 67 | int client_sock = accept(sock, NULL, NULL); 68 | if (client_sock == -1) { 69 | printf("error = %s\n", strerror(errno)); 70 | continue; 71 | } 72 | int rc = pthread_create(&worker_thread, &attr, worker_f, 73 | (void *)(uintptr_t)client_sock); 74 | if (rc != 0) { 75 | printf("error = %s\n", strerror(rc)); 76 | close(client_sock); 77 | } 78 | } 79 | pthread_attr_destroy(&attr); 80 | close(sock); 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /4/thread_pool.cpp: -------------------------------------------------------------------------------- 1 | #include "thread_pool.h" 2 | 3 | #include 4 | 5 | struct thread_task { 6 | thread_task_f function; 7 | 8 | /* PUT HERE OTHER MEMBERS */ 9 | }; 10 | 11 | struct thread_pool { 12 | std::vector threads; 13 | 14 | /* PUT HERE OTHER MEMBERS */ 15 | }; 16 | 17 | int 18 | thread_pool_new(int thread_count, struct thread_pool **pool) 19 | { 20 | /* IMPLEMENT THIS FUNCTION */ 21 | (void)thread_count; 22 | (void)pool; 23 | return TPOOL_ERR_NOT_IMPLEMENTED; 24 | } 25 | 26 | int 27 | thread_pool_delete(struct thread_pool *pool) 28 | { 29 | /* IMPLEMENT THIS FUNCTION */ 30 | (void)pool; 31 | return TPOOL_ERR_NOT_IMPLEMENTED; 32 | } 33 | 34 | int 35 | thread_pool_push_task(struct thread_pool *pool, struct thread_task *task) 36 | { 37 | /* IMPLEMENT THIS FUNCTION */ 38 | (void)pool; 39 | (void)task; 40 | return TPOOL_ERR_NOT_IMPLEMENTED; 41 | } 42 | 43 | int 44 | thread_task_new(struct thread_task **task, const thread_task_f &function) 45 | { 46 | /* IMPLEMENT THIS FUNCTION */ 47 | (void)task; 48 | (void)function; 49 | return TPOOL_ERR_NOT_IMPLEMENTED; 50 | } 51 | 52 | bool 53 | thread_task_is_finished(const struct thread_task *task) 54 | { 55 | /* IMPLEMENT THIS FUNCTION */ 56 | (void)task; 57 | return false; 58 | } 59 | 60 | bool 61 | thread_task_is_running(const struct thread_task *task) 62 | { 63 | /* IMPLEMENT THIS FUNCTION */ 64 | (void)task; 65 | return false; 66 | } 67 | 68 | int 69 | thread_task_join(struct thread_task *task) 70 | { 71 | /* IMPLEMENT THIS FUNCTION */ 72 | (void)task; 73 | return TPOOL_ERR_NOT_IMPLEMENTED; 74 | } 75 | 76 | #if NEED_TIMED_JOIN 77 | 78 | int 79 | thread_task_timed_join(struct thread_task *task, double timeout) 80 | { 81 | /* IMPLEMENT THIS FUNCTION */ 82 | (void)task; 83 | (void)timeout; 84 | return TPOOL_ERR_NOT_IMPLEMENTED; 85 | } 86 | 87 | #endif 88 | 89 | int 90 | thread_task_delete(struct thread_task *task) 91 | { 92 | /* IMPLEMENT THIS FUNCTION */ 93 | (void)task; 94 | return TPOOL_ERR_NOT_IMPLEMENTED; 95 | } 96 | 97 | #if NEED_DETACH 98 | 99 | int 100 | thread_task_detach(struct thread_task *task) 101 | { 102 | /* IMPLEMENT THIS FUNCTION */ 103 | (void)task; 104 | return TPOOL_ERR_NOT_IMPLEMENTED; 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/11_cthread_bad_detach.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "clone.h" 10 | 11 | typedef int (*cthread_f)(void *); 12 | 13 | struct cthread { 14 | int returned_code; 15 | cthread_f func; 16 | void *arg; 17 | void *stack; 18 | bool is_finished; 19 | bool is_detached; 20 | jmp_buf jmp; 21 | }; 22 | 23 | void 24 | cthread_destroy(struct cthread *thread) 25 | { 26 | printf("thread is destroyed\n"); 27 | free(thread->stack); 28 | } 29 | 30 | int 31 | cthread_join(volatile struct cthread *thread) 32 | { 33 | printf("thread is joined\n"); 34 | while (! thread->is_finished) 35 | sched_yield(); 36 | cthread_destroy((struct cthread *) thread); 37 | return thread->returned_code; 38 | } 39 | 40 | int 41 | cthread_runner(void *arg) 42 | { 43 | struct cthread *thread = (struct cthread *) arg; 44 | if (setjmp(thread->jmp) == 0) { 45 | thread->returned_code = 46 | thread->func(thread->arg); 47 | } 48 | if (thread->is_detached) 49 | cthread_destroy(thread); 50 | thread->is_finished = true; 51 | return 0; 52 | } 53 | 54 | void 55 | cthread_detach(struct cthread *thread) 56 | { 57 | if (thread->is_finished) 58 | cthread_destroy(thread); 59 | sleep(1); 60 | thread->is_detached = true; 61 | } 62 | 63 | void 64 | cthread_create(struct cthread *result, cthread_f func, 65 | void *arg) 66 | { 67 | result->returned_code = 0; 68 | result->func = func; 69 | result->arg = arg; 70 | result->is_finished = false; 71 | result->is_detached = false; 72 | thread_create_clone(cthread_runner, (void *) result, 73 | &result->stack); 74 | } 75 | 76 | void 77 | cthread_exit(struct cthread *thread, int retcode) 78 | { 79 | thread->returned_code = retcode; 80 | longjmp(thread->jmp, 1); 81 | } 82 | 83 | int 84 | func(void *arg) 85 | { 86 | printf("thread started\n"); 87 | return 0; 88 | } 89 | 90 | int 91 | main() 92 | { 93 | struct cthread thread; 94 | cthread_create(&thread, func, NULL); 95 | cthread_detach(&thread); 96 | while (! thread.is_finished) 97 | sched_yield(); 98 | printf("detached thread finished\n"); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /utils/heap_help/README.md: -------------------------------------------------------------------------------- 1 | The utility helps to detect incorrect heap usage like memory leaks. It works via 2 | redefinition of the standard functions which are expected to allocate something 3 | on the heap. 4 | 5 | **Quick start**: build your code together with `heap_help.c` with compiler flags 6 | `-ldl -rdynamic`. When the app exits, if there are any leaks, a report is 7 | printed saying how many leaks you have, of which sizes, and can show some basic 8 | stacktraces. 9 | 10 | You can also at any moment check the number of not freed allocations using the 11 | function `heaph_get_alloc_count()`. Ideally, before your `main()` function 12 | returns, this number should be zero. Keep in mind that before the process is 13 | terminated, this number might be growing even when you don't allocate anything 14 | due to internal allocations done by the standard library. Those ones are 15 | filtered out at the process exit time. 16 | 17 | There are modes which allow to get more or less info: 18 | 19 | * `./my_app` - run your app with the default heap help mode; 20 | 21 | To enable / disable backtrace collection. On some platforms it might crash, so 22 | try disabling the backtrace if it is failing: 23 | 24 | * `HHBACKTRACE=on` - enable backtrace collection and resolution, default. 25 | 26 | * `HHBACKTRACE=off` - disable it. 27 | 28 | The report mode can help you see how many allocations you do, and some other 29 | reporting details: 30 | 31 | * `HHREPORT=l ./my_app` - l = "leaks", the default mode. If there are no leaks, 32 | then nothing is printed at exit. Otherwise the leaks are printed to stdout 33 | (not all of them if there are too many) with their sizes and stack traces; 34 | 35 | * `HHREPORT=q ./my_app` - q = "quiet", nothing is printed. 36 | 37 | * `HHREPORT=v ./my_app` - v = "verbose", either the leaks are printed like with 38 | the mode "l", or is printed a message saying that "there are no leaks". The 39 | mode helps to check if the heap help is working at all. 40 | 41 | The tool also can help to detect usage of invalid memory. For that it can fill 42 | the newly allocated memory to increase the chances to get a crash and fine the 43 | buggy place. 44 | 45 | * `HHCONTENT=o ./my_app` - o = "original", memory is returned raw as is. 46 | 47 | * `HHCONTENT=t ./my_app` - t = "trash", new memory will be filled with some 48 | trash bytes. 49 | -------------------------------------------------------------------------------- /examples/cpp20_coroutines/README.md: -------------------------------------------------------------------------------- 1 | ## C++20 coroutines 2 | 3 | The example uses C++20 stackless coroutines for doing asynchronous IO on top of epoll and non-blocking sockets. That is a relatively realistic potential usecase which at the same time looks simple enough to understand how those C++ builtin coroutines are working. 4 | 5 | The program starts 2 threads: one is a worker for a bunch of clients, another is a worker for server and its peers. The threads serve IO of their sockets. 6 | 7 | The test's goal is for the clients to send and receive N 1-byte messages, and then close the socket. At the same time the test's code shouldn't use any callbacks. All must be done using coroutines with `co_await` command. 8 | 9 | ### Summary 10 | 11 | The test works, and the code looks simpler than it would be with the callbacks indeed. With smart approach to implementing those async operations they won't require any heap allocations, and the coroutine switching seems fast (although the test doesn't measure that), which means the performance is not a problem. 12 | 13 | #### Can't yield from anywhere 14 | However there is a significant downside, that the coroutines only allow to yield from the root function. In other words, the coroutine can't call a plain function which would `co_await` inside. That makes those coroutines hardly usable for any complex code having deep callstacks, doing multiple blocking operations during the processing. Such complex pipelines would have to be flattened into a sequence of steps to bring all the blocking operations up to the root of the coroutine. Stackfull coroutines don't have such issue, they allow to yield from any place. 15 | 16 | #### About memory usage 17 | Another point to mention is that those stackless coroutines are claimed to be very lightweight in terms of memory compared to the stackfull ones, because the latter need to allocate a big tens of KBs stack. That isn't really a problem, at least in Linux. Memory mapping from virtual to physical pages in Linux is lazy. It means, that if for a stackfull coroutine a stack 100MB is created as `mmap(100MB)`, then those 100MB won't instantly occupy 100MB physical memory. This call will only reserve a range of virtual memory of size 100MB for future use. The actual physical memory allocation will happen on demand, in 4KB blocks. That is, while this stackfull coroutine would be using only <= 4KB stack, only this size is mapped. As it will use more and more stack, it would physically grow in 4KB steps. That already isn't too much. 18 | -------------------------------------------------------------------------------- /5/chat_client.cpp: -------------------------------------------------------------------------------- 1 | #include "chat.h" 2 | #include "chat_client.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct chat_client { 9 | /** Socket connected to the server. */ 10 | int socket = -1; 11 | /** Array of received messages. */ 12 | /* ... */ 13 | /** Output buffer. */ 14 | /* ... */ 15 | /* PUT HERE OTHER MEMBERS */ 16 | }; 17 | 18 | struct chat_client * 19 | chat_client_new(std::string_view name) 20 | { 21 | /* Ignore 'name' param if don't want to support it for +5 points. */ 22 | (void)name; 23 | /* IMPLEMENT THIS FUNCTION */ 24 | return new chat_client(); 25 | } 26 | 27 | void 28 | chat_client_delete(struct chat_client *client) 29 | { 30 | if (client->socket >= 0) 31 | close(client->socket); 32 | 33 | /* IMPLEMENT THIS FUNCTION */ 34 | 35 | delete client; 36 | } 37 | 38 | int 39 | chat_client_connect(struct chat_client *client, std::string_view addr) 40 | { 41 | /* 42 | * 1) Use getaddrinfo() to resolve addr to struct sockaddr_in. 43 | * 2) Create a client socket (function socket()). 44 | * 3) Connect it by the found address (function connect()). 45 | */ 46 | /* IMPLEMENT THIS FUNCTION */ 47 | (void)client; 48 | (void)addr; 49 | 50 | return CHAT_ERR_NOT_IMPLEMENTED; 51 | } 52 | 53 | struct chat_message * 54 | chat_client_pop_next(struct chat_client *client) 55 | { 56 | /* IMPLEMENT THIS FUNCTION */ 57 | (void)client; 58 | return NULL; 59 | } 60 | 61 | int 62 | chat_client_update(struct chat_client *client, double timeout) 63 | { 64 | /* 65 | * The easiest way to wait for updates on a single socket with a timeout 66 | * is to use poll(). Epoll is good for many sockets, poll is good for a 67 | * few. 68 | * 69 | * You create one struct pollfd, fill it, call poll() on it, handle the 70 | * events (do read/write). 71 | */ 72 | (void)client; 73 | (void)timeout; 74 | return CHAT_ERR_NOT_IMPLEMENTED; 75 | } 76 | 77 | int 78 | chat_client_get_descriptor(const struct chat_client *client) 79 | { 80 | return client->socket; 81 | } 82 | 83 | int 84 | chat_client_get_events(const struct chat_client *client) 85 | { 86 | /* 87 | * IMPLEMENT THIS FUNCTION - add OUTPUT event if has non-empty output 88 | * buffer. 89 | */ 90 | (void)client; 91 | return CHAT_EVENT_INPUT; 92 | } 93 | 94 | int 95 | chat_client_feed(struct chat_client *client, const char *msg, uint32_t msg_size) 96 | { 97 | /* IMPLEMENT THIS FUNCTION */ 98 | (void)client; 99 | (void)msg; 100 | (void)msg_size; 101 | return CHAT_ERR_NOT_IMPLEMENTED; 102 | } 103 | -------------------------------------------------------------------------------- /allcups/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cp -r $RESOURCES_DIR_MOUNT/* /sysprog 4 | 5 | if [ "$IS_LOCAL" == "1" ]; then 6 | hw=$HW 7 | SOLUTION_MOUNT="$RESOURCES_DIR_MOUNT/$hw" 8 | cp $SOLUTION_MOUNT/* /sysprog/solution 9 | else 10 | hw=$(jq -r '.hw' "$RESOURCES_DIR_MOUNT/allcups/settings.json") 11 | cp /sysprog/"$hw"/* /sysprog/solution 12 | unzip -o $SOLUTION_MOUNT -d /sysprog/solution 13 | fi 14 | 15 | status='"ERR"' 16 | score=0 17 | output='' 18 | 19 | if [ "$hw" -eq 1 ]; then 20 | cp $RESOURCES_DIR_MOUNT/"$hw"/test.c /sysprog/solution 21 | cp $RESOURCES_DIR_MOUNT/"$hw"/libcoro.c /sysprog/solution 22 | cp $RESOURCES_DIR_MOUNT/"$hw"/libcoro.h /sysprog/solution 23 | cp $RESOURCES_DIR_MOUNT/"$hw"/Makefile /sysprog/solution 24 | rm -f /sysprog/solution/libcoro_test.c 25 | cd /sysprog/solution 26 | 27 | echo '🔨 Building' 28 | make test_glob 29 | 30 | echo '⏳ Running tests' 31 | if output=$(./test 2>&1); then 32 | status='"OK"' 33 | score=$(./test --max_points) 34 | fi 35 | elif [ "$hw" -eq 2 ]; then 36 | cp $RESOURCES_DIR_MOUNT/"$hw"/checker.py /sysprog/solution 37 | cp $RESOURCES_DIR_MOUNT/"$hw"/Makefile /sysprog/solution 38 | cp $RESOURCES_DIR_MOUNT/"$hw"/tests.txt /sysprog/solution 39 | rm /sysprog/solution/parser_test.c 40 | cd /sysprog/solution 41 | 42 | echo '🔨 Building' 43 | make test_glob 44 | 45 | echo '⏳ Running tests' 46 | if output=$(python3 checker.py --with_logic 1 --with_background 1 -e ./mybash 2>&1); then 47 | status='"OK"' 48 | score="25" 49 | elif output=$(python3 checker.py --with_logic 1 -e ./mybash 2>&1); then 50 | status='"OK"' 51 | score="20" 52 | elif output=$(python3 checker.py --with_background 1 -e ./mybash 2>&1); then 53 | status='"OK"' 54 | score="20" 55 | elif output=$(python3 checker.py -e ./mybash 2>&1); then 56 | status='"OK"' 57 | score="15" 58 | fi 59 | elif [ "$hw" -eq 3 ] || [ "$hw" -eq 4 ] || [ "$hw" -eq 5 ]; then 60 | cp $RESOURCES_DIR_MOUNT/"$hw"/test.c /sysprog/solution 61 | cp $RESOURCES_DIR_MOUNT/"$hw"/Makefile /sysprog/solution 62 | rm -f /sysprog/solution/*_exe.c 63 | cd /sysprog/solution 64 | 65 | echo '🔨 Building' 66 | make test_glob 67 | 68 | echo '⏳ Running tests' 69 | if output=$(./test 2>&1); then 70 | status='"OK"' 71 | score=$(./test --max_points) 72 | fi 73 | else 74 | echo "Unknown or unsupported HW number" 75 | fi 76 | 77 | errors_json=$(echo "$output" | jq -R -s '.') 78 | 79 | echo '⚖️ Result:' 80 | if [ "$IS_LOCAL" == "1" ]; then 81 | echo "Status: $status, points: $score" 82 | echo "$output" 83 | else 84 | result=$(cat << EOF 85 | { 86 | "status": $status, 87 | "validationQuality": $score, 88 | "testingQuality": $score, 89 | "errors": [$errors_json] 90 | } 91 | EOF 92 | ) 93 | echo "$result" 94 | echo $result > $RESULT_LOCATION 95 | fi 96 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/9_sem_event_source.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int 13 | sem_get_or_create(key_t key) 14 | { 15 | int semid = semget(key, 4, IPC_CREAT | IPC_EXCL | S_IRWXU | S_IRWXO); 16 | if (semid == -1) { 17 | if (errno != EEXIST) { 18 | printf("error = %s\n", strerror(errno)); 19 | return -1; 20 | } 21 | semid = semget(key, 0, 0); 22 | if (semid == -1) { 23 | printf("error = %s\n", strerror(errno)); 24 | return -1; 25 | } 26 | printf("Connected to sems %d\n", semid); 27 | } else { 28 | printf("Created sems with id %d\n", semid); 29 | unsigned short values[4] = {0, 0, 0, 1}; 30 | semctl(semid, -1, SETALL, values); 31 | } 32 | return semid; 33 | } 34 | 35 | void 36 | sem_inc(int semid, int myid) 37 | { 38 | struct sembuf op; 39 | op.sem_flg = SEM_UNDO; 40 | op.sem_num = myid - 1; 41 | op.sem_op = 1; 42 | semop(semid, &op, 1); 43 | } 44 | 45 | int 46 | msg_queue_get_or_create(key_t key) 47 | { 48 | int queueid = msgget(key, IPC_CREAT | S_IRWXU | S_IRWXO); 49 | if (queueid == -1) 50 | printf("error = %s\n", strerror(errno)); 51 | else 52 | printf("Created/connected to queue %d\n", queueid); 53 | return queueid; 54 | } 55 | 56 | int 57 | pack_msg(char *msg, long type, const char *data, int data_size) 58 | { 59 | char *pos = msg; 60 | memcpy(pos, &type, sizeof(type)); 61 | pos += sizeof(type); 62 | memcpy(pos, &data_size, sizeof(data_size)); 63 | pos += sizeof(data_size); 64 | memcpy(pos, data, data_size); 65 | pos += data_size; 66 | return pos - msg; 67 | } 68 | 69 | void 70 | send_msg(int queueid, int myid, const char *data, int data_size) 71 | { 72 | char msg[512]; 73 | int size = pack_msg(msg, myid, data, data_size); 74 | msgsnd(queueid, msg, size, 0); 75 | printf("Sent %d bytes\n", size); 76 | } 77 | 78 | int 79 | main(int argc, const char **argv) 80 | { 81 | if (argc < 2) { 82 | printf("No id\n"); 83 | return -1; 84 | } 85 | key_t key = ftok("./event_gen", 0); 86 | int semid = sem_get_or_create(key); 87 | if (semid == -1) 88 | return -1; 89 | int queueid = msg_queue_get_or_create(key); 90 | if (queueid == -1) 91 | return -1; 92 | 93 | int myid = atoi(argv[1]); 94 | 95 | char *line = NULL; 96 | size_t line_size = 0; 97 | while(1) { 98 | if (getline(&line, &line_size, stdin) <= 0) 99 | break; 100 | send_msg(queueid, myid, line, strlen(line)); 101 | sem_inc(semid, myid); 102 | } 103 | if (myid == 1) { 104 | msgctl(queueid, IPC_RMID, NULL); 105 | semctl(semid, -1, IPC_RMID); 106 | } 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /2/test_parser.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | _section_header = '######## Section ' 4 | _case_header = '----# Test {' 5 | _case_output = '----# Output' 6 | _case_end = '----# }' 7 | 8 | class TestCase: 9 | def __init__(self, name, line): 10 | self.name = name 11 | self.line = line 12 | self.body = '' 13 | self.output = '' 14 | 15 | class TestSection: 16 | def __init__(self, name, section_type): 17 | self.name = name 18 | self.type = section_type 19 | self.cases = [] 20 | 21 | def parse(filename): 22 | with open(filename) as f: 23 | test_lines = f.read().splitlines() 24 | sections = [] 25 | current_section = None 26 | current_case = None 27 | case_section = None 28 | pos = 0 29 | for line_i, line in enumerate(test_lines): 30 | line_i += 1 31 | if current_case: 32 | if case_section == 'body' and line.startswith(_case_output): 33 | case_section = 'output' 34 | continue 35 | if line.startswith(_case_end): 36 | current_section.cases.append(current_case) 37 | current_case = None 38 | continue 39 | line += '\n' 40 | if case_section == 'body': 41 | current_case.body += line 42 | elif case_section == 'output': 43 | current_case.output += line 44 | else: 45 | sys.exit('Unreachable') 46 | continue 47 | if line.startswith(_section_header): 48 | if current_section: 49 | sections.append(current_section) 50 | current_section = None 51 | section_name = line[len(_section_header):].strip() 52 | if not section_name: 53 | sys.exit('Section name on line {} is empty' 54 | .format(line_i)) 55 | if section_name.startswith('bonus'): 56 | section_type = section_name 57 | else: 58 | section_type = 'base' 59 | current_section = TestSection(section_name, section_type) 60 | continue 61 | if not line: 62 | continue 63 | if line.startswith(_case_header): 64 | case_name = line[len(_case_header):].strip(' -') 65 | if not case_name: 66 | sys.exit('Case name on line {} is empty'.format(line_i)) 67 | current_case = TestCase(case_name, line_i) 68 | case_section = 'body' 69 | continue 70 | sys.exit('Unknown test syntax on line {}'.format(line_i)) 71 | if current_case: 72 | sys.exit('Unfinished test case in the end of file') 73 | if current_section: 74 | sections.append(current_section) 75 | if not sections: 76 | sys.exit('No tests found') 77 | return sections 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Practical examples and homeworks for "System Programming" course of lectures. 2 | 3 | The course: https://slides.com/gerold103/decks/sysprog and https://slides.com/gerold103/decks/sysprog_eng 4 | 5 | ## Running the tests 6 | 7 | ### Manual 8 | 9 | The folders with numbers as names contain the homeworks with tests (where they could be written). The students are welcome to build and run the tests manually - they should work in any non-Windows system. Alternatively they can be run in Docker. It is recommended to use the latest ubuntu Docker image for that. 10 | 11 | In order to check more than just the functional correctness it is encouraged to also check for the memory leaks using `utils/heap_help/`. 12 | 13 | ### Local auto-tests 14 | 15 | In order to run the tests the same as a remote automatic system would do (like VK All Cups) the participants can use the following commands right in their `sysprog/` folder. 16 | ```Bash 17 | # Build the docker image used for running the tests in a stable isolated Linux 18 | # environment. 19 | docker build . -t allcups -f allcups/DockAllcups.dockerfile 20 | 21 | # Run tests for the second homework. One can replace N in `HW=N` with any other 22 | # homework number. 23 | docker run -v ./:/tmp/data --env IS_LOCAL=1 --env HW=2 allcups 24 | ``` 25 | 26 | The argument `HW=N` allows to specify which homework needs to be tested. The `N` must match the directory name where the solution is located. The solutions are expected to be implemented in a fork of this repository, in the same folders where the tasks and their templates are stored. 27 | 28 | ### Remote auto-tests 29 | 30 | However the above commands are slightly different from what the remote automatic system would run. It would rather execute the following lines, right in the `sysprog/` folder as well. 31 | ```Bash 32 | # Specify the number of the homework. 33 | echo "{\"hw\": 3}" > allcups/settings.json 34 | 35 | # The file will contain the score. 36 | touch allcups/output.json 37 | 38 | # The solution needs to be archived as a zip file. 39 | docker run -v ./:/tmp/data -v ./solutions/3.zip:/opt/client/input -v ./allcups/output.json:/opt/results/output.json allcups 40 | ``` 41 | 42 | The remote system uses the vanilla unchanged `sysprog/` repository to get the original tests and `Makefile`s. Then it takes your solution as an archive (can be created using `cd your_solution; zip -r ../solution.zip ./*`), builds it using the original sysprog/ Makefile from the corresponding homework, and then runs it against the original tests. The homework number is determined from the `settings.json` in the first command above. The result is saved into `output.json` and is printed to the console. 43 | 44 | It is highly recommended not to change your local test files and Makefiles at all. Otherwise your solution testing is going to diverge from it is running in the auto-tests system. 45 | -------------------------------------------------------------------------------- /examples/coro_alloca.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | enum { 7 | /** Total number of coroutines. 3 for simplicity. */ 8 | CORO_COUNT = 3, 9 | /** 10 | * This stack size is really small, especially for Mac. 11 | * But enough for tiny functions. 12 | */ 13 | CORO_STACK = 8 * 1024, 14 | }; 15 | 16 | /** Coroutine. */ 17 | struct coro { 18 | /** Last stop context. */ 19 | jmp_buf ctx; 20 | /** True, if it has finished. */ 21 | bool is_finished; 22 | }; 23 | 24 | /** Currently working coroutine. */ 25 | static int coro_i; 26 | /** Coroutines array. Fixed size for simplicity. */ 27 | static struct coro coros[CORO_COUNT]; 28 | 29 | /** 30 | * That macro allocates some stack and plans to start a coroutine 31 | * here. An actual start happens on coro_continue_next from the 32 | * main function. 33 | */ 34 | #define coro_put_here(c, func, ...) ({ \ 35 | int rc = setjmp((c)->ctx); \ 36 | if (rc == 0) { \ 37 | (c)->is_finished = false; \ 38 | volatile char *p = alloca(CORO_STACK); \ 39 | } else { \ 40 | func(__VA_ARGS__); \ 41 | (c)->is_finished = true; \ 42 | } \ 43 | rc; \ 44 | }) 45 | 46 | /** This coroutine. */ 47 | #define coro_this() (&coros[coro_i]) 48 | 49 | /** 50 | * Give the control to a next not finished coroutine. If all the 51 | * others are finished - do nothing. 52 | */ 53 | #define coro_yield() ({ \ 54 | for (int i = 0; i < CORO_COUNT; ++i) { \ 55 | int next = (coro_i + 1) % CORO_COUNT; \ 56 | if (! coros[next].is_finished) { \ 57 | if (setjmp(coro_this()->ctx) == 0) { \ 58 | coro_i = next; \ 59 | longjmp(coro_this()->ctx, 1); \ 60 | } \ 61 | break; \ 62 | } \ 63 | } \ 64 | }) 65 | 66 | /** 67 | * Continue a next not finished coroutine. Note, that function 68 | * does not return, if a not finished coroutine has been found. 69 | */ 70 | static void 71 | coro_continue_next() 72 | { 73 | for (int i = 0; i < CORO_COUNT; ++i) { 74 | if (! coros[i].is_finished) { 75 | coro_i = i; 76 | longjmp(coro_this()->ctx, 1); 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * Just a function to test the example. It calls itself 83 | * recursively and yields many times. 84 | */ 85 | static void 86 | coro_example_func(int deep) 87 | { 88 | printf("%d: start deep = %d\n", coro_i, deep); 89 | coro_yield(); 90 | if (deep < 2) 91 | coro_example_func(deep + 1); 92 | coro_yield(); 93 | printf("%d: end deep = %d\n", coro_i, deep); 94 | } 95 | 96 | int 97 | main(void) 98 | { 99 | printf("Start main\n"); 100 | for (int i = 0; i < CORO_COUNT; ++i) { 101 | if (coro_put_here(&coros[i], coro_example_func, 0) != 0) 102 | break; 103 | } 104 | /* Repeat until no next unfinished coros. */ 105 | coro_continue_next(); 106 | printf("Finish main\n"); 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /5/chat_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct chat_client; 7 | 8 | /** 9 | * Create a new chat client. No bind, no listen, just allocate and 10 | * initialize it. 11 | */ 12 | struct chat_client * 13 | chat_client_new(std::string_view name); 14 | 15 | /** Free all client's resources. */ 16 | void 17 | chat_client_delete(struct chat_client *client); 18 | 19 | /** 20 | * Try to connect to the given address. 21 | * 22 | * @param client Chat client. 23 | * @param addr Address to connect to, like 'localhost:1234', 24 | * '127.0.0.1:3313', '192.168.0.1:4567', etc. 25 | * 26 | * @retval 0 Success. 27 | * @retval !=0 Error code. 28 | * - CHAT_ERR_ALREADY_STARTED - the client is already connected. 29 | * - CHAT_ERR_NO_ADDR - the addr couldn't be resolved to any IP. 30 | * - CHAT_ERR_SYS - a system error, check errno. 31 | */ 32 | int 33 | chat_client_connect(struct chat_client *client, std::string_view addr); 34 | 35 | /** 36 | * Pop a next pending chat message. The returned message has to be 37 | * freed using chat_message_delete(). 38 | * 39 | * @param client Chat client. 40 | * 41 | * @retval not-NULL A message. 42 | * @retval NULL No more messages yet. 43 | */ 44 | struct chat_message * 45 | chat_client_pop_next(struct chat_client *client); 46 | 47 | /** 48 | * Wait for any update for the given timeout and do this update. 49 | * 50 | * @param client Chat client. 51 | * @param timeout Timeout in seconds to wait for. 52 | * 53 | * @retval 0 Success. 54 | * @retval !=0 Error code. 55 | * - CHAT_ERR_TIMEOUT - no updates, timed out. 56 | * - CHAT_ERR_NOT_STARTED - the client is not connected yet. 57 | * - CHAT_ERR_SYS - a system error, check errno. 58 | */ 59 | int 60 | chat_client_update(struct chat_client *client, double timeout); 61 | 62 | /** 63 | * Get client's descriptor suitable for event loops like poll/epoll/kqueue. This 64 | * is useful when want to embed the client into some external event loop. For 65 | * example, to join it with reading stdin. 66 | * 67 | * @retval >=0 A valid descriptor. 68 | * @retval -1 No descriptor. 69 | */ 70 | int 71 | chat_client_get_descriptor(const struct chat_client *client); 72 | 73 | /** 74 | * Get a mask of chat_event values wanted by the client. Needed together with 75 | * client's descriptor for any waiting in poll/epoll/queue. 76 | * 77 | * @retval !=0 Event mask to wait for. 78 | * @retval 0 No events. 79 | */ 80 | int 81 | chat_client_get_events(const struct chat_client *client); 82 | 83 | /** 84 | * Feed a message to the client. 85 | * 86 | * @param client Chat client. 87 | * @param msg Message. 88 | * @param msg_size Size of the message. 89 | * 90 | * @retval 0 Success. 91 | * @retval !=0 Error code. 92 | * - CHAT_ERR_NOT_STARTED - the client is not connected yet. 93 | */ 94 | int 95 | chat_client_feed(struct chat_client *client, const char *msg, 96 | uint32_t msg_size); 97 | -------------------------------------------------------------------------------- /exam/questions_eng.txt: -------------------------------------------------------------------------------- 1 | ## 1 - Multitasking, scheduling 2 | 3 | 1. What are the two types of multitasking? How are they different? 4 | 2. What is the difference between coroutine and thread? 5 | 6 | ## 2 - Process 7 | 8 | 1. What are system calls? Why are they needed? How do they work? Explain the most modern way of their invocation. 9 | 2. When can process’ threads be interrupted by the kernel? 10 | 11 | ## 3 - Memory 12 | 13 | 1. What is false-sharing? Explain how it can happen and how to fix it. 14 | 2. What is virtual memory? How is it translated into physical memory? 15 | 3. What is between main memory and CPU? Why is this intermediate layer needed? 16 | 17 | ## 4 - Signals 18 | 19 | 1. What are signals, like SIGABRT, SIGSEGV, SIGINT, and others? Where are they coming from and when? 20 | 2. What are the 2 APIs for managing signal handling? Which one should be avoided and why? 21 | 22 | ## 5 - File system, storage 23 | 24 | 1. How does the kernel automatically understands what kind of file system is stored on a device? 25 | 2. What are the 2 main tasks of IO operations scheduler in the kernel? 26 | 3. Assume I opened a file stored on a device and got a descriptor fd. What guarantees do I have after I call write(fd, data) regarding data persistency and why? 27 | 4. Name at least 2 ways how the kernel speeds up the access to the storage devices. 28 | 5. Explain how HDD and SSD work. Which one is better or worse in which regard? 29 | 30 | ## 6 - Threads 31 | 32 | 1. Explain the difference between threads and processes. 33 | 2. How to protect access to data when multiple threads are doing reads and writes on it? How does this protection work? 34 | 3. What are lock-free atomic operations and their memory orders: relaxed, acquire, release, sequential-consistency? 35 | 4. A thread T1 wants to go to sleep until a flag F is set to true in another thread T2. Write code for the waiting in T1 and for setting the flag in T2. You must use a mutex and a condition variable. Busy-loop waiting and sleep/usleep/nanosleep/yield/all-alike are not allowed. 36 | 37 | ## 7 - IPC 38 | 39 | 1. What is IPC? What kinds of them do you know? Name at least 3 named IPCs and tell what they do. 40 | 2. What is pipe()? What is it mainly used for? What happens with the pipe’s ends when I call fork()? What happens if I try to read the read-end of a pipe which has no data, but also it has at least one not closed write-end? 41 | 42 | ## 8 - Network 43 | 44 | 1. Name the TCP/IP network model layers and tell briefly what they do. 45 | 2. How do TCP and UDP protocols work? What are the differences? 46 | 3. What is a TCP-connection exactly? At which layer of TCP/IP model does it appear? 47 | 48 | ## 9 - Advanced IO 49 | 50 | 1. What is select() and why is it dangerous? What is the closest alternative which is fine to use? What are the other more scalable ways of doing what select() is doing? 51 | 2. What is the difference between epoll/kqueue/IOCP and poll()? When are those suitable best? 52 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/2_parallel_sort.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct worker { 12 | int fd[2]; 13 | int *array; 14 | int size; 15 | int id; 16 | }; 17 | 18 | int 19 | cmp(const void *a, const void *b) 20 | { 21 | return *(int *)a - *(int *)b; 22 | } 23 | 24 | void 25 | sorter(struct worker *worker, const char *filename) 26 | { 27 | close(worker->fd[0]); 28 | FILE *file = fopen(filename, "r"); 29 | int size = 0; 30 | int capacity = 1024; 31 | int *array = malloc(capacity * sizeof(int)); 32 | while (fscanf(file, "%d", &array[size]) > 0) { 33 | ++size; 34 | if (size == capacity) { 35 | capacity *= 2; 36 | array = realloc(array, capacity * sizeof(int)); 37 | } 38 | } 39 | qsort(array, size, sizeof(int), cmp); 40 | fclose(file); 41 | printf("Worker %d sorted %d numbers\n", worker->id, size); 42 | write(worker->fd[1], &size, sizeof(size)); 43 | write(worker->fd[1], array, sizeof(int) * size); 44 | close(worker->fd[1]); 45 | free(array); 46 | } 47 | 48 | void 49 | read_from_pipe(int fd, char *in, int size) 50 | { 51 | int total = 0; 52 | int rc = read(fd, in, size); 53 | while (rc > 0 && size > 0) { 54 | size -= rc; 55 | in += rc; 56 | total += rc; 57 | rc = read(fd, in, size); 58 | } 59 | } 60 | 61 | int 62 | main(int argc, const char **argv) 63 | { 64 | struct timespec ts; 65 | clock_gettime(CLOCK_MONOTONIC, &ts); 66 | uint64_t start_ns = ts.tv_sec * 1000000000 + ts.tv_nsec; 67 | int nfiles = argc - 1; 68 | struct worker *workers = malloc(sizeof(struct worker) * nfiles); 69 | struct worker *w = workers; 70 | for (int i = 0; i < nfiles; ++i, ++w) { 71 | pipe(w->fd); 72 | w->id = i; 73 | if (fork() == 0) { 74 | sorter(w, argv[i + 1]); 75 | free(workers); 76 | return 0; 77 | } 78 | close(w->fd[1]); 79 | } 80 | int total_size = 0; 81 | w = workers; 82 | for (int i = 0; i < nfiles; ++i, ++w) { 83 | read_from_pipe(w->fd[0], (char *) &w->size, sizeof(w->size)); 84 | w->array = malloc(w->size * sizeof(int)); 85 | read_from_pipe(w->fd[0], (char *) w->array, 86 | w->size * sizeof(int)); 87 | printf("Got %d numbers from worker %d\n", w->size, w->id); 88 | close(w->fd[0]); 89 | wait(NULL); 90 | total_size += w->size; 91 | } 92 | int *total_array = malloc(total_size * sizeof(int)); 93 | int *pos = total_array; 94 | w = workers; 95 | for (int i = 0; i < nfiles; ++i, ++w) { 96 | memcpy(pos, w->array, w->size * sizeof(int)); 97 | pos += w->size; 98 | } 99 | clock_gettime(CLOCK_MONOTONIC, &ts); 100 | uint64_t end_ns = ts.tv_sec * 1000000000 + ts.tv_nsec; 101 | double sec = (end_ns - start_ns) / 1000000000.0; 102 | printf("presort time = %lfs\n", sec); 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/8_sem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void 11 | print_all_sems(int semid) 12 | { 13 | unsigned short values[3]; 14 | if (semctl(semid, -1, GETALL, values) == -1) { 15 | printf("error = %s\n", strerror(errno)); 16 | } else { 17 | printf("Values are %d %d %d\n", values[0], values[1], 18 | values[2]); 19 | } 20 | } 21 | 22 | void 23 | init_sems(int semid) 24 | { 25 | unsigned short values[3] = {1, 2, 3}; 26 | semctl(semid, -1, SETALL, values); 27 | printf("Sem array is initialized\n"); 28 | print_all_sems(semid); 29 | } 30 | 31 | int 32 | main() 33 | { 34 | key_t key = ftok("./a.out", 0); 35 | int semid = semget(key, 3, IPC_CREAT | IPC_EXCL | S_IRWXU | S_IRWXO); 36 | 37 | if (semid == -1) { 38 | if (errno != EEXIST) { 39 | printf("error = %s\n", strerror(errno)); 40 | return -1; 41 | } 42 | printf("Sem array already exists\n"); 43 | semid = semget(key, 0, 0); 44 | if (semid == -1) { 45 | printf("error = %s\n", strerror(errno)); 46 | return -1; 47 | } 48 | printf("Connected to %d\n", semid); 49 | } else { 50 | printf("Created sems with id %d\n", semid); 51 | init_sems(semid); 52 | } 53 | 54 | struct sembuf ops[100], *op; 55 | int ops_count = 0, sem_num; 56 | bool is_inside_txn = false; 57 | char cmd, *line = NULL; 58 | size_t line_size = 0; 59 | while (1) { 60 | getline(&line, &line_size, stdin); 61 | if (line == NULL) 62 | continue; 63 | /* Trim '\n'. */ 64 | line[strlen(line) - 1] = 0; 65 | if (strcmp(line, "begin") == 0) { 66 | is_inside_txn = true; 67 | } else if (strcmp(line, "commit") == 0) { 68 | if (semop(semid, ops, ops_count) == -1) { 69 | printf("error = %s\n", strerror(errno)); 70 | } else { 71 | is_inside_txn = false; 72 | ops_count = 0; 73 | print_all_sems(semid); 74 | } 75 | } else if (strcmp(line, "rollback") == 0) { 76 | is_inside_txn = false; 77 | ops_count = 0; 78 | } else if (strcmp(line, "delete") == 0) { 79 | if (semctl(semid, -1, IPC_RMID) == -1) 80 | printf("error = %s\n", strerror(errno)); 81 | return 0; 82 | } else if (strcmp(line, "quit") == 0) { 83 | return 0; 84 | } else if (strcmp(line, "show") == 0) { 85 | print_all_sems(semid); 86 | } else { 87 | sscanf(line, "%d %c", &sem_num, &cmd); 88 | op = &ops[ops_count]; 89 | if (cmd == '-') { 90 | op->sem_op = -1; 91 | } else if (cmd == '+') { 92 | op->sem_op = 1; 93 | } else { 94 | printf("Unknown operation\n"); 95 | continue; 96 | } 97 | op->sem_num = sem_num - 1; 98 | op->sem_flg = SEM_UNDO; 99 | if (! is_inside_txn) { 100 | if (semop(semid, op, 1) == -1) 101 | printf("error = %s\n", strerror(errno)); 102 | else 103 | print_all_sems(semid); 104 | } else { 105 | ops_count++; 106 | } 107 | } 108 | } 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /5/chat_client_exe.cpp: -------------------------------------------------------------------------------- 1 | #include "chat.h" 2 | #include "chat_client.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int 13 | main(int argc, char **argv) 14 | { 15 | if (argc < 2) { 16 | printf("Expected an address to connect to\n"); 17 | return -1; 18 | } 19 | const char *addr = argv[1]; 20 | const char *name = argc >= 3 ? argv[2] : "anon"; 21 | struct chat_client *cli = chat_client_new(name); 22 | int rc = chat_client_connect(cli, addr); 23 | if (rc != 0) { 24 | printf("Couldn't connect: %d\n", rc); 25 | chat_client_delete(cli); 26 | return -1; 27 | } 28 | struct pollfd poll_fds[2]; 29 | memset(poll_fds, 0, sizeof(poll_fds)); 30 | 31 | struct pollfd *poll_input = &poll_fds[0]; 32 | poll_input->fd = STDIN_FILENO; 33 | poll_input->events = POLLIN; 34 | 35 | struct pollfd *poll_client = &poll_fds[1]; 36 | poll_client->fd = chat_client_get_descriptor(cli); 37 | assert(poll_client->fd >= 0); 38 | 39 | const int buf_size = 1024; 40 | char buf[buf_size]; 41 | while (true) { 42 | /* 43 | * Find what events the client wants. 'IN' is needed always, 44 | * really. But 'OUT' is needed only when the client has data to 45 | * send. Always adding 'OUT' would make poll() return 46 | * immediately even when there is nothing to do leading to a 47 | * busy loop. 48 | */ 49 | poll_client->events = 50 | chat_events_to_poll_events(chat_client_get_events(cli)); 51 | 52 | int rc = poll(poll_fds, 2, -1); 53 | if (rc < 0) { 54 | printf("Poll error: %d\n", errno); 55 | break; 56 | } 57 | if (poll_input->revents != 0) { 58 | /* 59 | * Standard input is signaled - consume some part of it. 60 | * If there is more, poll() will return again on a next 61 | * iteration immediately. 62 | */ 63 | poll_input->revents = 0; 64 | rc = read(STDIN_FILENO, buf, buf_size - 1); 65 | if (rc == 0) { 66 | printf("EOF - exiting\n"); 67 | break; 68 | } 69 | rc = chat_client_feed(cli, buf, rc); 70 | if (rc != 0) { 71 | printf("Feed error: %d\n", rc); 72 | break; 73 | } 74 | } 75 | if (poll_client->revents != 0) { 76 | /* 77 | * Some of the client's needed events are ready. Let it 78 | * handle them internally. Timeout is not needed here, 79 | * hence it is zero. 80 | */ 81 | poll_client->revents = 0; 82 | rc = chat_client_update(cli, 0); 83 | if (rc != 0 && rc != CHAT_ERR_TIMEOUT) { 84 | printf("Update error: %d\n", rc); 85 | break; 86 | } 87 | } 88 | /* Flush all the pending messages to the standard output. */ 89 | struct chat_message *msg; 90 | while ((msg = chat_client_pop_next(cli)) != NULL) { 91 | #if NEED_AUTHOR 92 | printf("%s: %s\n", msg->author.c_str(), msg->data.c_str()); 93 | #else 94 | printf("%s\n", msg->data.c_str()); 95 | #endif 96 | delete msg; 97 | } 98 | } 99 | chat_client_delete(cli); 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/3_fcntl_lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int 9 | write_symbols(int fd, char *cmd) 10 | { 11 | int n; 12 | char c; 13 | sscanf(cmd, "%d %c", &n, &c); 14 | for (int i = 0; i < n; ++i) { 15 | if (write(fd, &c, sizeof(c)) == -1) 16 | return -1; 17 | } 18 | return 0; 19 | } 20 | 21 | int 22 | char_to_whence(char whence) 23 | { 24 | if (whence == 's') 25 | return SEEK_SET; 26 | if (whence == 'e') 27 | return SEEK_END; 28 | return SEEK_CUR; 29 | } 30 | 31 | int 32 | char_to_type(char type) 33 | { 34 | if (type == 'r') 35 | return F_RDLCK; 36 | if (type == 'w') 37 | return F_WRLCK; 38 | return F_UNLCK; 39 | } 40 | 41 | int 42 | do_lock(int fd, bool block, char *cmd) 43 | { 44 | char whence, type; 45 | int start, len; 46 | sscanf(cmd, "%c %c %d %d", &type, &whence, &start, &len); 47 | printf("type = %c, whence = %c, start = %d, len = %d\n", type, whence, 48 | start, len); 49 | struct flock fl; 50 | fl.l_type = char_to_type(type); 51 | fl.l_whence = char_to_whence(whence); 52 | fl.l_start = start; 53 | fl.l_len = len; 54 | return fcntl(fd, block ? F_SETLKW : F_SETLK, &fl); 55 | } 56 | 57 | int 58 | get_lock(int fd, char *cmd) 59 | { 60 | char whence, type; 61 | int start, len; 62 | sscanf(cmd, "%c %c %d %d", &type, &whence, &start, &len); 63 | printf("type = %c, whence = %c, start = %d, len = %d\n", type, whence, 64 | start, len); 65 | struct flock fl; 66 | fl.l_type = char_to_type(type); 67 | fl.l_whence = char_to_whence(whence); 68 | fl.l_start = start; 69 | fl.l_len = len; 70 | if (fcntl(fd, F_GETLK, &fl) == -1) 71 | return -1; 72 | if (fl.l_type == F_UNLCK) 73 | printf("no lock on this region\n"); 74 | else 75 | printf("process %d holds the lock\n", (int)fl.l_pid); 76 | return 0; 77 | } 78 | 79 | int 80 | main() 81 | { 82 | printf("my pid = %d\n", (int) getpid()); 83 | int fd = open("tmp.txt", O_CREAT | O_RDWR, S_IRWXU); 84 | if (fd == -1) { 85 | printf("error = %s\n", strerror(errno)); 86 | return -1; 87 | } 88 | char *buf = NULL; 89 | size_t size = 0; 90 | while (getline(&buf, &size, stdin) > 0) { 91 | char *line = buf; 92 | line[strlen(line) - 1] = 0; 93 | int rc; 94 | printf("line = %s\n", line); 95 | char *cmd = strsep(&line, " \n"); 96 | printf("args = %s\n", line); 97 | printf("cmd = %s\n", cmd); 98 | if (strcmp(cmd, "write") == 0) { 99 | rc = write_symbols(fd, line); 100 | } else if (strcmp(cmd, "lock") == 0) { 101 | rc = do_lock(fd, false, line); 102 | } else if (strcmp(cmd, "lockb") == 0) { 103 | rc = do_lock(fd, true, line); 104 | } else if (strcmp(cmd, "getlock") == 0) { 105 | rc = get_lock(fd, line); 106 | } else { 107 | printf("unknown command\n"); 108 | continue; 109 | } 110 | if (rc == -1) 111 | printf("error = %s\n", strerror(errno)); 112 | else 113 | printf("ok\n"); 114 | } 115 | close(fd); 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /examples/reuse_port_addr/README.md: -------------------------------------------------------------------------------- 1 | ## SO_REUSEADDR vs SO_REUSEPORT 2 | 3 | `SO_REUSEADDR` is an option allowing to 'recycle' an already closed socket, but still 4 | alive in the kernel. The thing is that TCP socket is not deleted from the kernel right 5 | after it is closed, because it still can have some data to send. This is why it happens, 6 | that an attempt to `bind` to an address right after `close` has been called on it, can 7 | lead to an error 'Address in use'. This option allows to immediately delete the closed 8 | socket, if there is an attempt to bind to its address again. 9 | 10 | `SO_REUSEPORT` is an option, allowing to load-balance new clients over multiple 11 | processes bound to exactly the same IP and port. 12 | 13 | Look at the examples in this folder. There is a server, which tries to bind+listen to 14 | a local address, accept one client, and exit. The client just tries to connect and 15 | exits. The server can accept two command line options: `reuse_addr` and `reuse_port` 16 | to set the corresponding flags before `bind()`. 17 | 18 | ### First test: try to bind to the same address without any options: 19 | ``` 20 | # TERMINAL 1 # TERMINAL 2 # 21 | $> gcc server.c -o server 22 | $> gcc client.c -o client 23 | $> ./server $> ./server 24 | bind success bind error: Address already in use 25 | listen success $> 26 | ^C 27 | ``` 28 | Failed. 29 | 30 | ### Second test: try to bind to the same address with `SO_REUSEADDR`: 31 | ``` 32 | # TERMINAL 1 # TERMINAL 2 # 33 | $> ./server reuse_addr $> ./server reuse_addr 34 | reuse addr is set reuse addr is set 35 | bind success bind error: Address already in use 36 | listen success $> 37 | ^C 38 | ``` 39 | Failed. 40 | 41 | ### Third test: try to bind to the same address with `SO_REUSEPORT`: 42 | ``` 43 | # TERMINAL 1 # TERMINAL 2 # 44 | $> ./server reuse_port $> ./server reuse_port 45 | reuse port is set reuse port is set 46 | bind success bind success 47 | listen success listen success 48 | ... ... 49 | ``` 50 | It works. Now create a client: 51 | 52 | ``` 53 | # TERMINAL 1 # TERMINAL 2 # TERMINAL 3 # 54 | ... ... 55 | listen success listen success 56 | $> ./client 57 | accept success connect success 58 | $> $> 59 | 60 | $> ./client 61 | accept success connect success 62 | $> $> 63 | ``` 64 | Two clients are accepted by different servers. 65 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/5_server_poll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int 16 | interact(int client_sock) 17 | { 18 | int buffer = 0; 19 | ssize_t size = recv(client_sock, &buffer, sizeof(buffer), 0); 20 | if (size <= 0) 21 | return (int) size; 22 | printf("Received %d\n", buffer); 23 | buffer++; 24 | size = send(client_sock, &buffer, sizeof(buffer), 0); 25 | if (size > 0) 26 | printf("Sent %d\n", buffer); 27 | return (int) size; 28 | } 29 | 30 | void 31 | remove_client(struct pollfd **fds, int *fd_count, int i) 32 | { 33 | --*fd_count; 34 | struct pollfd *new_fds = malloc(*fd_count * sizeof(new_fds[0])); 35 | memcpy(new_fds, *fds, i * sizeof(new_fds[0])); 36 | memcpy(new_fds + i, *fds + i + 1, 37 | (*fd_count - i) * sizeof(new_fds[0])); 38 | free(*fds); 39 | *fds = new_fds; 40 | } 41 | 42 | int 43 | main(int argc, const char **argv) 44 | { 45 | int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 46 | if (server == -1) { 47 | printf("error = %s\n", strerror(errno)); 48 | return -1; 49 | } 50 | struct sockaddr_in addr; 51 | addr.sin_family = AF_INET; 52 | addr.sin_port = htons(12345); 53 | inet_aton("127.0.0.1", &addr.sin_addr); 54 | 55 | if (bind(server, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 56 | printf("bind error = %s\n", strerror(errno)); 57 | return -1; 58 | } 59 | if (listen(server, 128) == -1) { 60 | printf("listen error = %s\n", strerror(errno)); 61 | return -1; 62 | } 63 | struct pollfd *fds = malloc(sizeof(fds[0])); 64 | int fd_count = 1; 65 | fds[0].fd = server; 66 | fds[0].events = POLLIN; 67 | while(1) { 68 | int nfds = poll(fds, fd_count, 2000); 69 | if (nfds == 0) { 70 | printf("Timeout\n"); 71 | continue; 72 | } 73 | if (nfds == -1) { 74 | printf("error = %s\n", strerror(errno)); 75 | break; 76 | } 77 | if ((fds[0].revents & POLLIN) != 0) { 78 | int client_sock = accept(server, NULL, NULL); 79 | if (client_sock == -1) { 80 | printf("error = %s\n", strerror(errno)); 81 | break; 82 | } 83 | printf("New client\n"); 84 | fd_count++; 85 | fds = realloc(fds, fd_count * sizeof(fds[0])); 86 | fds[fd_count - 1].fd = client_sock; 87 | fds[fd_count - 1].events = POLLIN; 88 | nfds--; 89 | } 90 | for (int i = 0; i < fd_count && nfds > 0; ++i) { 91 | if ((fds[i].revents & POLLIN) == 0) 92 | continue; 93 | nfds--; 94 | printf("Interact with fd %d\n", fds[i].fd); 95 | int rc = interact(fds[i].fd); 96 | if (rc == -1) { 97 | printf("error = %s\n", strerror(errno)); 98 | if (errno != EWOULDBLOCK && errno != EAGAIN) 99 | break; 100 | } else if (rc == 0) { 101 | printf("Client disconnected\n"); 102 | remove_client(&fds, &fd_count, i); 103 | } 104 | } 105 | } 106 | for (int i = 0; i < fd_count; ++i) 107 | close(fds[i].fd); 108 | free(fds); 109 | close(server); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/8_5_cpu_reordering.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "8_5_random_generator.h" 5 | 6 | /** 7 | * Set this flag to 1 to stop reorderings, and to 0 to see them. 8 | */ 9 | #define USE_CPU_FENCE 0 10 | 11 | /** Prevent compiler reordering. */ 12 | #define compiler_barrier() ({asm volatile("" ::: "memory");}) 13 | /** Prevent CPU reordering. */ 14 | #define memory_barrier() ({asm volatile("mfence" ::: "memory");}) 15 | 16 | #if USE_CPU_FENCE 17 | #define barrier() memory_barrier() 18 | #else 19 | #define barrier() compiler_barrier() 20 | #endif 21 | 22 | static volatile bool start_t1 = false; 23 | static volatile bool start_t2 = false; 24 | static volatile bool end_t1 = false; 25 | static volatile bool end_t2 = false; 26 | 27 | static int X, Y; 28 | static int r1, r2; 29 | 30 | /** 31 | * The case - one thread sets X and reads Y. Other thread sets Y 32 | * and reads X. Logically it is impossible, that both threads see 33 | * the initial X and Y, but in fact it happens, without a proper 34 | * protection. 35 | */ 36 | 37 | void * 38 | thread1_f(void *param) 39 | { 40 | struct mersenne_twister twister; 41 | mersenne_twister_create(&twister, 1); 42 | for (;;) 43 | { 44 | /* 45 | * Key moment - both threads should start the 46 | * read-write pairs simultaneously. It increases 47 | * reproducibility. 48 | */ 49 | while(! start_t1) { } 50 | start_t1 = false; 51 | compiler_barrier(); 52 | /* 53 | * The small random delay smooths the problem, 54 | * that one thread can wakeup a bit earlier than 55 | * another, and will perform its transaction too 56 | * early. 57 | */ 58 | while (mersenne_twister_generate(&twister) % 8 != 0) {} 59 | compiler_barrier(); 60 | 61 | /** ----- The transaction ----- */ 62 | X = 1; 63 | barrier(); 64 | r1 = Y; 65 | /** --------------------------- */ 66 | 67 | compiler_barrier(); 68 | end_t1 = true; 69 | } 70 | return NULL; 71 | }; 72 | 73 | void * 74 | thread2_f(void *param) 75 | { 76 | struct mersenne_twister twister; 77 | mersenne_twister_create(&twister, 2); 78 | for (;;) 79 | { 80 | while(! start_t2) { } 81 | start_t2 = false; 82 | compiler_barrier(); 83 | 84 | while (mersenne_twister_generate(&twister) % 8 != 0) {} 85 | compiler_barrier(); 86 | 87 | /** ----- The transaction ----- */ 88 | Y = 1; 89 | barrier(); 90 | r2 = X; 91 | /** --------------------------- */ 92 | 93 | compiler_barrier(); 94 | end_t2 = true; 95 | } 96 | return NULL; 97 | }; 98 | 99 | int 100 | main() 101 | { 102 | pthread_t thread1, thread2; 103 | pthread_create(&thread1, NULL, thread1_f, NULL); 104 | pthread_create(&thread2, NULL, thread2_f, NULL); 105 | 106 | int detected = 0; 107 | for (int iterations = 1; ; iterations++) 108 | { 109 | X = 0; 110 | Y = 0; 111 | start_t1 = true; 112 | start_t2 = true; 113 | compiler_barrier(); 114 | 115 | while(! end_t1 || ! end_t2) { } 116 | compiler_barrier(); 117 | end_t1 = false; 118 | end_t2 = false; 119 | 120 | if (r1 == 0 && r2 == 0) 121 | { 122 | detected++; 123 | printf("%d reorders detected after %d iterations\n", 124 | detected, iterations); 125 | } 126 | } 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /5/chat_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct chat_server; 6 | 7 | /** 8 | * Create a new chat server. No bind, no listen, just allocate and 9 | * initialize it. 10 | */ 11 | struct chat_server * 12 | chat_server_new(void); 13 | 14 | /** Free all server's resources. */ 15 | void 16 | chat_server_delete(struct chat_server *server); 17 | 18 | /** 19 | * Try to listen for new clients on the given port. 20 | * 21 | * @param server Chat server. 22 | * @param port Port to listen on. 23 | * 24 | * @retval 0 Success. 25 | * @retval !=0 Error code. 26 | * - CHAT_ERR_PORT_BUSY - the port is already busy. 27 | * - CHAT_ERR_ALREADY_STARTED - the server is already listening. 28 | * - CHAT_ERR_SYS - a system error, check errno. 29 | */ 30 | int 31 | chat_server_listen(struct chat_server *server, uint16_t port); 32 | 33 | /** 34 | * Pop a next pending chat message. The returned message has to be 35 | * freed using chat_message_delete(). 36 | * 37 | * @param server Chat server. 38 | * 39 | * @retval not-NULL A message. 40 | * @retval NULL No more messages yet. 41 | */ 42 | struct chat_message * 43 | chat_server_pop_next(struct chat_server *server); 44 | 45 | /** 46 | * Wait for any update on any of the sockets for the given timeout 47 | * and do this update. 48 | * 49 | * @param server Chat server. 50 | * @param timeout Timeout in seconds to wait for. 51 | * 52 | * @retval 0 Success. 53 | * @retval !=0 Error code. 54 | * - CHAT_ERR_TIMEOUT - no updates, timed out. 55 | * - CHAT_ERR_NOT_STARTED - the server is not listening yet. 56 | * - CHAT_ERR_SYS - a system error, check errno. 57 | */ 58 | int 59 | chat_server_update(struct chat_server *server, double timeout); 60 | 61 | /** 62 | * Get server's descriptor suitable for event loops like poll/epoll/kqueue. This 63 | * is useful when want to embed the server into some external event loop. For 64 | * example, to join it with reading stdin. 65 | * 66 | * @retval >=0 A valid descriptor. 67 | * @retval -1 No descriptor. 68 | */ 69 | int 70 | chat_server_get_descriptor(const struct chat_server *server); 71 | 72 | /** 73 | * Get the server's own socket descriptor. Not a multiplexing descriptor like 74 | * an epoll or kqueue one, but the original listening descriptor. 75 | * 76 | * @retval >=0 A valid descriptor. 77 | * @retval -1 No descriptor. 78 | */ 79 | int 80 | chat_server_get_socket(const struct chat_server *server); 81 | 82 | /** 83 | * Get a mask of chat_event values wanted by the server. Needed together with 84 | * server's descriptor for any waiting in poll/epoll/kqueue. 85 | * 86 | * @retval !=0 Event mask to wait for. 87 | * @retval 0 No events. 88 | */ 89 | int 90 | chat_server_get_events(const struct chat_server *server); 91 | 92 | /** 93 | * Feed a message to the server to broadcast to all clients. 94 | * 95 | * @param server Chat server. 96 | * @param msg Message. 97 | * @param msg_size Size of the message. 98 | * 99 | * @retval 0 Success. 100 | * @retval !=0 Error code. 101 | * - CHAT_ERR_NOT_IMPLEMENTED - not implemented. 102 | * - CHAT_ERR_NOT_STARTED - the server is not listening yet. 103 | */ 104 | int 105 | chat_server_feed(struct chat_server *server, const char *msg, 106 | uint32_t msg_size); 107 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/12_cthread_detach.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "clone.h" 10 | 11 | typedef int (*cthread_f)(void *); 12 | 13 | void 14 | spin_lock(volatile bool *lock) 15 | { 16 | while (! __sync_bool_compare_and_swap(lock, 0, 1)) {}; 17 | } 18 | 19 | void 20 | spin_unlock(volatile bool *lock) 21 | { 22 | __sync_bool_compare_and_swap(lock, 1, 0); 23 | } 24 | 25 | struct cthread_stack { 26 | pid_t tid; 27 | void *stack; 28 | struct cthread_stack *next; 29 | }; 30 | 31 | struct cthread_stack *stack_list = NULL; 32 | volatile bool last_stack_lock = false; 33 | 34 | struct cthread { 35 | int returned_code; 36 | cthread_f func; 37 | void *arg; 38 | struct cthread_stack *stack; 39 | bool lock; 40 | bool is_finished; 41 | bool is_detached; 42 | jmp_buf jmp; 43 | }; 44 | 45 | void 46 | cthread_destroy(struct cthread *thread) 47 | { 48 | printf("thread is destroyed\n"); 49 | spin_lock(&last_stack_lock); 50 | struct cthread_stack *iter = stack_list; 51 | while (iter != NULL) { 52 | if (iter->tid != 0) 53 | break; 54 | struct cthread_stack *next = iter->next; 55 | free(iter->stack); 56 | free(iter); 57 | iter = next; 58 | printf("a stack is freed\n"); 59 | } 60 | thread->stack->next = iter; 61 | stack_list = thread->stack; 62 | spin_unlock(&last_stack_lock); 63 | } 64 | 65 | int 66 | cthread_join(volatile struct cthread *thread) 67 | { 68 | printf("thread is joined\n"); 69 | while (! thread->is_finished) 70 | sched_yield(); 71 | cthread_destroy((struct cthread *) thread); 72 | return thread->returned_code; 73 | } 74 | 75 | int 76 | cthread_runner(void *arg) 77 | { 78 | struct cthread *thread = (struct cthread *) arg; 79 | if (setjmp(thread->jmp) == 0) { 80 | thread->returned_code = 81 | thread->func(thread->arg); 82 | } 83 | spin_lock(&thread->lock); 84 | if (thread->is_detached) 85 | cthread_destroy(thread); 86 | thread->is_finished = true; 87 | spin_unlock(&thread->lock); 88 | return 0; 89 | } 90 | 91 | void 92 | cthread_detach(struct cthread *thread) 93 | { 94 | spin_lock(&thread->lock); 95 | if (thread->is_finished) 96 | cthread_destroy(thread); 97 | //sleep(1); 98 | thread->is_detached = true; 99 | spin_unlock(&thread->lock); 100 | } 101 | 102 | void 103 | cthread_create(struct cthread *result, cthread_f func, 104 | void *arg) 105 | { 106 | result->returned_code = 0; 107 | result->func = func; 108 | result->arg = arg; 109 | result->is_finished = false; 110 | result->is_detached = false; 111 | result->lock = false; 112 | result->stack = (struct cthread_stack *) malloc(sizeof(*result->stack)); 113 | result->stack->next = NULL; 114 | thread_create_clone_tid(cthread_runner, (void *) result, 115 | &result->stack->stack, &result->stack->tid); 116 | } 117 | 118 | void 119 | cthread_exit(struct cthread *thread, int retcode) 120 | { 121 | thread->returned_code = retcode; 122 | longjmp(thread->jmp, 1); 123 | } 124 | 125 | int 126 | func(void *arg) 127 | { 128 | printf("thread started\n"); 129 | return 0; 130 | } 131 | 132 | int 133 | main() 134 | { 135 | struct cthread thread[10]; 136 | for (int i = 0; i < 10; ++i) { 137 | cthread_create(&thread[i], func, NULL); 138 | cthread_detach(&thread[i]); 139 | } 140 | 141 | for (int i = 0; i < 10; ++i) { 142 | if (! thread[i].is_finished) { 143 | i = 0; 144 | sched_yield(); 145 | } 146 | } 147 | printf("detached threads finished\n"); 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/4_server_select.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int 15 | interact(int client_sock) 16 | { 17 | int buffer = 0; 18 | ssize_t size = recv(client_sock, &buffer, sizeof(buffer), 0); 19 | if (size <= 0) 20 | return (int) size; 21 | printf("Received %d\n", buffer); 22 | buffer++; 23 | size = send(client_sock, &buffer, sizeof(buffer), 0); 24 | if (size > 0) 25 | printf("Sent %d\n", buffer); 26 | return (int) size; 27 | } 28 | 29 | int 30 | fill_fdset(fd_set *set, int *clients, int client_count, int server) 31 | { 32 | int max_fd = server; 33 | FD_ZERO(set); 34 | FD_SET(server, set); 35 | for (int i = 0; i < client_count; ++i) { 36 | FD_SET(clients[i], set); 37 | if (clients[i] > max_fd) 38 | max_fd = clients[i]; 39 | } 40 | return max_fd; 41 | } 42 | 43 | void 44 | remove_client(int **clients, int *client_count, int i) 45 | { 46 | --*client_count; 47 | int *new_clients = malloc(*client_count * sizeof(int)); 48 | memcpy(new_clients, *clients, i * sizeof(int)); 49 | memcpy(new_clients + i, *clients + i + 1, 50 | (*client_count - i) * sizeof(int)); 51 | free(*clients); 52 | *clients = new_clients; 53 | } 54 | 55 | int 56 | main(int argc, const char **argv) 57 | { 58 | int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 59 | if (server == -1) { 60 | printf("error = %s\n", strerror(errno)); 61 | return -1; 62 | } 63 | struct sockaddr_in addr; 64 | addr.sin_family = AF_INET; 65 | addr.sin_port = htons(12345); 66 | inet_aton("127.0.0.1", &addr.sin_addr); 67 | 68 | if (bind(server, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 69 | printf("bind error = %s\n", strerror(errno)); 70 | return -1; 71 | } 72 | if (listen(server, 128) == -1) { 73 | printf("listen error = %s\n", strerror(errno)); 74 | return -1; 75 | } 76 | int *clients = NULL; 77 | int client_count = 0; 78 | fd_set readset; 79 | while(1) { 80 | int max_fd = fill_fdset(&readset, clients, client_count, 81 | server); 82 | struct timeval timeval; 83 | timeval.tv_sec = 2; 84 | timeval.tv_usec = 0; 85 | int nfds = select(max_fd + 1, &readset, NULL, NULL, &timeval); 86 | if (nfds == 0) { 87 | printf("Timeout\n"); 88 | continue; 89 | } 90 | if (nfds == -1) { 91 | printf("error = %s\n", strerror(errno)); 92 | break; 93 | } 94 | if (FD_ISSET(server, &readset)) { 95 | int client_sock = accept(server, NULL, NULL); 96 | if (client_sock == -1) { 97 | printf("error = %s\n", strerror(errno)); 98 | break; 99 | } 100 | printf("New client\n"); 101 | client_count++; 102 | clients = realloc(clients, client_count * sizeof(int)); 103 | clients[client_count - 1] = client_sock; 104 | nfds--; 105 | } 106 | for (int i = 0; i < client_count && nfds > 0; ++i) { 107 | if (! FD_ISSET(clients[i], &readset)) 108 | continue; 109 | nfds--; 110 | printf("Interact with fd %d\n", clients[i]); 111 | int rc = interact(clients[i]); 112 | if (rc == -1) { 113 | printf("error = %s\n", strerror(errno)); 114 | if (errno != EWOULDBLOCK && errno != EAGAIN) 115 | break; 116 | } else if (rc == 0) { 117 | printf("Client disconnected\n"); 118 | remove_client(&clients, &client_count, i); 119 | } 120 | } 121 | } 122 | close(server); 123 | for (int i = 0; i < client_count; ++i) 124 | close(clients[i]); 125 | free(clients); 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/14_condvar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define FUTEX_WAIT 0 11 | #define FUTEX_WAKE 1 12 | 13 | static void 14 | futex_wake(int *val) 15 | { 16 | syscall(SYS_futex, val, FUTEX_WAKE, 1, NULL, NULL, 0); 17 | } 18 | 19 | static void 20 | futex_wait(int *val, int old) 21 | { 22 | syscall(SYS_futex, val, FUTEX_WAIT, old, NULL, NULL, 0); 23 | } 24 | 25 | struct condvar { 26 | int value; 27 | int prev; 28 | }; 29 | 30 | static void 31 | condvar_signal(struct condvar *cond) 32 | { 33 | int old = __atomic_load_n(&cond->prev, __ATOMIC_RELAXED); 34 | __atomic_store_n(&cond->value, old + 1, __ATOMIC_RELEASE); 35 | futex_wake(&cond->value); 36 | } 37 | 38 | static void 39 | condvar_wait(struct condvar *cond, pthread_mutex_t *mutex) 40 | { 41 | int old = __atomic_load_n(&cond->value, __ATOMIC_ACQUIRE); 42 | __atomic_store_n(&cond->prev, old, __ATOMIC_RELAXED); 43 | 44 | pthread_mutex_unlock(mutex); 45 | futex_wait(&cond->value, old); 46 | pthread_mutex_lock(mutex); 47 | } 48 | 49 | static void 50 | condvar_create(struct condvar *cond) 51 | { 52 | cond->value = 0; 53 | cond->prev = 0; 54 | } 55 | 56 | struct state { 57 | pthread_mutex_t mutex; 58 | struct condvar send_cond; 59 | struct condvar recv_cond; 60 | 61 | int counter_send; 62 | int counter_recv; 63 | int target; 64 | }; 65 | 66 | static int thread_send_id = 0; 67 | static int thread_recv_id = 0; 68 | 69 | static void * 70 | thread_send_f(void *arg) 71 | { 72 | struct state *state = arg; 73 | int tid = __atomic_add_fetch(&thread_send_id, 1, __ATOMIC_RELAXED); 74 | printf("send %d: start\n", tid); 75 | 76 | pthread_mutex_lock(&state->mutex); 77 | while (state->counter_send < state->target) { 78 | while (state->counter_send > state->counter_recv) 79 | condvar_wait(&state->send_cond, &state->mutex); 80 | ++state->counter_send; 81 | printf("send %d: sent %d\n", tid, state->counter_send); 82 | condvar_signal(&state->recv_cond); 83 | } 84 | pthread_mutex_unlock(&state->mutex); 85 | } 86 | 87 | static void * 88 | thread_recv_f(void *arg) 89 | { 90 | struct state *state = arg; 91 | int tid = __atomic_add_fetch(&thread_recv_id, 1, __ATOMIC_RELAXED); 92 | printf("recv %d: start\n", tid); 93 | 94 | pthread_mutex_lock(&state->mutex); 95 | while (state->counter_recv < state->target) { 96 | while (state->counter_send == state->counter_recv) 97 | condvar_wait(&state->recv_cond, &state->mutex); 98 | assert(state->counter_send == state->counter_recv + 1); 99 | ++state->counter_recv; 100 | printf("recv %d: received %d\n", tid, state->counter_recv); 101 | condvar_signal(&state->send_cond); 102 | } 103 | pthread_mutex_unlock(&state->mutex); 104 | } 105 | 106 | int 107 | main() 108 | { 109 | struct state state; 110 | pthread_mutex_init(&state.mutex, NULL); 111 | condvar_create(&state.send_cond); 112 | condvar_create(&state.recv_cond); 113 | state.counter_send = 0; 114 | state.counter_recv = 0; 115 | state.target = 1000000; 116 | 117 | const int thread_count = 5; 118 | pthread_t tid_send[thread_count]; 119 | for (int i = 0; i < thread_count; ++i) 120 | pthread_create(&tid_send[i], NULL, thread_send_f, &state); 121 | 122 | pthread_t tid_recv[thread_count]; 123 | for (int i = 0; i < thread_count; ++i) 124 | pthread_create(&tid_recv[i], NULL, thread_recv_f, &state); 125 | 126 | for (int i = 0; i < thread_count; ++i) { 127 | pthread_join(tid_send[i], NULL); 128 | pthread_join(tid_recv[i], NULL); 129 | } 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/13_simple_condvar_2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define FUTEX_WAIT 0 11 | #define FUTEX_WAKE 1 12 | 13 | static void 14 | futex_wake(int *val) 15 | { 16 | syscall(SYS_futex, val, FUTEX_WAKE, 1, NULL, NULL, 0); 17 | } 18 | 19 | static void 20 | futex_wait(int *val, int old) 21 | { 22 | syscall(SYS_futex, val, FUTEX_WAIT, old, NULL, NULL, 0); 23 | } 24 | 25 | struct condvar { 26 | int sequence; 27 | }; 28 | 29 | static void 30 | condvar_signal(struct condvar *cond) 31 | { 32 | // This will break if called enough times to overflow the sequence 33 | /// while wait() has unlocked the mutex, but didn't start waiting yet. 34 | // Too long to reproduce though. 35 | __atomic_add_fetch(&cond->sequence, 1, __ATOMIC_RELEASE); 36 | futex_wake(&cond->sequence); 37 | } 38 | 39 | static void 40 | condvar_wait(struct condvar *cond, pthread_mutex_t *mutex) 41 | { 42 | int old = __atomic_load_n(&cond->sequence, __ATOMIC_ACQUIRE); 43 | pthread_mutex_unlock(mutex); 44 | futex_wait(&cond->sequence, old); 45 | pthread_mutex_lock(mutex); 46 | } 47 | 48 | static void 49 | condvar_create(struct condvar *cond) 50 | { 51 | cond->sequence = 0; 52 | } 53 | 54 | struct state { 55 | pthread_mutex_t mutex; 56 | struct condvar send_cond; 57 | struct condvar recv_cond; 58 | 59 | int counter_send; 60 | int counter_recv; 61 | int target; 62 | }; 63 | 64 | static int thread_send_id = 0; 65 | static int thread_recv_id = 0; 66 | 67 | static void * 68 | thread_send_f(void *arg) 69 | { 70 | struct state *state = arg; 71 | int tid = __atomic_add_fetch(&thread_send_id, 1, __ATOMIC_RELAXED); 72 | printf("send %d: start\n", tid); 73 | 74 | pthread_mutex_lock(&state->mutex); 75 | while (state->counter_send < state->target) { 76 | while (state->counter_send > state->counter_recv) 77 | condvar_wait(&state->send_cond, &state->mutex); 78 | ++state->counter_send; 79 | printf("send %d: sent %d\n", tid, state->counter_send); 80 | condvar_signal(&state->recv_cond); 81 | } 82 | pthread_mutex_unlock(&state->mutex); 83 | } 84 | 85 | static void * 86 | thread_recv_f(void *arg) 87 | { 88 | struct state *state = arg; 89 | int tid = __atomic_add_fetch(&thread_recv_id, 1, __ATOMIC_RELAXED); 90 | printf("recv %d: start\n", tid); 91 | 92 | pthread_mutex_lock(&state->mutex); 93 | while (state->counter_recv < state->target) { 94 | while (state->counter_send == state->counter_recv) 95 | condvar_wait(&state->recv_cond, &state->mutex); 96 | assert(state->counter_send == state->counter_recv + 1); 97 | ++state->counter_recv; 98 | printf("recv %d: received %d\n", tid, state->counter_recv); 99 | condvar_signal(&state->send_cond); 100 | } 101 | pthread_mutex_unlock(&state->mutex); 102 | } 103 | 104 | int 105 | main() 106 | { 107 | struct state state; 108 | pthread_mutex_init(&state.mutex, NULL); 109 | condvar_create(&state.send_cond); 110 | condvar_create(&state.recv_cond); 111 | state.counter_send = 0; 112 | state.counter_recv = 0; 113 | state.target = 1000000; 114 | 115 | const int thread_count = 5; 116 | pthread_t tid_send[thread_count]; 117 | for (int i = 0; i < thread_count; ++i) 118 | pthread_create(&tid_send[i], NULL, thread_send_f, &state); 119 | 120 | pthread_t tid_recv[thread_count]; 121 | for (int i = 0; i < thread_count; ++i) 122 | pthread_create(&tid_recv[i], NULL, thread_recv_f, &state); 123 | 124 | for (int i = 0; i < thread_count; ++i) { 125 | pthread_join(tid_send[i], NULL); 126 | pthread_join(tid_recv[i], NULL); 127 | } 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/7_server_epoll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | struct peer { 18 | int fd; 19 | struct peer *next; 20 | struct peer *prev; 21 | }; 22 | 23 | int 24 | interact(struct peer *p) 25 | { 26 | int buffer = 0; 27 | ssize_t size = recv(p->fd, &buffer, sizeof(buffer), 0); 28 | if (size <= 0) 29 | return (int) size; 30 | printf("Received %d\n", buffer); 31 | buffer++; 32 | size = send(p->fd, &buffer, sizeof(buffer), 0); 33 | if (size > 0) 34 | printf("Sent %d\n", buffer); 35 | return (int) size; 36 | } 37 | 38 | int 39 | main(int argc, const char **argv) 40 | { 41 | int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 42 | if (server == -1) { 43 | printf("error = %s\n", strerror(errno)); 44 | return -1; 45 | } 46 | struct sockaddr_in addr; 47 | addr.sin_family = AF_INET; 48 | addr.sin_port = htons(12345); 49 | inet_aton("127.0.0.1", &addr.sin_addr); 50 | 51 | if (bind(server, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 52 | printf("bind error = %s\n", strerror(errno)); 53 | return -1; 54 | } 55 | if (listen(server, 128) == -1) { 56 | printf("listen error = %s\n", strerror(errno)); 57 | return -1; 58 | } 59 | int ep = epoll_create(1); 60 | if (ep == -1) { 61 | printf("error = %s\n", strerror(errno)); 62 | close(server); 63 | return -1; 64 | } 65 | struct epoll_event new_ev; 66 | new_ev.data.ptr = NULL; 67 | new_ev.events = EPOLLIN; 68 | if (epoll_ctl(ep, EPOLL_CTL_ADD, server, &new_ev) == -1) { 69 | printf("error = %s\n", strerror(errno)); 70 | close(server); 71 | return -1; 72 | } 73 | struct peer *peers = NULL; 74 | while(1) { 75 | int nfds = epoll_wait(ep, &new_ev, 1, 2000); 76 | if (nfds == 0) { 77 | printf("Timeout\n"); 78 | continue; 79 | } 80 | if (nfds == -1) { 81 | printf("error = %s\n", strerror(errno)); 82 | break; 83 | } 84 | if (new_ev.data.ptr == NULL) { 85 | int peer_sock = accept(server, NULL, NULL); 86 | if (peer_sock == -1) { 87 | printf("error = %s\n", strerror(errno)); 88 | break; 89 | } 90 | printf("New client\n"); 91 | struct peer *p = malloc(sizeof(*p)); 92 | new_ev.data.ptr = p; 93 | new_ev.events = EPOLLIN; 94 | if (epoll_ctl(ep, EPOLL_CTL_ADD, peer_sock, 95 | &new_ev) == -1) { 96 | printf("error = %s\n", strerror(errno)); 97 | free(p); 98 | break; 99 | } 100 | p->fd = peer_sock; 101 | p->next = peers; 102 | p->prev = NULL; 103 | if (peers != NULL) 104 | peers->prev = p; 105 | peers = p; 106 | continue; 107 | } 108 | struct peer *p = new_ev.data.ptr; 109 | printf("Interact with fd %d\n", (int)p->fd); 110 | int rc = interact(p); 111 | if (rc == -1) { 112 | printf("error = %s\n", strerror(errno)); 113 | if (errno != EWOULDBLOCK && errno != EAGAIN) 114 | break; 115 | continue; 116 | } 117 | if (rc != 0) 118 | continue; 119 | 120 | printf("Client disconnected\n"); 121 | epoll_ctl(ep, EPOLL_CTL_DEL, p->fd, NULL); 122 | if (p->prev != NULL) 123 | p->prev->next = p->next; 124 | if (p->next != NULL) 125 | p->next->prev = p->prev; 126 | if (p == peers) 127 | peers = p->next; 128 | close(p->fd); 129 | free(p); 130 | } 131 | while (peers != NULL) { 132 | struct peer *next = peers->next; 133 | close(peers->fd); 134 | free(peers); 135 | peers = next; 136 | } 137 | close(ep); 138 | close(server); 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /3/userfs.cpp: -------------------------------------------------------------------------------- 1 | #include "userfs.h" 2 | 3 | #include "rlist.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | enum { 10 | BLOCK_SIZE = 512, 11 | MAX_FILE_SIZE = 1024 * 1024 * 100, 12 | }; 13 | 14 | /** Global error code. Set from any function on any error. */ 15 | static ufs_error_code ufs_error_code = UFS_ERR_NO_ERR; 16 | 17 | struct block { 18 | /** Block memory. */ 19 | char memory[BLOCK_SIZE]; 20 | /** A link in the block list of the owner-file. */ 21 | rlist in_block_list = RLIST_LINK_INITIALIZER; 22 | 23 | /* PUT HERE OTHER MEMBERS */ 24 | }; 25 | 26 | struct file { 27 | /** 28 | * Doubly-linked intrusive list of file blocks. Intrusiveness of the 29 | * list gives you the full control over the lifetime of the items in the 30 | * list without having to use double pointers with performance penalty. 31 | */ 32 | rlist blocks = RLIST_HEAD_INITIALIZER(blocks); 33 | /** How many file descriptors are opened on the file. */ 34 | int refs = 0; 35 | /** File name. */ 36 | std::string name; 37 | /** A link in the global file list. */ 38 | rlist in_file_list = RLIST_LINK_INITIALIZER; 39 | 40 | /* PUT HERE OTHER MEMBERS */ 41 | }; 42 | 43 | /** 44 | * Intrusive list of all files. In this case the intrusiveness of the list also 45 | * grants the ability to remove items from any position in O(1) complexity 46 | * without having to know their iterator. 47 | */ 48 | static rlist file_list = RLIST_HEAD_INITIALIZER(file_list); 49 | 50 | struct filedesc { 51 | file *file; 52 | 53 | /* PUT HERE OTHER MEMBERS */ 54 | }; 55 | 56 | /** 57 | * An array of file descriptors. When a file descriptor is 58 | * created, its pointer drops here. When a file descriptor is 59 | * closed, its place in this array is set to NULL and can be 60 | * taken by next ufs_open() call. 61 | */ 62 | static std::vector file_descriptors; 63 | 64 | enum ufs_error_code 65 | ufs_errno() 66 | { 67 | return ufs_error_code; 68 | } 69 | 70 | int 71 | ufs_open(const char *filename, int flags) 72 | { 73 | /* IMPLEMENT THIS FUNCTION */ 74 | (void)filename; 75 | (void)flags; 76 | (void)file_list; 77 | (void)file_descriptors; 78 | ufs_error_code = UFS_ERR_NOT_IMPLEMENTED; 79 | return -1; 80 | } 81 | 82 | ssize_t 83 | ufs_write(int fd, const char *buf, size_t size) 84 | { 85 | /* IMPLEMENT THIS FUNCTION */ 86 | (void)fd; 87 | (void)buf; 88 | (void)size; 89 | ufs_error_code = UFS_ERR_NOT_IMPLEMENTED; 90 | return -1; 91 | } 92 | 93 | ssize_t 94 | ufs_read(int fd, char *buf, size_t size) 95 | { 96 | /* IMPLEMENT THIS FUNCTION */ 97 | (void)fd; 98 | (void)buf; 99 | (void)size; 100 | ufs_error_code = UFS_ERR_NOT_IMPLEMENTED; 101 | return -1; 102 | } 103 | 104 | int 105 | ufs_close(int fd) 106 | { 107 | /* IMPLEMENT THIS FUNCTION */ 108 | (void)fd; 109 | ufs_error_code = UFS_ERR_NOT_IMPLEMENTED; 110 | return -1; 111 | } 112 | 113 | int 114 | ufs_delete(const char *filename) 115 | { 116 | /* IMPLEMENT THIS FUNCTION */ 117 | (void)filename; 118 | ufs_error_code = UFS_ERR_NOT_IMPLEMENTED; 119 | return -1; 120 | } 121 | 122 | #if NEED_RESIZE 123 | 124 | int 125 | ufs_resize(int fd, size_t new_size) 126 | { 127 | /* IMPLEMENT THIS FUNCTION */ 128 | (void)fd; 129 | (void)new_size; 130 | ufs_error_code = UFS_ERR_NOT_IMPLEMENTED; 131 | return -1; 132 | } 133 | 134 | #endif 135 | 136 | void 137 | ufs_destroy(void) 138 | { 139 | /* 140 | * The file_descriptors array is likely to leak even if 141 | * you resize it to zero or call clear(). This is because 142 | * the vector keeps memory reserved in case more elements 143 | * would be added. 144 | * 145 | * The recommended way of freeing the memory is to swap() 146 | * the vector with a temporary empty vector. 147 | */ 148 | } 149 | -------------------------------------------------------------------------------- /lecture_examples/10_users/9_daemon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int 17 | interact(int client_sock) 18 | { 19 | int buffer = 0; 20 | ssize_t size = read(client_sock, &buffer, sizeof(buffer)); 21 | if (size <= 0) 22 | return (int) size; 23 | printf("Received %d\n", buffer); 24 | buffer++; 25 | size = write(client_sock, &buffer, sizeof(buffer)); 26 | if (size > 0) 27 | printf("Sent %d\n", buffer); 28 | return (int) size; 29 | } 30 | 31 | void 32 | remove_client(struct pollfd **fds, int *fd_count, int i) 33 | { 34 | --*fd_count; 35 | struct pollfd *new_fds = malloc(*fd_count * sizeof(new_fds[0])); 36 | memcpy(new_fds, *fds, i * sizeof(new_fds[0])); 37 | memcpy(new_fds + i, *fds + i + 1, 38 | (*fd_count - i) * sizeof(new_fds[0])); 39 | free(*fds); 40 | *fds = new_fds; 41 | } 42 | 43 | int 44 | daemonize(const char *log_file) 45 | { 46 | if (fork() > 0) 47 | exit(0); 48 | int fd = open(log_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRWXU); 49 | if (fd == -1) { 50 | printf("open error\n"); 51 | return -1; 52 | } 53 | int rc = dup2(fd, STDOUT_FILENO); 54 | close(fd); 55 | if (rc == -1) { 56 | printf("dup error\n"); 57 | return -1; 58 | } 59 | close(STDIN_FILENO); 60 | close(STDERR_FILENO); 61 | return setsid(); 62 | } 63 | 64 | int 65 | main(int argc, const char **argv) 66 | { 67 | if (daemonize(argv[1]) == -1) { 68 | printf("error 1 = %s\n", strerror(errno)); 69 | return -1; 70 | } 71 | int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 72 | if (server == -1) { 73 | printf("error 2 = %s\n", strerror(errno)); 74 | return -1; 75 | } 76 | struct sockaddr_in addr; 77 | addr.sin_family = AF_INET; 78 | addr.sin_port = htons(12345); 79 | inet_aton("127.0.0.1", &addr.sin_addr); 80 | 81 | if (bind(server, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 82 | printf("bind error = %s\n", strerror(errno)); 83 | return -1; 84 | } 85 | if (listen(server, 128) == -1) { 86 | printf("listen error = %s\n", strerror(errno)); 87 | return -1; 88 | } 89 | struct pollfd *fds = malloc(sizeof(fds[0])); 90 | int fd_count = 1; 91 | fds[0].fd = server; 92 | fds[0].events = POLLIN; 93 | while(1) { 94 | fflush(stdout); 95 | int nfds = poll(fds, fd_count, 2000); 96 | if (nfds == 0) { 97 | printf("Timeout\n"); 98 | continue; 99 | } 100 | if (nfds == -1) { 101 | printf("error 3 = %s\n", strerror(errno)); 102 | break; 103 | } 104 | if ((fds[0].revents & POLLIN) != 0) { 105 | int client_sock = accept(server, NULL, NULL); 106 | if (client_sock == -1) { 107 | printf("error 4 = %s\n", strerror(errno)); 108 | break; 109 | } 110 | printf("New client\n"); 111 | fd_count++; 112 | fds = realloc(fds, fd_count * sizeof(fds[0])); 113 | fds[fd_count - 1].fd = client_sock; 114 | fds[fd_count - 1].events = POLLIN; 115 | nfds--; 116 | } 117 | for (int i = 0; i < fd_count && nfds > 0; ++i) { 118 | if ((fds[i].revents & POLLIN) == 0) 119 | continue; 120 | nfds--; 121 | printf("Interact with fd %d\n", fds[i].fd); 122 | int rc = interact(fds[i].fd); 123 | if (rc == -1) { 124 | printf("error 5 = %s\n", strerror(errno)); 125 | break; 126 | } 127 | if (rc == 0) { 128 | printf("Client disconnected\n"); 129 | remove_client(&fds, &fd_count, i); 130 | } 131 | } 132 | } 133 | for (int i = 0; i < fd_count; ++i) 134 | close(fds[i].fd); 135 | free(fds); 136 | close(server); 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /lecture_examples/6_threads/13_simple_condvar_1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define FUTEX_WAIT 0 11 | #define FUTEX_WAKE 1 12 | 13 | static void 14 | futex_wake(int *val) 15 | { 16 | syscall(SYS_futex, val, FUTEX_WAKE, 1, NULL, NULL, 0); 17 | } 18 | 19 | static void 20 | futex_wait(int *val, int old) 21 | { 22 | syscall(SYS_futex, val, FUTEX_WAIT, old, NULL, NULL, 0); 23 | } 24 | 25 | struct condvar { 26 | int sequence; 27 | }; 28 | 29 | static void 30 | condvar_signal(struct condvar *cond) 31 | { 32 | __atomic_add_fetch(&cond->sequence, 1, __ATOMIC_RELEASE); 33 | futex_wake(&cond->sequence); 34 | } 35 | 36 | static void 37 | condvar_wait(struct condvar *cond) 38 | { 39 | // This will deadlock, because will miss 'signal()' sometimes. 40 | // Uncomment the next line to increase the likelyhood. 41 | // 42 | // usleep(100); 43 | // 44 | int old = __atomic_load_n(&cond->sequence, __ATOMIC_ACQUIRE); 45 | futex_wait(&cond->sequence, old); 46 | } 47 | 48 | static void 49 | condvar_create(struct condvar *cond) 50 | { 51 | cond->sequence = 0; 52 | } 53 | 54 | struct state { 55 | pthread_mutex_t mutex; 56 | struct condvar send_cond; 57 | struct condvar recv_cond; 58 | 59 | int counter_send; 60 | int counter_recv; 61 | int target; 62 | }; 63 | 64 | static int thread_send_id = 0; 65 | static int thread_recv_id = 0; 66 | 67 | static void * 68 | thread_send_f(void *arg) 69 | { 70 | struct state *state = arg; 71 | int tid = __atomic_add_fetch(&thread_send_id, 1, __ATOMIC_RELAXED); 72 | printf("send %d: start\n", tid); 73 | 74 | pthread_mutex_lock(&state->mutex); 75 | while (state->counter_send < state->target) { 76 | while (state->counter_send > state->counter_recv) { 77 | pthread_mutex_unlock(&state->mutex); 78 | condvar_wait(&state->send_cond); 79 | pthread_mutex_lock(&state->mutex); 80 | } 81 | ++state->counter_send; 82 | printf("send %d: sent %d\n", tid, state->counter_send); 83 | condvar_signal(&state->recv_cond); 84 | } 85 | pthread_mutex_unlock(&state->mutex); 86 | } 87 | 88 | static void * 89 | thread_recv_f(void *arg) 90 | { 91 | struct state *state = arg; 92 | int tid = __atomic_add_fetch(&thread_recv_id, 1, __ATOMIC_RELAXED); 93 | printf("recv %d: start\n", tid); 94 | 95 | pthread_mutex_lock(&state->mutex); 96 | while (state->counter_recv < state->target) { 97 | while (state->counter_send == state->counter_recv) { 98 | pthread_mutex_unlock(&state->mutex); 99 | condvar_wait(&state->recv_cond); 100 | pthread_mutex_lock(&state->mutex); 101 | } 102 | assert(state->counter_send == state->counter_recv + 1); 103 | ++state->counter_recv; 104 | printf("recv %d: received %d\n", tid, state->counter_recv); 105 | condvar_signal(&state->send_cond); 106 | } 107 | pthread_mutex_unlock(&state->mutex); 108 | } 109 | 110 | int 111 | main() 112 | { 113 | struct state state; 114 | pthread_mutex_init(&state.mutex, NULL); 115 | condvar_create(&state.send_cond); 116 | condvar_create(&state.recv_cond); 117 | state.counter_send = 0; 118 | state.counter_recv = 0; 119 | state.target = 1000000; 120 | 121 | const int thread_count = 5; 122 | pthread_t tid_send[thread_count]; 123 | for (int i = 0; i < thread_count; ++i) 124 | pthread_create(&tid_send[i], NULL, thread_send_f, &state); 125 | 126 | pthread_t tid_recv[thread_count]; 127 | for (int i = 0; i < thread_count; ++i) 128 | pthread_create(&tid_recv[i], NULL, thread_recv_f, &state); 129 | 130 | for (int i = 0; i < thread_count; ++i) { 131 | pthread_join(tid_send[i], NULL); 132 | pthread_join(tid_recv[i], NULL); 133 | } 134 | return 0; 135 | } 136 | -------------------------------------------------------------------------------- /lecture_examples/9_aio/6_server_kqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | struct peer { 18 | int fd; 19 | struct peer *next; 20 | struct peer *prev; 21 | }; 22 | 23 | int 24 | interact(struct peer *p) 25 | { 26 | int buffer = 0; 27 | ssize_t size = recv(p->fd, &buffer, sizeof(buffer), 0); 28 | if (size <= 0) 29 | return (int) size; 30 | printf("Received %d\n", buffer); 31 | buffer++; 32 | size = send(p->fd, &buffer, sizeof(buffer), 0); 33 | if (size > 0) 34 | printf("Sent %d\n", buffer); 35 | return (int) size; 36 | } 37 | 38 | int 39 | main(int argc, const char **argv) 40 | { 41 | int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 42 | if (server == -1) { 43 | printf("error = %s\n", strerror(errno)); 44 | return -1; 45 | } 46 | struct sockaddr_in addr; 47 | addr.sin_family = AF_INET; 48 | addr.sin_port = htons(12345); 49 | inet_aton("127.0.0.1", &addr.sin_addr); 50 | 51 | if (bind(server, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 52 | printf("bind error = %s\n", strerror(errno)); 53 | return -1; 54 | } 55 | if (listen(server, 128) == -1) { 56 | printf("listen error = %s\n", strerror(errno)); 57 | return -1; 58 | } 59 | int kq = kqueue(); 60 | if (kq == -1) { 61 | printf("error = %s\n", strerror(errno)); 62 | close(server); 63 | return -1; 64 | } 65 | struct kevent new_ev; 66 | EV_SET(&new_ev, server, EVFILT_READ, EV_ADD, 0, 0, 0); 67 | if (kevent(kq, &new_ev, 1, 0, 0, NULL) == -1) { 68 | printf("error = %s\n", strerror(errno)); 69 | close(server); 70 | return -1; 71 | } 72 | struct peer *peers = NULL; 73 | struct timespec timeout; 74 | timeout.tv_sec = 2; 75 | timeout.tv_nsec = 0; 76 | while(1) { 77 | int nfds = kevent(kq, NULL, 0, &new_ev, 1, &timeout); 78 | if (nfds == 0) { 79 | printf("Timeout\n"); 80 | continue; 81 | } 82 | if (nfds == -1) { 83 | printf("error = %s\n", strerror(errno)); 84 | break; 85 | } 86 | if (new_ev.udata == NULL) { 87 | int peer_sock = accept(server, NULL, NULL); 88 | if (peer_sock == -1) { 89 | printf("error = %s\n", strerror(errno)); 90 | break; 91 | } 92 | printf("New client\n"); 93 | struct peer *p = malloc(sizeof(*p)); 94 | EV_SET(&new_ev, peer_sock, EVFILT_READ, EV_ADD, 95 | 0, 0, p); 96 | if (kevent(kq, &new_ev, 1, 0, 0, NULL) == -1) { 97 | printf("error = %s\n", strerror(errno)); 98 | close(peer_sock); 99 | free(p); 100 | break; 101 | } 102 | p->fd = peer_sock; 103 | p->next = peers; 104 | p->prev = NULL; 105 | if (peers != NULL) 106 | peers->prev = p; 107 | peers = p; 108 | continue; 109 | } 110 | struct peer *p = new_ev.udata; 111 | printf("Interact with fd %d\n", p->fd); 112 | int rc = interact(p); 113 | if (rc == -1) { 114 | printf("error = %s\n", strerror(errno)); 115 | if (errno != EWOULDBLOCK && errno != EAGAIN) 116 | break; 117 | continue; 118 | } 119 | if ((new_ev.flags & EV_EOF) == 0) 120 | continue; 121 | 122 | printf("Client disconnected\n"); 123 | if (p->prev != NULL) 124 | p->prev->next = p->next; 125 | if (p->next != NULL) 126 | p->next->prev = p->prev; 127 | if (p == peers) 128 | peers = p->next; 129 | close(p->fd); 130 | free(p); 131 | } 132 | while (peers != NULL) { 133 | struct peer *next = peers->next; 134 | close(peers->fd); 135 | free(peers); 136 | peers = next; 137 | } 138 | close(kq); 139 | close(server); 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /5/chat_server.cpp: -------------------------------------------------------------------------------- 1 | #include "chat.h" 2 | #include "chat_server.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct chat_peer { 10 | /** Client's socket. To read/write messages. */ 11 | int socket; 12 | /** Output buffer. */ 13 | /* ... */ 14 | /* PUT HERE OTHER MEMBERS */ 15 | }; 16 | 17 | struct chat_server { 18 | /** Listening socket. To accept new clients. */ 19 | int socket = -1; 20 | /** Array of peers. */ 21 | /* ... */ 22 | /* PUT HERE OTHER MEMBERS */ 23 | }; 24 | 25 | struct chat_server * 26 | chat_server_new(void) 27 | { 28 | /* IMPLEMENT THIS FUNCTION */ 29 | return new chat_server(); 30 | } 31 | 32 | void 33 | chat_server_delete(struct chat_server *server) 34 | { 35 | if (server->socket >= 0) 36 | close(server->socket); 37 | 38 | /* IMPLEMENT THIS FUNCTION */ 39 | 40 | delete server; 41 | } 42 | 43 | int 44 | chat_server_listen(struct chat_server *server, uint16_t port) 45 | { 46 | struct sockaddr_in addr; 47 | memset(&addr, 0, sizeof(addr)); 48 | addr.sin_port = htons(port); 49 | /* Listen on all IPs of this machine. */ 50 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 51 | 52 | /* 53 | * 1) Create a server socket (function socket()). 54 | * 2) Bind the server socket to addr (function bind()). 55 | * 3) Listen the server socket (function listen()). 56 | * 4) Create epoll/kqueue if needed. 57 | */ 58 | /* IMPLEMENT THIS FUNCTION */ 59 | (void)server; 60 | 61 | return CHAT_ERR_NOT_IMPLEMENTED; 62 | } 63 | 64 | struct chat_message * 65 | chat_server_pop_next(struct chat_server *server) 66 | { 67 | /* IMPLEMENT THIS FUNCTION */ 68 | (void)server; 69 | return NULL; 70 | } 71 | 72 | int 73 | chat_server_update(struct chat_server *server, double timeout) 74 | { 75 | /* 76 | * 1) Wait on epoll/kqueue/poll for update on any socket. 77 | * 2) Handle the update. 78 | * 2.1) If the update was on listen-socket, then you probably need to 79 | * call accept() on it - a new client wants to join. 80 | * 2.2) If the update was on a client-socket, then you might want to 81 | * read/write on it. 82 | */ 83 | (void)server; 84 | (void)timeout; 85 | return CHAT_ERR_NOT_IMPLEMENTED; 86 | } 87 | 88 | int 89 | chat_server_get_descriptor(const struct chat_server *server) 90 | { 91 | #if NEED_SERVER_FEED 92 | /* IMPLEMENT THIS FUNCTION if want +5 points. */ 93 | 94 | /* 95 | * Server has multiple sockets - own and from connected clients. Hence 96 | * you can't return a socket here. But if you are using epoll/kqueue, 97 | * then you can return their descriptor. These descriptors can be polled 98 | * just like sockets and will return an event when any of their owned 99 | * descriptors has any events. 100 | * 101 | * For example, assume you created an epoll descriptor and added to 102 | * there a listen-socket and a few client-sockets. Now if you will call 103 | * poll() on the epoll's descriptor, then on return from poll() you can 104 | * be sure epoll_wait() can return something useful for some of those 105 | * sockets. 106 | */ 107 | #endif 108 | (void)server; 109 | return -1; 110 | } 111 | 112 | int 113 | chat_server_get_socket(const struct chat_server *server) 114 | { 115 | return server->socket; 116 | } 117 | 118 | int 119 | chat_server_get_events(const struct chat_server *server) 120 | { 121 | /* 122 | * IMPLEMENT THIS FUNCTION - add OUTPUT event if has non-empty output 123 | * buffer in any of the client-sockets. 124 | */ 125 | (void)server; 126 | return CHAT_EVENT_INPUT; 127 | } 128 | 129 | int 130 | chat_server_feed(struct chat_server *server, const char *msg, uint32_t msg_size) 131 | { 132 | #if NEED_SERVER_FEED 133 | /* IMPLEMENT THIS FUNCTION if want +5 points. */ 134 | #endif 135 | (void)server; 136 | (void)msg; 137 | (void)msg_size; 138 | return CHAT_ERR_NOT_IMPLEMENTED; 139 | } 140 | -------------------------------------------------------------------------------- /advanced/boost_chat/chat_server_exe.cpp: -------------------------------------------------------------------------------- 1 | #include "chat_server.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class chat_server_app final 12 | { 13 | public: 14 | chat_server_app( 15 | uint16_t port); 16 | 17 | int 18 | run(); 19 | 20 | private: 21 | void 22 | priv_recv_next(); 23 | 24 | void 25 | priv_read_next(); 26 | 27 | void 28 | priv_on_recv( 29 | chat_errcode err, 30 | std::unique_ptr msg); 31 | 32 | void 33 | priv_on_input( 34 | const boost::system::error_code& err, 35 | size_t size); 36 | 37 | boost::asio::io_context m_ioctx; 38 | boost::asio::io_context::strand m_strand; 39 | chat_server m_server; 40 | 41 | boost::asio::posix::stream_descriptor m_input; 42 | char m_in_buf[CHAT_RECV_BUF_SIZE]; 43 | 44 | int m_res; 45 | }; 46 | 47 | static int 48 | port_from_str(const char *str, uint16_t *port) 49 | { 50 | errno = 0; 51 | char *end = NULL; 52 | long res = strtol(str, &end, 10); 53 | if (res == 0 && errno != 0) 54 | return -1; 55 | if (*end != 0) 56 | return -1; 57 | if (res > UINT16_MAX || res < 0) 58 | return -1; 59 | *port = (uint16_t)res; 60 | return 0; 61 | } 62 | 63 | chat_server_app::chat_server_app( 64 | uint16_t port) 65 | : m_strand(m_ioctx) 66 | , m_server(m_ioctx) 67 | , m_input(m_ioctx, dup(STDIN_FILENO)) 68 | , m_res(0) 69 | { 70 | m_server.start(port); 71 | boost::asio::post(m_strand, std::bind(&chat_server_app::priv_recv_next, this)); 72 | boost::asio::post(m_strand, std::bind(&chat_server_app::priv_read_next, this)); 73 | } 74 | 75 | int 76 | chat_server_app::run() 77 | { 78 | boost::asio::executor_work_guard work(m_ioctx.get_executor()); 79 | m_ioctx.run(); 80 | return m_res; 81 | } 82 | 83 | void 84 | chat_server_app::priv_recv_next() 85 | { 86 | assert(m_strand.running_in_this_thread()); 87 | m_server.recv_async(boost::asio::bind_executor(m_strand, 88 | std::bind(&chat_server_app::priv_on_recv, this, std::placeholders::_1, 89 | std::placeholders::_2))); 90 | } 91 | 92 | void 93 | chat_server_app::priv_read_next() 94 | { 95 | assert(m_strand.running_in_this_thread()); 96 | boost::asio::async_read(m_input, boost::asio::buffer(m_in_buf, CHAT_RECV_BUF_SIZE), 97 | boost::asio::bind_executor(m_strand, 98 | std::bind(&chat_server_app::priv_on_input, this, std::placeholders::_1, 99 | std::placeholders::_2))); 100 | } 101 | 102 | void 103 | chat_server_app::priv_on_recv( 104 | chat_errcode err, 105 | std::unique_ptr msg) 106 | { 107 | assert(m_strand.running_in_this_thread()); 108 | if (err != CHAT_ERR_NONE) { 109 | std::cout << "Recv error: chat " << err << '\n'; 110 | m_res = -1; 111 | m_ioctx.stop(); 112 | return; 113 | } 114 | std::cout << msg->m_author << ": " << msg->m_data; 115 | priv_recv_next(); 116 | } 117 | 118 | void 119 | chat_server_app::priv_on_input( 120 | const boost::system::error_code& err, 121 | size_t size) 122 | { 123 | assert(m_strand.running_in_this_thread()); 124 | if (err) { 125 | if (err == boost::asio::error::eof) { 126 | std::cout << "Input EOF, terminating\n"; 127 | } else { 128 | std::cout << "Input error: boost " << err << '\n'; 129 | m_res = -1; 130 | } 131 | m_ioctx.stop(); 132 | return; 133 | } 134 | assert(size > 0); 135 | m_server.feed_async(std::string_view(m_in_buf, size)); 136 | priv_read_next(); 137 | } 138 | 139 | int 140 | main(int argc, char **argv) 141 | { 142 | if (argc < 2) { 143 | std::cout << "Expected a port to listen on\n"; 144 | return -1; 145 | } 146 | uint16_t port = 0; 147 | int rc = port_from_str(argv[1], &port); 148 | if (rc != 0) { 149 | std::cout << "Invalid port\n"; 150 | return -1; 151 | } 152 | chat_server_app app(port); 153 | return app.run(); 154 | } 155 | -------------------------------------------------------------------------------- /advanced/boost_chat/chat_client_exe.cpp: -------------------------------------------------------------------------------- 1 | #include "chat_client.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class chat_client_app final 12 | { 13 | public: 14 | chat_client_app( 15 | std::string_view name, 16 | std::string_view endpoint); 17 | 18 | int 19 | run(); 20 | 21 | private: 22 | void 23 | priv_recv_next(); 24 | 25 | void 26 | priv_read_next(); 27 | 28 | void 29 | priv_on_connect( 30 | chat_errcode err); 31 | 32 | void 33 | priv_on_recv( 34 | chat_errcode err, 35 | std::unique_ptr msg); 36 | 37 | void 38 | priv_on_input( 39 | const boost::system::error_code& err, 40 | size_t size); 41 | 42 | boost::asio::io_context m_ioctx; 43 | boost::asio::io_context::strand m_strand; 44 | chat_client m_cli; 45 | 46 | boost::asio::posix::stream_descriptor m_input; 47 | char m_in_buf[CHAT_RECV_BUF_SIZE]; 48 | 49 | int m_res; 50 | }; 51 | 52 | chat_client_app::chat_client_app( 53 | std::string_view name, 54 | std::string_view endpoint) 55 | : m_strand(m_ioctx) 56 | , m_cli(m_ioctx, name) 57 | , m_input(m_ioctx, dup(STDIN_FILENO)) 58 | , m_res(0) 59 | { 60 | m_cli.connect_async(endpoint, boost::asio::bind_executor(m_strand, 61 | std::bind(&chat_client_app::priv_on_connect, this, std::placeholders::_1))); 62 | } 63 | 64 | int 65 | chat_client_app::run() 66 | { 67 | boost::asio::executor_work_guard work(m_ioctx.get_executor()); 68 | m_ioctx.run(); 69 | return m_res; 70 | } 71 | 72 | void 73 | chat_client_app::priv_recv_next() 74 | { 75 | assert(m_strand.running_in_this_thread()); 76 | m_cli.recv_async(boost::asio::bind_executor(m_strand, 77 | std::bind(&chat_client_app::priv_on_recv, this, std::placeholders::_1, 78 | std::placeholders::_2))); 79 | } 80 | 81 | void 82 | chat_client_app::priv_read_next() 83 | { 84 | assert(m_strand.running_in_this_thread()); 85 | boost::asio::async_read(m_input, boost::asio::buffer(m_in_buf, CHAT_RECV_BUF_SIZE), 86 | boost::asio::bind_executor(m_strand, 87 | std::bind(&chat_client_app::priv_on_input, this, std::placeholders::_1, 88 | std::placeholders::_2))); 89 | } 90 | 91 | void 92 | chat_client_app::priv_on_connect( 93 | chat_errcode err) 94 | { 95 | assert(m_strand.running_in_this_thread()); 96 | if (err != CHAT_ERR_NONE) { 97 | std::cout << "Connect error: chat " << err << '\n'; 98 | m_res = -1; 99 | m_ioctx.stop(); 100 | return; 101 | } 102 | priv_recv_next(); 103 | priv_read_next(); 104 | } 105 | 106 | void 107 | chat_client_app::priv_on_recv( 108 | chat_errcode err, 109 | std::unique_ptr msg) 110 | { 111 | assert(m_strand.running_in_this_thread()); 112 | if (err != CHAT_ERR_NONE) { 113 | std::cout << "Recv error: chat " << err << '\n'; 114 | m_res = -1; 115 | m_ioctx.stop(); 116 | return; 117 | } 118 | std::cout << msg->m_author << ": " << msg->m_data; 119 | priv_recv_next(); 120 | } 121 | 122 | void 123 | chat_client_app::priv_on_input( 124 | const boost::system::error_code& err, 125 | size_t size) 126 | { 127 | assert(m_strand.running_in_this_thread()); 128 | if (err) { 129 | if (err == boost::asio::error::eof) { 130 | std::cout << "Input EOF, terminating\n"; 131 | } else { 132 | std::cout << "Input error: boost " << err << '\n'; 133 | m_res = -1; 134 | } 135 | m_ioctx.stop(); 136 | return; 137 | } 138 | assert(size > 0); 139 | m_cli.feed_async(std::string_view(m_in_buf, size)); 140 | priv_read_next(); 141 | } 142 | 143 | int 144 | main(int argc, char **argv) 145 | { 146 | if (argc < 2) { 147 | std::cout << "Expected an address to connect to\n"; 148 | return -1; 149 | } 150 | const char *endpoint = argv[1]; 151 | const char *name = argc >= 3 ? argv[2] : "anon"; 152 | chat_client_app app(name, endpoint); 153 | return app.run(); 154 | } 155 | -------------------------------------------------------------------------------- /lecture_examples/7_ipc/3_mem_sort.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MEM_SIZE 65536 14 | #define IS_READABLE 0 15 | #define IS_WRITABLE 1 16 | #define MEM_META_SIZE 2 17 | 18 | struct worker { 19 | char *mem; 20 | int *array; 21 | int size; 22 | int id; 23 | }; 24 | 25 | int 26 | cmp(const void *a, const void *b) 27 | { 28 | return *(int *)a - *(int *)b; 29 | } 30 | 31 | void 32 | write_to_shared_mem(char *mem, const char *src, int size) 33 | { 34 | volatile char *is_readable = &mem[IS_READABLE]; 35 | volatile char *is_writable = &mem[IS_WRITABLE]; 36 | mem += MEM_META_SIZE; 37 | int mem_size = MEM_SIZE - MEM_META_SIZE; 38 | int saved_size = mem_size; 39 | char *saved_mem = mem; 40 | while (1) { 41 | while (! __atomic_load_n(is_writable, __ATOMIC_ACQUIRE)) 42 | sched_yield(); 43 | int to_copy = mem_size > size ? size : mem_size; 44 | memcpy(mem, src, to_copy); 45 | size -= to_copy; 46 | mem_size -= to_copy; 47 | mem += to_copy; 48 | src += to_copy; 49 | 50 | __atomic_store_n(is_writable, 0, __ATOMIC_RELEASE); 51 | __atomic_store_n(is_readable, 1, __ATOMIC_RELEASE); 52 | if (size == 0) 53 | break; 54 | mem = saved_mem; 55 | mem_size = saved_size; 56 | } 57 | } 58 | 59 | void 60 | read_from_shared_mem(char *mem, char *dst, int size) 61 | { 62 | volatile char *is_readable = &mem[IS_READABLE]; 63 | volatile char *is_writable = &mem[IS_WRITABLE]; 64 | mem += MEM_META_SIZE; 65 | int mem_size = MEM_SIZE - MEM_META_SIZE; 66 | int saved_size = mem_size; 67 | char *saved_mem = mem; 68 | while (1) { 69 | while (! __atomic_load_n(is_readable, __ATOMIC_ACQUIRE)) 70 | sched_yield(); 71 | int to_copy = mem_size > size ? size : mem_size; 72 | memcpy(dst, mem, to_copy); 73 | size -= to_copy; 74 | mem_size -= to_copy; 75 | mem += to_copy; 76 | dst += to_copy; 77 | 78 | __atomic_store_n(is_readable, 0, __ATOMIC_RELEASE); 79 | __atomic_store_n(is_writable, 1, __ATOMIC_RELEASE); 80 | if (size == 0) 81 | break; 82 | mem = saved_mem; 83 | mem_size = saved_size; 84 | } 85 | } 86 | 87 | void 88 | sorter(struct worker *worker, const char *filename) 89 | { 90 | FILE *file = fopen(filename, "r"); 91 | int size = 0; 92 | int capacity = 1024; 93 | int *array = malloc(capacity * sizeof(int)); 94 | while (fscanf(file, "%d", &array[size]) > 0) { 95 | ++size; 96 | if (size == capacity) { 97 | capacity *= 2; 98 | array = realloc(array, capacity * sizeof(int)); 99 | } 100 | } 101 | qsort(array, size, sizeof(int), cmp); 102 | fclose(file); 103 | printf("Worker %d sorted %d numbers\n", worker->id, size); 104 | write_to_shared_mem(worker->mem, (char *) &size, sizeof(size)); 105 | write_to_shared_mem(worker->mem, (char *) array, sizeof(int) * size); 106 | free(array); 107 | } 108 | 109 | int 110 | main(int argc, const char **argv) 111 | { 112 | struct timespec ts; 113 | clock_gettime(CLOCK_MONOTONIC, &ts); 114 | uint64_t start_ns = ts.tv_sec * 1000000000 + ts.tv_nsec; 115 | int nfiles = argc - 1; 116 | struct worker *workers = malloc(sizeof(struct worker) * nfiles); 117 | struct worker *w = workers; 118 | for (int i = 0; i < nfiles; ++i, ++w) { 119 | w->id = i; 120 | w->mem = mmap(NULL, MEM_SIZE, PROT_READ | PROT_WRITE, 121 | MAP_ANON | MAP_SHARED, -1, 0); 122 | w->mem[IS_READABLE] = 0; 123 | w->mem[IS_WRITABLE] = 1; 124 | if (fork() == 0) { 125 | sorter(w, argv[i + 1]); 126 | free(workers); 127 | return 0; 128 | } 129 | } 130 | int total_size = 0; 131 | w = workers; 132 | for (int i = 0; i < nfiles; ++i, ++w) { 133 | read_from_shared_mem(w->mem, (char *) &w->size, 134 | sizeof(w->size)); 135 | w->array = malloc(w->size * sizeof(int)); 136 | read_from_shared_mem(w->mem, (char *) w->array, 137 | w->size * sizeof(int)); 138 | printf("Got %d numbers from worker %d\n", w->size, w->id); 139 | wait(NULL); 140 | total_size += w->size; 141 | } 142 | int *total_array = malloc(total_size * sizeof(int)); 143 | int *pos = total_array; 144 | w = workers; 145 | for (int i = 0; i < nfiles; ++i, ++w) { 146 | memcpy(pos, w->array, w->size * sizeof(int)); 147 | pos += w->size; 148 | } 149 | clock_gettime(CLOCK_MONOTONIC, &ts); 150 | uint64_t end_ns = ts.tv_sec * 1000000000 + ts.tv_nsec; 151 | double sec = (end_ns - start_ns) / 1000000000.0; 152 | printf("presort time = %lfs\n", sec); 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /advanced/boost_chat/task_eng.txt: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------ 2 | Game lobby chat. 3 | Language: C++. 4 | Deadline: n/a. 5 | ------------------------------------------------------------------ 6 | 7 | Need to implement a game lobby chat. It consists of a chat server 8 | and a client. 9 | 10 | The clients connect to the server and each message is broadcasted 11 | to all the clients via this server. So the server is like a game 12 | lobby. Everyone reads all messages from all the others and the 13 | messages are not persisted anyhow. 14 | 15 | In the attached .h and .cpp files you can find templates of 16 | functions, classes, and structures which need to be implemented. 17 | 18 | A usage example of the exe files is that you start a server, 19 | start one or more clients, and everything sent by anybody is 20 | displayed in all clients. Like so: 21 | 22 | $> ./server $> ./client $> ./client 23 | I am client-1! I am client-1! I am client-1! 24 | I am client-2! I am client-2! I am client-2! 25 | 26 | 27 | Rules: 28 | 29 | - Message end is `\n` (line wrap). Note, that '\n' is not a part 30 | of the message. It is just a delimiter. I.e. if feed('msg\n') is 31 | called, then the peers should return 'msg' from recv(). Note 32 | also that feed() can get multiple messages or even their parts: 33 | - feed('msg1\nmsg2\n') - recv() returns 'msg1' and 'msg2'; 34 | - feed('msg1\nms') - recv() returns 'msg1'. 'ms' remains 35 | buffered until meets the next '\n'. 36 | 37 | - Own messages are not sent back to the client. 38 | 39 | - Empty messages (consisting of just spaces - see isspace() 40 | function) are not sent at all. 41 | 42 | - Each message is trimmed from any spaces at both left and right 43 | sides. For example, if I type " m s g " in a terminal and 44 | press enter, the message to be sent should be "m s g". 45 | 46 | 47 | Restrictions: 48 | 49 | - Global variables are not allowed (except for the already 50 | existing ones). 51 | 52 | - Memory leaks are not allowed. 53 | 54 | - Mutexes are not allowed anywhere except for test.cpp. You have 55 | to use strands to make sure there is no concurrent access to any 56 | data. 57 | 58 | - You should not use sleep()/usleep() or any similar function 59 | anywhere except for tests. All waiting for anything has to be 60 | done using async boost functions and methods. 61 | 62 | - No busy-loops of any sort anywhere. 63 | 64 | - Be ready that send/write and other output functions can send 65 | just a part of data. For example, send(100) can return 60. Keep 66 | that in mind when handle results of async_send() and whatever 67 | else you are using. 68 | 69 | - The API in the headers can not be changed (unless you spot a 70 | mistake there - then tell me). 71 | 72 | - The attached makefile should keep working and compilation should 73 | have zero warnings/errors. Alternatively you can try to use 74 | cmake, but then need to keep all the same compiler options. 75 | 76 | 77 | Relaxations: 78 | 79 | - Can assume message queues are always small enough to fit into 80 | the memory. 81 | 82 | - Can assume all input and output buffers always fit into the 83 | memory. 84 | 85 | - Can use abort() for critical errors like when accept() fails. 86 | 87 | - You can store output buffer in any format. For example, copy 88 | each message passed to feed() and store those in a list. Or copy 89 | every message into a single big buffer to the end. 90 | 91 | - Client count is never too big. Peer-sockets and memory for their 92 | resources are always enough on the server. 93 | 94 | - You can assume the client and server operate only on IPv4 95 | addresses. 96 | 97 | - You can try to reuse some code between chat_client_peer and 98 | chat_server_peer. For example, introduce a new interface 99 | chat_peer which is able to send/recv messages and the received 100 | ones it passes to a virtual function that you overload in the 101 | client and server peer classes. 102 | 103 | 104 | Advices: 105 | 106 | - Briefly read the solution templates to get the idea what is 107 | needed overall. 108 | 109 | - Decide on your protocol how will you send and receive messages. 110 | It means you need to design how the messages in the socket's 111 | byte stream would be separated from each other. Zero-byte after 112 | each or send a header with the message size before the message 113 | itself - doesn't matter much, but you need to choose one way. 114 | 115 | - You might want to start with the client. It is easier than the 116 | server. 117 | --------------------------------------------------------------------------------