{"id":16067978,"url":"https://github.com/clementvidon/philosophers","last_synced_at":"2025-03-18T05:31:03.872Z","repository":{"id":63414914,"uuid":"523389834","full_name":"clementvidon/philosophers","owner":"clementvidon","description":"[documented code / -pedantic -std=c89] - 42School variant of the dining philosophers problem to learn the basics of threading a process, how to create threads and use mutexes. ","archived":false,"fork":false,"pushed_at":"2022-10-12T07:34:14.000Z","size":323,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-28T07:32:36.345Z","etag":null,"topics":["42","42born2code","42cursus","42paris","42projects","42school","ansi-c","c89","cleancode","concurrency","concurrent-programming","dining-philosophers","dining-philosophers-problem","dinning-phillospher","multithreading","mutex-synchronisation","philosophers","philosophers-dinner-problem","philosophers42"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clementvidon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-08-10T15:06:20.000Z","updated_at":"2025-02-08T15:46:13.000Z","dependencies_parsed_at":"2022-11-18T13:30:46.634Z","dependency_job_id":null,"html_url":"https://github.com/clementvidon/philosophers","commit_stats":null,"previous_names":["clementvidon/philosophers"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2Fphilosophers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2Fphilosophers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2Fphilosophers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clementvidon%2Fphilosophers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clementvidon","download_url":"https://codeload.github.com/clementvidon/philosophers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243903155,"owners_count":20366431,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["42","42born2code","42cursus","42paris","42projects","42school","ansi-c","c89","cleancode","concurrency","concurrent-programming","dining-philosophers","dining-philosophers-problem","dinning-phillospher","multithreading","mutex-synchronisation","philosophers","philosophers-dinner-problem","philosophers42"],"created_at":"2024-10-09T06:07:31.775Z","updated_at":"2025-03-18T05:31:02.174Z","avatar_url":"https://github.com/clementvidon.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n\tPHILOSOPHERS 42\n\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003e\n\t\u003ca href=\"#summary\"\u003eSummary\u003c/a\u003e\n\t\u003cspan\u003e · \u003c/span\u003e\n\t\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n\t\u003cspan\u003e · \u003c/span\u003e\n\t\u003ca href=\"#tester\"\u003eTester\u003c/a\u003e\n\t\u003cspan\u003e · \u003c/span\u003e\n\t\u003ca href=\"#resources\"\u003eResources\u003c/a\u003e\n\t\u003cspan\u003e · \u003c/span\u003e\n\t\u003ca href=\"#tools\"\u003eTools\u003c/a\u003e\n\t\u003cspan\u003e · \u003c/span\u003e\n\t\u003ca href=\"#helgrind-tutorial\"\u003eHelgrind tutor\u003c/a\u003e\n\t\u003cspan\u003e · \u003c/span\u003e\n\t\u003ca href=\"#optimization\"\u003eOptimization\u003c/a\u003e\n\u003c/h3\u003e\n\n##  Summary\n\nSolution of classical synchronization problem about\n[dining philosophers](https://en.wikipedia.org/wiki/Dining_philosophers_problem)\nwhere each philosopher is a thread and mutexes are used to prevent deadlocks.\n\n* ***[detailed subject](doc/subject.md)***\n\nExternal functions: `printf`, `malloc`, `free`, `write`, `usleep`, `gettimeofday`, `pthread_create`, `pthread_join`, `pthread_mutex_init`, `pthread_mutex_destroy`, `pthread_mutex_lock`, `pthread_mutex_unlock`\n\nCode written in accordance with **42 C** coding style,  **ANSI C89** compliant and entirely **documented with docstrings**.\n\n##  Usage\n\nRun `make` in the root of the projet and launch as follows:\n\n    ./philo \u003cphilo_nb\u003e \u003ctime_die\u003e \u003ctime_eat\u003e \u003ctime_slp\u003e [ \u003cmust_eat\u003e ]\n\n- `philo_nb` -- the number of philosophers and forks.\n- `time_die` -- A philosopher dies if he doesn't start to eat `time_die` ms\n  after **the beginning of his last meal** (or the simulation).\n- `time_eat` -- The time it takes for a philosopher to eat.\n- `time_slp` -- The time it takes for a philosopher to sleep.\n- `must_eat` -- (optional) simulation stops if all philosophers ate at least\n  such amount of times.\n\n- IF NOT `must_eat` THEN simulation stops at first death.\n- IF `philo_nb \u003e 200` OR `time_to_* \u003c 60` ms THEN undefined behavior.\n\nExample of a dinner that should never stop:\n\n    ./philo 4 410 200 200\n            +-|---|---|-- philo_nb\n              +---|---|-- time_die\n                  +---|-- time_eat\n                      +-- time_slp\n\n➡️ **Minimum `time_die` for EVEN `philo_nb`**:\n\n```\n\t2 x time_eat + e\n```\n\n➡️ **Minimum `time_die` for ODD `philo_nb`**:\n\n```\nif time_eat \u003e= time_slp\n\n\t3 * time_eat + e\n\nif time_eat \u003c time_slp\n\n\ttime_eat + time_slp + e\n```\n\n*Where `e` is a margin of error starting from +1ms.*\n\n***Makefile rules***\n\n- `make` -- compiles philo.\n- `make clean` -- deletes object files.\n- `make fclean` -- deletes object files and philo.\n- `make re` -- `fclean` + `make`.\n\n##  Tester\n\nThe tester will run specified tests 5 times:\n1. with helgrind\n2. with valgrind\n3. with sanitizer=address\n4. with sanitizer=thread\n5. with nothing\n\nThe first test will run all the tests from the evaluation scripts 5 times:\n`make test_eval`\n\nThe second test will run the test of your choice 5 times:\n`make test_custom p=\"\u003cphilosophers_arguments\u003e\"`\n\n##  Resources\n\n- **[CodeVault](https://www.youtube.com/playlist?list=PLfqABt5AS4FmuQf70psXrsMLEDQXNkLq2)**\u003cbr\u003e\n\n- **[Concurrent programming](https://begriffs.com/posts/2020-03-23-concurrent-programming.html)**\u003cbr\u003e\n\n##  Tools\n\n- **[philosophers-visualizer](https://nafuka11.github.io/philosophers-visualizer/)**\n\n- **[ft_mallocator](https://github.com/tmatis/ft_mallocator)**\n\n- **valgrind**: `valgrind -q --leak-check=yes --show-leak-kinds=all`\n\n- **sanitizer**: `-fsanitize=thread`\n\n- **valgrind**: `valgrind -q --tool=helgrind`\n\n##  Helgrind tutorial\n\nHow to track and fix a data race?\n\n1. **Output example**\n\n`DATA RACE` and `CONFLICTS` are the main terms that interest us `1\u003e` in the\noutput of helgrind, `utils.c:35` and `utils.c:40` the location of the two\nvariables that he indicates to us as being in conflict and that we must protect\nwith a mutex `2\u003e`.\n\n    1\u003e ==174034== Possible DATA RACE during write of size 1 at 0x4A44062 by thread #1\n       ==174034== Locks held: none\n    2\u003e ==174034==    at 0x40262C: ft_died (simulator_utils.c:47)\n       ==174034==    by 0x401C81: ft_monitor (simulator.c:93)\n       ==174034==    by 0x401B14: ft_simulator (simulator.c:132)\n       ==174034==    by 0x4012B8: main (main.c:79)\n       ==174034==\n    1\u003e ==174034== This CONFLICTS with a previous read of size 1 by thread #4\n       ==174034== Locks held: 3, at addresses 0x4A44128 0x4A44250 0x4A442A0\n    2\u003e ==174034==    at 0x40240C: ft_msleep (time_utils.c:77)\n       ==174034==    by 0x402093: ft_eating (simulation.c:103)\n       ==174034==    by 0x401F0A: ft_simulation (simulation.c:136)\n       ==174034==    by 0x483F876: mythread_wrapper (hg_intercepts.c:387)\n       ==174034==    by 0x4862EA6: start_thread (pthread_create.c:477)\n       ==174034==    by 0x4979DEE: clone (clone.S:95)\n       ==174034==  Address 0x4a44062 is 34 bytes inside a block of size 48 alloc'd\n       ==174034==    at 0x48397CF: malloc (vg_replace_malloc.c:307)\n       ==174034==    by 0x401930: ft_init (init.c:114)\n       ==174034==    by 0x401289: main (main.c:77)\n       ==174034==  Block was alloc'd by thread #1\n\n2. **Fix example**\u003cbr\u003e\n\nAt `simulator_utils.c:47` we have:\n\n    data-\u003edied = true;\n\nThat can be protected like this:\n\n    pthread_mutex_lock (\u0026data-\u003emutex[DIED]);\n    data-\u003edied = true;\n    pthread_mutex_unlock (\u0026data-\u003emutex[DIED]);\n\nAt `time_utils.c:47` we have:\n\n    if (philo-\u003edata-\u003edied)\n        return ;\n\nThat can be protected like this:\n\n    pthread_mutex_lock (\u0026philo-\u003edata-\u003emutex[DIED]);\n    if (philo-\u003edata-\u003edied)\n        return ((void)pthread_mutex_unlock (\u0026philo-\u003edata-\u003emutex[DIED]));\n    pthread_mutex_unlock (\u0026philo-\u003edata-\u003emutex[DIED]);\n\nOr like this:\n\n    int died;\n    pthread_mutex_lock (\u0026philo-\u003edata-\u003emutex[DIED]);\n    died = philo-\u003edata-\u003edied;\n    pthread_mutex_unlock (\u0026philo-\u003edata-\u003emutex[DIED]);\n    if (died)\n       return ;\n\n##  Optimization\n\n1. **Locking granularity**:\n\n\"The **granularity of a lock is how much data it protects**. A lock with coarse\ngranularity will protect a large amount of data, and a lock with fine\ngranularity will protect a small amount of data.\n\nAlthough it is tempting **to place locks at the finest level of granularity**\npossible, it **is not usually the best strategy**. Having many locks with fine\ngranularity tends to introduce overhead. The overhead comes from the increased\namount of memory needed to hold these locks and the decreased likelihood that\nthe locks will be cache resident when they are needed.\n\nHowever, having few locks usually results in those locks becoming contended by\nmultiple threads, which limits the scaling of the application. Therefore, the\ngranularity of the locks is usually some kind of **trade-off** between a few\ncontended locks and many uncontended locks. The appropriate balancing point will\n**depend on the number of threads**, so it may actually change if the application\nis run with different workloads of with different numbers of threads. Hence, it\nis important to test an application with both representative workloads and on a\nrepresentative system with sufficient virtual CPUs.\n\nThe key to picking the **optimal locking strategy** is often a good grasp of the\n**theoretical performance** of the application, in comparison with the actual\nprofile of the application. It is also important to examine the actual scaling\nof the application as the number of threads increase.\"\n\n*Source: Multicore Application Programming by Darryl Gove*\n\n2. **[Performance Analysis with Callgrind and Cachegrind](https://www.vi-hps.org/cms/upload/material/tw10/vi-hps-tw10-KCachegrind.pdf)**\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclementvidon%2Fphilosophers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclementvidon%2Fphilosophers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclementvidon%2Fphilosophers/lists"}