1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  */
9 /* Multi-thread searching.
10    Illustrates: thread cancellation, cleanup handlers. */
11 
12 #include <sys/errno.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <sys/types.h>
17 #include <pthread.h>
18 
19 /* Defines the number of searching threads */
20 #define NUM_THREADS 5
21 
22 /* Function prototypes */
23 void *search(void *);
24 void print_it(void *);
25 
26 /* Global variables */
27 pthread_t threads[NUM_THREADS];
28 pthread_mutex_t lock;
29 int tries;
30 volatile int started;
31 
libc_ex3()32 int libc_ex3()
33 {
34   int i;
35   int pid;
36 
37   /* create a number to search for */
38   pid = getpid();
39   printf("Searching for the number = %d...\n", pid);
40 
41   /* Initialize the mutex lock */
42   pthread_mutex_init(&lock, NULL);
43 
44   /* Create the searching threads */
45   for (started=0; started<NUM_THREADS; started++)
46     pthread_create(&threads[started], NULL, search, (void *)pid);
47 
48   /* Wait for (join) all the searching threads */
49   for (i=0; i<NUM_THREADS; i++)
50     pthread_join(threads[i], NULL);
51 
52   printf("It took %d tries to find the number.\n", tries);
53 
54   /* Exit the program */
55   return 0;
56 }
57 #include <finsh.h>
58 FINSH_FUNCTION_EXPORT(libc_ex3, example 5 for libc);
59 
60 /* This is the cleanup function that is called
61    when the threads are cancelled */
62 
print_it(void * arg)63 void print_it(void *arg)
64 {
65   int *try = (int *) arg;
66   pthread_t tid;
67 
68   /* Get the calling thread's ID */
69   tid = pthread_self();
70 
71   /* Print where the thread was in its search when it was cancelled */
72   printf("Thread %lx was canceled on its %d try.\n", tid, *try);
73 }
74 
75 /* This is the search routine that is executed in each thread */
76 
search(void * arg)77 void *search(void *arg)
78 {
79   int num = (int) arg;
80   int i, j, ntries;
81   pthread_t tid;
82 
83   /* get the calling thread ID */
84   tid = pthread_self();
85 
86   /* use the thread ID to set the seed for the random number generator */
87   /* Since srand and rand are not thread-safe, serialize with lock */
88 
89   /* Try to lock the mutex lock --
90      if locked, check to see if the thread has been cancelled
91      if not locked then continue */
92   while (pthread_mutex_trylock(&lock) == EBUSY)
93     pthread_testcancel();
94 
95   srand((int)tid);
96   i = rand() & 0xFFFFFF;
97   pthread_mutex_unlock(&lock);
98   ntries = 0;
99 
100   /* Set the cancellation parameters --
101      - Enable thread cancellation
102      - Defer the action of the cancellation */
103 
104   pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
105   pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
106 
107   while (started < NUM_THREADS)
108     sched_yield ();
109 
110   /* Push the cleanup routine (print_it) onto the thread
111      cleanup stack.  This routine will be called when the
112      thread is cancelled.  Also note that the pthread_cleanup_push
113      call must have a matching pthread_cleanup_pop call.  The
114      push and pop calls MUST be at the same lexical level
115      within the code */
116 
117   /* Pass address of `ntries' since the current value of `ntries' is not
118      the one we want to use in the cleanup function */
119 
120   pthread_cleanup_push(print_it, (void *)&ntries);
121 
122   /* Loop forever */
123   while (1) {
124     i = (i + 1) & 0xFFFFFF;
125     ntries++;
126 
127     /* Does the random number match the target number? */
128     if (num == i) {
129       /* Try to lock the mutex lock --
130          if locked, check to see if the thread has been cancelled
131          if not locked then continue */
132       while (pthread_mutex_trylock(&lock) == EBUSY)
133         pthread_testcancel();
134 
135       /* Set the global variable for the number of tries */
136       tries = ntries;
137       printf("Thread %lx found the number!\n", tid);
138 
139       /* Cancel all the other threads */
140       for (j=0; j<NUM_THREADS; j++)
141         if (threads[j] != tid) pthread_cancel(threads[j]);
142 
143       /* Break out of the while loop */
144       break;
145     }
146 
147     /* Every 100 tries check to see if the thread has been cancelled. */
148     if (ntries % 100 == 0) {
149       pthread_testcancel();
150     }
151   }
152 
153   /* The only way we can get here is when the thread breaks out
154      of the while loop.  In this case the thread that makes it here
155      has found the number we are looking for and does not need to run
156      the thread cleanup function.  This is why the pthread_cleanup_pop
157      function is called with a 0 argument; this will pop the cleanup
158      function off the stack without executing it */
159 
160   pthread_cleanup_pop(0);
161   return((void *)0);
162 }
163