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 #include <zephyr/kernel.h>
31 #include <cmsis_os.h>
32 
33 #if defined(CONFIG_STDOUT_CONSOLE)
34 #include <stdio.h>
35 #else
36 #include <zephyr/sys/printk.h>
37 #endif
38 
39 #include <zephyr/sys/__assert.h>
40 
41 #include "phil_obj_abstract.h"
42 
43 #define SEMAPHORES 1
44 #define MUTEXES 2
45 
46 /**************************************/
47 /* control the behaviour of the demo **/
48 
49 #ifndef DEBUG_PRINTF
50 #define DEBUG_PRINTF 0
51 #endif
52 
53 #ifndef FORKS
54 #define FORKS MUTEXES
55 #if 0
56 #define FORKS SEMAPHORES
57 #endif
58 #endif
59 
60 #ifndef SAME_PRIO
61 #define SAME_PRIO 0
62 #endif
63 
64 #ifndef NUM_PHIL
65 #define NUM_PHIL 6
66 #endif
67 
68 /* end - control behaviour of the demo */
69 /***************************************/
70 osSemaphoreId forks[NUM_PHIL];
71 
72 #define fork(x) (forks[x])
73 
74 /*
75  * CMSIS limits the stack size, but qemu_x86_64, qemu_xtensa (all),
76  * qemu_leon3 and the boards such as up_squared, intel_ehl_crb,
77  * acrn_ehl_crb need 1024 to run this.
78  * For other arch and boards suggested stack size is 512.
79  */
80 #define STACK_SIZE CONFIG_CMSIS_THREAD_MAX_STACK_SIZE
81 
82 #if DEBUG_PRINTF
83 #define PR_DEBUG printk
84 #else
85 #define PR_DEBUG(...)
86 #endif
87 
88 #include "phil_obj_abstract.h"
89 
set_phil_state_pos(int id)90 static void set_phil_state_pos(int id)
91 {
92 #if !DEBUG_PRINTF
93 	printk("\x1b[%d;%dH", id + 1, 1);
94 #endif
95 }
96 
97 #include <stdarg.h>
print_phil_state(int id,const char * fmt,int32_t delay)98 static void print_phil_state(int id, const char *fmt, int32_t delay)
99 {
100 	int prio = osThreadGetPriority(osThreadGetId());
101 
102 	set_phil_state_pos(id);
103 #define STATE_LEN 80
104 	char state[STATE_LEN];
105 	int p = 0;
106 
107 	p += snprintk(state + p, STATE_LEN - p, "Philosopher %d [%s:%s%d] ",
108 		     id, prio < 0 ? "C" : "P",
109 		     prio < 0 ? "" : " ",
110 		     prio);
111 
112 	if (delay) {
113 		p += snprintk(state + p, STATE_LEN - p, fmt,
114 			      delay < 1000 ? " " : "", delay);
115 	} else {
116 		p += snprintk(state + p, STATE_LEN - p, fmt, "");
117 	}
118 
119 	p += snprintk(state + p, STATE_LEN - p, "\n");
120 	printk("%s", state);
121 }
122 
get_random_delay(int id,int period_in_ms)123 static int32_t get_random_delay(int id, int period_in_ms)
124 {
125 	/*
126 	 * The random delay is unit-less, and is based on the philosopher's ID
127 	 * and the current uptime to create some pseudo-randomness. It produces
128 	 * a value between 0 and 31.
129 	 */
130 	int32_t delay = (k_uptime_get_32() / 100 * (id + 1)) & 0x1f;
131 
132 	/* add 1 to not generate a delay of 0 */
133 	int32_t ms = (delay + 1) * period_in_ms;
134 
135 	return ms;
136 }
137 
is_last_philosopher(int id)138 static inline int is_last_philosopher(int id)
139 {
140 	return id == (NUM_PHIL - 1);
141 }
142 
philosopher(void const * id)143 void philosopher(void const *id)
144 {
145 	fork_t fork1;
146 	fork_t 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 		fork1 = fork(0);
153 		fork2 = fork(my_id);
154 	} else {
155 		fork1 = fork(my_id);
156 		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(fork1);
164 		print_phil_state(my_id, "   HOLDING ONE FORK   ", 0);
165 		take(fork2);
166 
167 		delay = get_random_delay(my_id, 25);
168 		print_phil_state(my_id, "  EATING  [ %s%d ms ] ", delay);
169 		osDelay(delay);
170 
171 		drop(fork2);
172 		print_phil_state(my_id, "   DROPPED ONE FORK   ", 0);
173 		drop(fork1);
174 
175 		delay = get_random_delay(my_id, 25);
176 		print_phil_state(my_id, " THINKING [ %s%d ms ] ", delay);
177 		osDelay(delay);
178 	}
179 
180 }
181 
182 osThreadDef(philosopher, osPriorityLow, 6, STACK_SIZE);
183 
new_prio(int phil)184 static int new_prio(int phil)
185 {
186 #if SAME_PRIO
187 	return osPriorityNormal;
188 #else
189 	return osPriorityLow + phil;
190 #endif
191 }
192 
start_threads(void)193 static void start_threads(void)
194 {
195 	osThreadId id;
196 
197 	for (int i = 0; i < NUM_PHIL; i++) {
198 		int prio = new_prio(i);
199 		id = osThreadCreate(osThread(philosopher), INT_TO_POINTER(i));
200 		osThreadSetPriority(id, prio);
201 	}
202 }
203 
204 #define DEMO_DESCRIPTION							\
205 	"\x1b[2J\x1b[15;1H"							\
206 	"Demo Description\n"							\
207 	"----------------\n"							\
208 	"An implementation of a solution to the Dining Philosophers\n"		\
209 	"problem (a classic multi-thread synchronization problem) using\n"	\
210 	"CMSIS RTOS V1 APIs. This particular implementation demonstrates the\n"	\
211 	"usage of multiple preemptible threads of differing\n"			\
212 	"priorities, as well as %s and thread sleeping.\n", fork_type_str
213 
display_demo_description(void)214 static void display_demo_description(void)
215 {
216 #if !DEBUG_PRINTF
217 	printk(DEMO_DESCRIPTION);
218 #endif
219 }
220 
main(void)221 int main(void)
222 {
223 	display_demo_description();
224 	start_threads();
225 	return 0;
226 }
227