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