{"id":19053301,"url":"https://github.com/jotavare/philosophers","last_synced_at":"2025-07-09T05:04:19.466Z","repository":{"id":155258669,"uuid":"628648142","full_name":"jotavare/philosophers","owner":"jotavare","description":"Often referred to as the Dining Philosophers Problem, is a classical synchronization problem that explores the challenges of resource sharing and deadlock avoidance. ","archived":false,"fork":false,"pushed_at":"2024-06-26T23:16:08.000Z","size":1467,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-08T23:33:32.314Z","etag":null,"topics":["c","data-races","deadlock","dining-philosophers-problem","gdb","makefile","multithreading","mutex-synchronisation","mutexes-locks","norminette","philosophers","pthreads","semaphore","thread","valgrind"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jotavare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-16T15:50:07.000Z","updated_at":"2024-09-01T09:09:15.000Z","dependencies_parsed_at":"2023-12-02T19:24:45.495Z","dependency_job_id":"51d19f9a-6050-4a40-950c-4721a7e1ec60","html_url":"https://github.com/jotavare/philosophers","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jotavare%2Fphilosophers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jotavare%2Fphilosophers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jotavare%2Fphilosophers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jotavare%2Fphilosophers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jotavare","download_url":"https://codeload.github.com/jotavare/philosophers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232186791,"owners_count":18485380,"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":["c","data-races","deadlock","dining-philosophers-problem","gdb","makefile","multithreading","mutex-synchronisation","mutexes-locks","norminette","philosophers","pthreads","semaphore","thread","valgrind"],"created_at":"2024-11-08T23:30:01.556Z","updated_at":"2025-01-02T10:43:06.337Z","avatar_url":"https://github.com/jotavare.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/jotavare/jotavare/blob/main/42/banners/piscine_and_common_core/github_piscine_and_common_core_banner_philosophers.png\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/status-finished-success?color=%2312bab9\u0026style=flat-square\"/\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/evaluated-18%20%2F%2012%20%2F%202022-success?color=%2312bab9\u0026style=flat-square\"/\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/score-100%20%2F%20100-success?color=%2312bab9\u0026style=flat-square\"/\u003e\n\t\u003cimg src=\"https://img.shields.io/github/languages/top/jotavare/philosophers?color=%2312bab9\u0026style=flat-square\"/\u003e\n\t\u003cimg src=\"https://img.shields.io/github/last-commit/jotavare/philosophers?color=%2312bab9\u0026style=flat-square\"/\u003e\n\t\u003ca href='https://www.linkedin.com/in/joaoptoliveira' target=\"_blank\"\u003e\u003cimg alt='Linkedin' src='https://img.shields.io/badge/LinkedIn-100000?style=flat-square\u0026logo=Linkedin\u0026logoColor=white\u0026labelColor=0A66C2\u0026color=0A66C2'/\u003e\u003c/a\u003e\n\t\u003ca href='https://profile.intra.42.fr/users/jotavare' target=\"_blank\"\u003e\u003cimg alt='42' src='https://img.shields.io/badge/Porto-100000?style=flat-square\u0026logo=42\u0026logoColor=white\u0026labelColor=000000\u0026color=000000'/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"#about\"\u003eAbout\u003c/a\u003e •\n\t\u003ca href=\"#how-to-use\"\u003eHow to use\u003c/a\u003e •\n\t\u003ca href=\"#mandatory\"\u003eMandatory\u003c/a\u003e •\n\t\u003ca href=\"#bonus\"\u003eBonus\u003c/a\u003e •\n\t\u003ca href=\"#philosophers\"\u003ePhilosophers\u003c/a\u003e •\n\t\u003ca href=\"#examples\"\u003eExamples\u003c/a\u003e •\n\t\u003ca href=\"#norminette\"\u003eNorminette\u003c/a\u003e •\n\t\u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e •\n\t\u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n\u003c/p\u003e\n\n## ABOUT\nFive philosophers reside in a house, sharing a dining table with a special spaghetti dish requiring two forks, one between each plate. To eat, a philosopher needs both left and right forks, which depend on neighbours' activities. They alternate between contemplating and dining, setting down both forks after a meal.\n\nThe challenge I had, was to figure out how to make sure no philosopher goes hungry while dealing with the unpredictability of when others want to eat or think. It's like trying to create a system that keeps everyone fed without knowing when they'll be hungry or lost in thought.\n\nFor further exploration of this problem, you can consult the \u003ca href=\"https://en.wikipedia.org/wiki/Dining_philosophers_problem\"\u003eWikipedia\u003c/a\u003e article.\n\n- [Subject](https://github.com/jotavare/philosophers/blob/master/subject/en_subject_philosophers.pdf) `PDF`\n- [References](https://github.com/jotavare/42-resources#03-philosophers) `GitHub`\n\n## HOW TO USE\n#### 1º - Clone the repository\n```bash\ngit clone git@github.com:jotavare/philosophers.git\n```\n\n#### 2º - Enter the project folder and run `make`\n```bash\ncd philosophers/philosophers\nmake\n```\n\n#### 3º - Launch the program\n\u003e The last argument is optional for the execution of the program.\n```bash\n./philo [n of philos] [time to die] [time to eat] [time to sleep] [n times each philo must eat]\n```\n\n#### MAKEFILE RULES\n`make` or `make all` - Compile philosophers **mandatory** files.\n\n`make clean` - Delete all .o (object files) files.\n\n`make fclean` - Delete all .o (object file) and .a (executable) files.\n\n`make re` - Use rules `fclean` + `all`.\n\n## MANDATORY\n\u003e Objective: can't kill the philosophers.\n- [x] Each philosopher is a **thread** and each fork is a **mutex**.\n- [x] They do it in order: `eat` -\u003e `sleep` -\u003e `think` (they don't think, they wait to have their forks to eat).\n- [x] To eat they must have two forks, knowing that there is only one fork per philosopher.\n- [x] If one of them dies, the simulation stops and death must be displayed in a maximum of 10 milliseconds.\n- [x] Write each change of the philosopher's status.\n \n## BONUS\n\u003e The bonus program takes the same arguments and it as to comply with the mandatory rules.\n- [ ] All the forks are put in the middle of the table.\n- [ ] They have no states in memory, but the number of available forks is represented by a semaphore.\n- [ ] Each philosopher should be a process, but the main process should not be a philosopher.\n\n## PHILOSOPHERS\n\n```bash\n./philo [arg1] [arg2] [arg3] [arg4] [arg5]\n``` \n\n| Arg | Function | Description |\n| :- | :- | :- |\n| [arg1] | `number_of_philosophers`                    | Number of philosophers and number of forks.              |\n| [arg2] | `time_to_die`                               | If he hasn't eaten for time_to_die milliseconds he dies. |\n| [arg3] | `time_to_eat`                               | Time to eat with two forks in milliseconds.              |\n| [arg4] | `time_to_sleep`                             | Time to sleep in milliseconds.                           |\n| [arg5] | `number_of_times_each_philosopher_must_eat` | Number of times each philosopher must eat. (Optional)    |\n\n#### THREADS AND MUTEXES\nA **thread** is a unit of execution within a process. Each **process** has at least one **thread**, but additional **threads** can be created. A **thread** consists of unique elements and shared elements with other **threads** of the same process, such as the code section, data section, and operating system resources like open files and signals.\n\nHowever, if two **threads** of the same process try to access the same shared memory variable simultaneously, it can lead to undefined behaviours, known as **data races**. To prevent this, **mutexes** are used. **Mutexes** block a piece of code, allowing only one **thread** at a time to execute that piece of code, similar to how a toilet key is used.\n\nIn the context of the given example:\n* Each fork has its own **mutex**, which can be locked when a philosopher takes it.\n* There is also a **mutex** shared by all the philosophers, ensuring that text is printed without mixing.\n\n#### STRATEGY\n\u003eTo prevent conflicts and ensure proper execution, the following strategies are employed:\n\nMake even or odd philosophers start with a delay.** If all philosophers start at the same time and take their right fork, none of them will be able to eat.\n\n```c\nif (ph-\u003eid % 2 == 0)\n  ft_usleep(ph-\u003epa-\u003eeat / 10);\n```\n \nEach philosopher has their fork on the left (`left_fork`) and borrows the fork from their right neighbour using a pointer (`*right_fork`) that points to the left fork of the neighbour on the right.\n \n```c\nwhile (i \u003c p-\u003ea.total)\n{\n  p-\u003eph[i].id = i + 1;\n  // Each philosopher has their fork on the left\n  pthread_mutex_init(\u0026p-\u003eph[i].left_fork, NULL);\n  if (i == p-\u003ea.total - 1)\n    // Borrow the fork from the right neighbour if the philosopher is the last one\n    p-\u003eph[i].right_fork = \u0026p-\u003eph[0].left_fork;\n  else\n    // Borrow the fork from the right neighbor\n    p-\u003eph[i].right_fork = \u0026p-\u003eph[i + 1].left_fork;\n  i++;\n}\n```\n \nDeath checking is performed in a separate **thread** to ensure timely detection. If the main **thread** continuously checks for death, it can significantly impact performance. So, when a philosopher performs their activities, a separate **thread** is launched to check if any philosopher has died. This **thread** sleeps for the duration specified by `time_to_die` and then checks if the philosopher is still alive.\n \n```c\npthread_create(\u0026ph-\u003ethread_death_id, NULL, is_dead, data);\nvoid *is_dead(void *data)\n{\n  ft_usleep(ph-\u003epa-\u003edie + 1);\n  if (!check_death(ph, 0) \u0026\u0026 !ph-\u003efinish \u0026\u0026 ((actual_time() - ph-\u003ems_eat) \u003e= (long)(ph-\u003epa-\u003edie)))\n{\n// The philosopher is dead\n```\n \n#### TIME MANAGEMENT\n\u003e Time can be managed using the following conversions:\n\n| Second | Millisecond | Microsecond |\n| :-- | :-- | :-- |\n| 1     | 1000 | 1e+6 |\n| 0.001 | 1    | 1000 |\n \nThe `gettimeofday` function is used to get the current time, which is stored in a timeval structure. The following example demonstrates how `gettimeofday` works:\n```c\nstruct timeval current_time;\ngettimeofday(\u0026current_time, NULL);\nprintf(\"seconds : %ld\\nmicro seconds : %d\", current_time.tv_sec, current_time.tv_usec);\n```\n \nTo get the current time in milliseconds using `gettimeofday`, the following function can be used:\n```c\nlong int actual_time(void)\n{\n  long int time;\n  struct timeval current_time;\n  time = 0;\n  if (gettimeofday(\u0026current_time, NULL) == -1)\n    ft_exit(\"Gettimeofday returned -1\\n\");\n  //time in milliseconds\n  time = (current_time.tv_sec * 1000) + (current_time.tv_usec / 1000);\n  return (time);\n}\n```\n \nA custom `ft_usleep` function is created to provide more precise control over the sleep time compared to the actual `usleep` function, which waits at least the specified time. The custom function repeatedly checks the time difference until the desired time has passed.\n```c\nvoid ft_usleep(long int time_in_ms)\n{\n  long int start_time;\n  start_time = 0;\n  start_time = actual_time();\n  while ((actual_time() - start_time) \u003c time_in_ms)\n    usleep(time_in_ms / 10);\n}\n````\n \n#### DATA RACES\nA **data race** occurs when two or more **threads** within a single process concurrently access the same memory location, with at least one of the accesses being a write operation, and no exclusive locks are used to control the accesses. **Data races** can lead to a non-deterministic order of accesses and produce different results from run to run. While some **data races** may be harmless, many are bugs in the program.\n\nTo fix **data races**, the option `-g fsanitize=thread` can be used.\n\nThe tools `valgrind --tool=helgrind` or `valgrind --tool=drd` can be utilized to detect any missing or misused **mutexes**. Warnings or errors from these tools indicate potential issues that should be manually checked. Such issues are often signs of a problematic project, even if it appears to be working.\n\n- `detached` refers to a **thread** that cleans its memory as soon as it finishes. It is essential to ensure that the main **thread** does not terminate before the detached **thread** completes its execution.\n- `reachable` refers to a **thread** that does not destroy its memory when it finishes. The `pthread_join` function can be used to block the execution until the **thread** finishes.\n \n## EXAMPLES\n\u003e The performance will change if you use `-fsanitize` and `valgrind` or both together.\n \n| Example | Expected Result |\n| :-- | :-- |\n| `./philo 1 200 200 200`           | Philosopher 1 takes a fork and dies after 200 ms.              |\n| `./philo 2 800 200 200`           | No philosopher dies.                                           |\n| `./philo 5 800 200 200`           | No philosopher dies.                                           |\n| `./philo 5 800 200 200 7`         | The program stops when each philosopher has eaten 7 times.     |\n| `./philo 4 410 200 200`           | No philosopher dies.                                           |\n| `./philo 4 310 200 200`           | A philosopher dies.                                            |\n| `./philo 4 500 200 1.2`           | Invalid argument.                                              |\n| `./philo 4 0 200 200`             | Invalid argument.                                              |\n| `./philo 4 -500 200 200`          | Invalid argument.                                              |\n| `./philo 4 500 200 2147483647`    | A philosopher dies after 500 ms                                |\n| `./philo 4 2147483647 200 200`    | No philosopher dies.                                           |\n| `./philo 4 214748364732 200 200`  | Invalid argument.                                              |\n| `./philo 4 200 210 200`           | A philosopher dies, it should display the death before 210 ms. |\n| `./philo 5 800 200 150`           | No philosopher dies.                                           |\n| `./philo 3 610 200 80`            | No philosopher dies.                                           |\n \n## NORMINETTE\n\u003e At 42 School, it is expected that almost every project is written following the Norm, which is the coding standard of the school.\n\n```\n- No for, do...while, switch, case, goto, ternary operators, or variable-length arrays allowed;\n- Each function must be a maximum of 25 lines, not counting the function's curly brackets;\n- Each line must be at most 80 columns wide, with comments included;\n- A function can take 4 named parameters maximum;\n- No assigns and declarations in the same line (unless static);\n- You can't declare more than 5 variables per function;\n- ...\n```\n\n* [42 Norms](https://github.com/42School/norminette/blob/master/pdf/en.norm.pdf) - Information about 42 code norms. `PDF`\n* [Norminette](https://github.com/42School/norminette) - Tool to respect the code norm, made by 42. `GitHub`\n* [42 Header](https://github.com/42Paris/42header) - 42 header for Vim. `GitHub`\n\n## CONTRIBUTING\n\nIf you find any issues or have suggestions for improvements, feel free to fork the repository and open an issue or submit a pull request.\n\n## LICENSE\n\nThis project is available under the MIT License. For further details, please refer to the [LICENSE](https://github.com/jotavare/philosophers/blob/master/LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjotavare%2Fphilosophers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjotavare%2Fphilosophers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjotavare%2Fphilosophers/lists"}