1 /* This is a small demo of the high-performance ThreadX kernel.  It includes examples of eight
2    threads of different priorities, using a message queue, semaphore, mutex, event flags group,
3    byte pool, and block pool.  */
4 
5 #include   "tx_api.h"
6 #include   <stdio.h>
7 
8 #define     DEMO_STACK_SIZE         1024
9 #define     DEMO_BYTE_POOL_SIZE     9120
10 #define     DEMO_BLOCK_POOL_SIZE    100
11 #define     DEMO_QUEUE_SIZE         100
12 
13 
14 /* Define the ThreadX object control blocks...  */
15 
16 TX_THREAD               thread_0;
17 TX_THREAD               thread_1;
18 TX_THREAD               thread_2;
19 TX_THREAD               thread_3;
20 TX_THREAD               thread_4;
21 TX_THREAD               thread_5;
22 TX_THREAD               thread_6;
23 TX_THREAD               thread_7;
24 TX_QUEUE                queue_0;
25 TX_SEMAPHORE            semaphore_0;
26 TX_MUTEX                mutex_0;
27 TX_EVENT_FLAGS_GROUP    event_flags_0;
28 TX_BYTE_POOL            byte_pool_0;
29 TX_BLOCK_POOL           block_pool_0;
30 
31 
32 /* Define the counters used in the demo application...  */
33 
34 ULONG           thread_0_counter;
35 ULONG           thread_1_counter;
36 ULONG           thread_1_messages_sent;
37 ULONG           thread_2_counter;
38 ULONG           thread_2_messages_received;
39 ULONG           thread_3_counter;
40 ULONG           thread_4_counter;
41 ULONG           thread_5_counter;
42 ULONG           thread_6_counter;
43 ULONG           thread_7_counter;
44 
45 
46 /* Define thread prototypes.  */
47 
48 void    thread_0_entry(ULONG thread_input);
49 void    thread_1_entry(ULONG thread_input);
50 void    thread_2_entry(ULONG thread_input);
51 void    thread_3_and_4_entry(ULONG thread_input);
52 void    thread_5_entry(ULONG thread_input);
53 void    thread_6_and_7_entry(ULONG thread_input);
54 
55 
56 /* Define main entry point.  */
57 
main()58 int main()
59 {
60 
61     /* Enter the ThreadX kernel.  */
62     tx_kernel_enter();
63 }
64 
65 
66 /* Define what the initial system looks like.  */
67 
tx_application_define(void * first_unused_memory)68 void    tx_application_define(void *first_unused_memory)
69 {
70 
71 CHAR    *pointer = TX_NULL;
72 
73     /* Create a byte memory pool from which to allocate the thread stacks.  */
74     tx_byte_pool_create(&byte_pool_0, "byte pool 0", first_unused_memory, DEMO_BYTE_POOL_SIZE);
75 
76     /* Put system definition stuff in here, e.g. thread creates and other assorted
77        create information.  */
78 
79     /* Allocate the stack for thread 0.  */
80     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
81 
82     /* Create the main thread.  */
83     tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
84             pointer, DEMO_STACK_SIZE,
85             1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
86 
87 
88     /* Allocate the stack for thread 1.  */
89     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
90 
91     /* Create threads 1 and 2. These threads pass information through a ThreadX
92        message queue.  It is also interesting to note that these threads have a time
93        slice.  */
94     tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1,
95             pointer, DEMO_STACK_SIZE,
96             16, 16, 4, TX_AUTO_START);
97 
98     /* Allocate the stack for thread 2.  */
99     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
100 
101     tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2,
102             pointer, DEMO_STACK_SIZE,
103             16, 16, 4, TX_AUTO_START);
104 
105     /* Allocate the stack for thread 3.  */
106     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
107 
108     /* Create threads 3 and 4.  These threads compete for a ThreadX counting semaphore.
109        An interesting thing here is that both threads share the same instruction area.  */
110     tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3,
111             pointer, DEMO_STACK_SIZE,
112             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
113 
114     /* Allocate the stack for thread 4.  */
115     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
116 
117     tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4,
118             pointer, DEMO_STACK_SIZE,
119             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
120 
121     /* Allocate the stack for thread 5.  */
122     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
123 
124     /* Create thread 5.  This thread simply pends on an event flag which will be set
125        by thread_0.  */
126     tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5,
127             pointer, DEMO_STACK_SIZE,
128             4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
129 
130     /* Allocate the stack for thread 6.  */
131     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
132 
133     /* Create threads 6 and 7.  These threads compete for a ThreadX mutex.  */
134     tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6,
135             pointer, DEMO_STACK_SIZE,
136             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
137 
138     /* Allocate the stack for thread 7.  */
139     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
140 
141     tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7,
142             pointer, DEMO_STACK_SIZE,
143             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
144 
145     /* Allocate the message queue.  */
146     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT);
147 
148     /* Create the message queue shared by threads 1 and 2.  */
149     tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
150 
151     /* Create the semaphore used by threads 3 and 4.  */
152     tx_semaphore_create(&semaphore_0, "semaphore 0", 1);
153 
154     /* Create the event flags group used by threads 1 and 5.  */
155     tx_event_flags_create(&event_flags_0, "event flags 0");
156 
157     /* Create the mutex used by thread 6 and 7 without priority inheritance.  */
158     tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT);
159 
160     /* Allocate the memory for a small block pool.  */
161     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT);
162 
163     /* Create a block memory pool to allocate a message buffer from.  */
164     tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE);
165 
166     /* Allocate a block and release the block memory.  */
167     tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT);
168 
169     /* Release the block back to the pool.  */
170     tx_block_release(pointer);
171 }
172 
173 
174 
175 /* Define the test threads.  */
176 
thread_0_entry(ULONG thread_input)177 void    thread_0_entry(ULONG thread_input)
178 {
179 
180 UINT    status;
181 
182 
183     /* This thread simply sits in while-forever-sleep loop.  */
184     while(1)
185     {
186 
187         /* Increment the thread counter.  */
188         thread_0_counter++;
189 
190         /* Print results.  */
191         printf("**** ThreadX Linux Demonstration **** (c) 1996-2020 Microsoft Corporation\n\n");
192         printf("           thread 0 events sent:          %lu\n", thread_0_counter);
193         printf("           thread 1 messages sent:        %lu\n", thread_1_counter);
194         printf("           thread 2 messages received:    %lu\n", thread_2_counter);
195         printf("           thread 3 obtained semaphore:   %lu\n", thread_3_counter);
196         printf("           thread 4 obtained semaphore:   %lu\n", thread_4_counter);
197         printf("           thread 5 events received:      %lu\n", thread_5_counter);
198         printf("           thread 6 mutex obtained:       %lu\n", thread_6_counter);
199         printf("           thread 7 mutex obtained:       %lu\n\n", thread_7_counter);
200 
201         /* Sleep for 10 ticks.  */
202         tx_thread_sleep(10);
203 
204         /* Set event flag 0 to wakeup thread 5.  */
205         status =  tx_event_flags_set(&event_flags_0, 0x1, TX_OR);
206 
207         /* Check status.  */
208         if (status != TX_SUCCESS)
209             break;
210     }
211 }
212 
213 
thread_1_entry(ULONG thread_input)214 void    thread_1_entry(ULONG thread_input)
215 {
216 
217 UINT    status;
218 
219 
220     /* This thread simply sends messages to a queue shared by thread 2.  */
221     while(1)
222     {
223 
224         /* Increment the thread counter.  */
225         thread_1_counter++;
226 
227         /* Send message to queue 0.  */
228         status =  tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER);
229 
230         /* Check completion status.  */
231         if (status != TX_SUCCESS)
232             break;
233 
234         /* Increment the message sent.  */
235         thread_1_messages_sent++;
236     }
237 }
238 
239 
thread_2_entry(ULONG thread_input)240 void    thread_2_entry(ULONG thread_input)
241 {
242 
243 ULONG   received_message;
244 UINT    status;
245 
246     /* This thread retrieves messages placed on the queue by thread 1.  */
247     while(1)
248     {
249 
250         /* Increment the thread counter.  */
251         thread_2_counter++;
252 
253         /* Retrieve a message from the queue.  */
254         status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER);
255 
256         /* Check completion status and make sure the message is what we
257            expected.  */
258         if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received))
259             break;
260 
261         /* Otherwise, all is okay.  Increment the received message count.  */
262         thread_2_messages_received++;
263     }
264 }
265 
266 
thread_3_and_4_entry(ULONG thread_input)267 void    thread_3_and_4_entry(ULONG thread_input)
268 {
269 
270 UINT    status;
271 
272 
273     /* This function is executed from thread 3 and thread 4.  As the loop
274        below shows, these function compete for ownership of semaphore_0.  */
275     while(1)
276     {
277 
278         /* Increment the thread counter.  */
279         if (thread_input == 3)
280             thread_3_counter++;
281         else
282             thread_4_counter++;
283 
284         /* Get the semaphore with suspension.  */
285         status =  tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER);
286 
287         /* Check status.  */
288         if (status != TX_SUCCESS)
289             break;
290 
291         /* Sleep for 2 ticks to hold the semaphore.  */
292         tx_thread_sleep(2);
293 
294         /* Release the semaphore.  */
295         status =  tx_semaphore_put(&semaphore_0);
296 
297         /* Check status.  */
298         if (status != TX_SUCCESS)
299             break;
300     }
301 }
302 
303 
thread_5_entry(ULONG thread_input)304 void    thread_5_entry(ULONG thread_input)
305 {
306 
307 UINT    status;
308 ULONG   actual_flags;
309 
310 
311     /* This thread simply waits for an event in a forever loop.  */
312     while(1)
313     {
314 
315         /* Increment the thread counter.  */
316         thread_5_counter++;
317 
318         /* Wait for event flag 0.  */
319         status =  tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR,
320                                                 &actual_flags, TX_WAIT_FOREVER);
321 
322         /* Check status.  */
323         if ((status != TX_SUCCESS) || (actual_flags != 0x1))
324             break;
325     }
326 }
327 
328 
thread_6_and_7_entry(ULONG thread_input)329 void    thread_6_and_7_entry(ULONG thread_input)
330 {
331 
332 UINT    status;
333 
334 
335     /* This function is executed from thread 6 and thread 7.  As the loop
336        below shows, these function compete for ownership of mutex_0.  */
337     while(1)
338     {
339 
340         /* Increment the thread counter.  */
341         if (thread_input == 6)
342             thread_6_counter++;
343         else
344             thread_7_counter++;
345 
346         /* Get the mutex with suspension.  */
347         status =  tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);
348 
349         /* Check status.  */
350         if (status != TX_SUCCESS)
351             break;
352 
353         /* Get the mutex again with suspension.  This shows
354            that an owning thread may retrieve the mutex it
355            owns multiple times.  */
356         status =  tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);
357 
358         /* Check status.  */
359         if (status != TX_SUCCESS)
360             break;
361 
362         /* Sleep for 2 ticks to hold the mutex.  */
363         tx_thread_sleep(2);
364 
365         /* Release the mutex.  */
366         status =  tx_mutex_put(&mutex_0);
367 
368         /* Check status.  */
369         if (status != TX_SUCCESS)
370             break;
371 
372         /* Release the mutex again.  This will actually
373            release ownership since it was obtained twice.  */
374         status =  tx_mutex_put(&mutex_0);
375 
376         /* Check status.  */
377         if (status != TX_SUCCESS)
378             break;
379     }
380 }
381