1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 *
10 * Dining philosophers demo
11 *
12 * The demo can be configured to use SEMAPHORES or MUTEXES as object types for its
13 * synchronization. To configure a specific object, set the value of FORKS to one
14 * of these.
15 *
16 * By default, the demo uses MUTEXES.
17 *
18 * The demo can be configured to work with threads of the same priority or
19 * not. If using different priorities of preemptible threads; if using one
20 * priority, there will be six preemptible threads of priority normal.
21 *
22 * By default, the demo uses different priorities.
23 *
24 * The number of threads is set via NUM_PHIL. The demo has only been tested
25 * with six threads. In theory it should work with any number of threads, but
26 * not without making changes to the thread attributes and corresponding kernel
27 * object attributes array in the phil_obj_abstract.h
28 * header file.
29 */
30
31 #include <zephyr/kernel.h>
32 #include <cmsis_os2.h>
33 #include <zephyr/sys/printk.h>
34
35 #include <zephyr/sys/__assert.h>
36
37 #include "phil_obj_abstract.h"
38
39 #define SEMAPHORES 1
40 #define MUTEXES 2
41
42 /**************************************/
43 /* control the behaviour of the demo **/
44
45 #ifndef DEBUG_PRINTF
46 #define DEBUG_PRINTF 0
47 #endif
48
49 #ifndef FORKS
50 #define FORKS MUTEXES
51 #if 0
52 #define FORKS SEMAPHORES
53 #endif
54 #endif
55
56 #ifndef SAME_PRIO
57 #define SAME_PRIO 0
58 #endif
59
60 #ifndef NUM_PHIL
61 #define NUM_PHIL 6
62 #endif
63
64 /* end - control behaviour of the demo */
65 /***************************************/
66 osSemaphoreId_t forks[NUM_PHIL];
67
68 #define fork(x) (forks[x])
69
70 #define STACK_SIZE CONFIG_CMSIS_V2_THREAD_MAX_STACK_SIZE
71 static K_THREAD_STACK_ARRAY_DEFINE(stacks, NUM_PHIL, STACK_SIZE);
72 static osThreadAttr_t thread_attr[] = {
73 {
74 .name = "Thread0",
75 .stack_mem = &stacks[0][0],
76 .stack_size = STACK_SIZE,
77 .priority = osPriorityHigh
78 },
79 {
80 .name = "Thread1",
81 .stack_mem = &stacks[1][0],
82 .stack_size = STACK_SIZE,
83 .priority = osPriorityHigh
84 },
85 {
86 .name = "Thread2",
87 .stack_mem = &stacks[2][0],
88 .stack_size = STACK_SIZE,
89 .priority = osPriorityHigh
90 },
91 {
92 .name = "Thread3",
93 .stack_mem = &stacks[3][0],
94 .stack_size = STACK_SIZE,
95 .priority = osPriorityHigh
96 },
97 {
98 .name = "Thread4",
99 .stack_mem = &stacks[4][0],
100 .stack_size = STACK_SIZE,
101 .priority = osPriorityHigh
102 },
103 {
104 .name = "Thread5",
105 .stack_mem = &stacks[5][0],
106 .stack_size = STACK_SIZE,
107 .priority = osPriorityHigh
108 }
109 };
110
111
112 #if DEBUG_PRINTF
113 #define PR_DEBUG printk
114 #else
115 #define PR_DEBUG(...)
116 #endif
117
118 #include "phil_obj_abstract.h"
119
set_phil_state_pos(int id)120 static void set_phil_state_pos(int id)
121 {
122 #if !DEBUG_PRINTF
123 printk("\x1b[%d;%dH", id + 1, 1);
124 #endif
125 }
126
127 #include <stdarg.h>
print_phil_state(int id,const char * fmt,int32_t delay)128 static void print_phil_state(int id, const char *fmt, int32_t delay)
129 {
130 int prio = osThreadGetPriority(osThreadGetId());
131
132 set_phil_state_pos(id);
133
134 printk("Philosopher %d [%s:%s%d] ",
135 id, prio < 0 ? "C" : "P",
136 prio < 0 ? "" : " ",
137 prio);
138
139 if (delay) {
140 printk(fmt, delay < 1000 ? " " : "", delay);
141 } else {
142 printk(fmt, "");
143 }
144
145 printk("\n");
146 }
147
get_random_delay(int id,int period_in_ms)148 static int32_t get_random_delay(int id, int period_in_ms)
149 {
150 /*
151 * The random delay is unit-less, and is based on the philosopher's ID
152 * and the current uptime to create some pseudo-randomness. It produces
153 * a value between 0 and 31.
154 */
155 int32_t delay = (k_uptime_get_32()/100 * (id + 1)) & 0x1f;
156
157 /* add 1 to not generate a delay of 0 */
158 int32_t ms = (delay + 1) * period_in_ms;
159
160 return ms;
161 }
162
is_last_philosopher(int id)163 static inline int is_last_philosopher(int id)
164 {
165 return id == (NUM_PHIL - 1);
166 }
167
philosopher(void * id)168 void philosopher(void *id)
169 {
170 fork_t fork1;
171 fork_t fork2;
172
173 int my_id = POINTER_TO_INT(id);
174
175 /* Djkstra's solution: always pick up the lowest numbered fork first */
176 if (is_last_philosopher(my_id)) {
177 fork1 = fork(0);
178 fork2 = fork(my_id);
179 } else {
180 fork1 = fork(my_id);
181 fork2 = fork(my_id + 1);
182 }
183
184 while (1) {
185 int32_t delay;
186
187 print_phil_state(my_id, " STARVING ", 0);
188 take(fork1);
189 print_phil_state(my_id, " HOLDING ONE FORK ", 0);
190 take(fork2);
191
192 delay = get_random_delay(my_id, 25);
193 print_phil_state(my_id, " EATING [ %s%d ms ] ", delay);
194 osDelay(delay);
195
196 drop(fork2);
197 print_phil_state(my_id, " DROPPED ONE FORK ", 0);
198 drop(fork1);
199
200 delay = get_random_delay(my_id, 25);
201 print_phil_state(my_id, " THINKING [ %s%d ms ] ", delay);
202 osDelay(delay);
203 }
204
205 }
206
new_prio(int phil)207 static int new_prio(int phil)
208 {
209 #if SAME_PRIO
210 return osPriorityNormal;
211 #else
212 return osPriorityLow + phil;
213 #endif
214 }
215
init_objects(void)216 static void init_objects(void)
217 {
218 for (int i = 0; i < NUM_PHIL; i++) {
219 forks[i] = fork_init(i);
220 }
221 }
222
start_threads(void)223 static void start_threads(void)
224 {
225 for (int i = 0; i < NUM_PHIL; i++) {
226 int prio = new_prio(i);
227 thread_attr[i].priority = prio;
228 osThreadNew(philosopher, INT_TO_POINTER(i), &thread_attr[i]);
229 }
230 }
231
232 #define DEMO_DESCRIPTION \
233 "\x1b[2J\x1b[15;1H" \
234 "Demo Description\n" \
235 "----------------\n" \
236 "An implementation of a solution to the Dining Philosophers\n" \
237 "problem (a classic multi-thread synchronization problem) using\n" \
238 "CMSIS RTOS V2 APIs. This particular implementation demonstrates the\n" \
239 "usage of multiple preemptible threads of differing\n" \
240 "priorities, as well as %s and thread sleeping.\n", fork_type_str
241
display_demo_description(void)242 static void display_demo_description(void)
243 {
244 #if !DEBUG_PRINTF
245 printk(DEMO_DESCRIPTION);
246 #endif
247 }
248
main(void)249 int main(void)
250 {
251 display_demo_description();
252 init_objects();
253 start_threads();
254
255 #ifdef CONFIG_COVERAGE
256 /* Wait a few seconds before main() exit, giving the sample the
257 * opportunity to dump some output before coverage data gets emitted
258 */
259 k_sleep(K_MSEC(5000));
260 #endif
261 return 0;
262 }
263