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