event/cnxk: fix clang build on Arm
[dpdk.git] / examples / performance-thread / pthread_shim / main.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2015 Intel Corporation
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <inttypes.h>
9 #include <sys/types.h>
10 #include <string.h>
11 #include <sys/queue.h>
12 #include <stdarg.h>
13 #include <errno.h>
14 #include <getopt.h>
15 #include <unistd.h>
16 #include <sched.h>
17 #include <pthread.h>
18
19 #include <rte_common.h>
20 #include <rte_lcore.h>
21 #include <rte_per_lcore.h>
22 #include <rte_timer.h>
23
24 #include "lthread_api.h"
25 #include "lthread_diag_api.h"
26 #include "pthread_shim.h"
27
28 #define DEBUG_APP 0
29 #define HELLOW_WORLD_MAX_LTHREADS 10
30 #define THREAD_NAME_LEN 16
31
32 #ifndef __GLIBC__ /* sched_getcpu() is glibc-specific */
33 #define sched_getcpu() rte_lcore_id()
34 #endif
35
36 __thread int print_count;
37 __thread pthread_mutex_t print_lock;
38
39 __thread pthread_mutex_t exit_lock;
40 __thread pthread_cond_t exit_cond;
41
42 /*
43  * A simple thread that demonstrates use of a mutex, a condition
44  * variable, thread local storage, explicit yield, and thread exit.
45  *
46  * The thread uses a mutex to protect a shared counter which is incremented
47  * and then it waits on condition variable before exiting.
48  *
49  * The thread argument is stored in and retrieved from TLS, using
50  * the pthread key create, get and set specific APIs.
51  *
52  * The thread yields while holding the mutex, to provide opportunity
53  * for other threads to contend.
54  *
55  * All of the pthread API functions used by this thread are actually
56  * resolved to corresponding lthread functions by the pthread shim
57  * implemented in pthread_shim.c
58  */
59 void *helloworld_pthread(void *arg);
60 void *helloworld_pthread(void *arg)
61 {
62         pthread_key_t key;
63
64         /* create a key for TLS */
65         pthread_key_create(&key, NULL);
66
67         /* store the arg in TLS */
68         pthread_setspecific(key, arg);
69
70         /* grab lock and increment shared counter */
71         pthread_mutex_lock(&print_lock);
72         print_count++;
73
74         /* yield thread to give opportunity for lock contention */
75         pthread_yield();
76
77         /* retrieve arg from TLS */
78         uint64_t thread_no = (uint64_t) pthread_getspecific(key);
79
80         printf("Hello - lcore = %d count = %d thread_no = %d thread_id = %p\n",
81                         sched_getcpu(),
82                         print_count,
83                         (int) thread_no,
84                         (void *)pthread_self());
85
86         /* release the lock */
87         pthread_mutex_unlock(&print_lock);
88
89         /*
90          * wait on condition variable
91          * before exiting
92          */
93         pthread_mutex_lock(&exit_lock);
94         pthread_cond_wait(&exit_cond, &exit_lock);
95         pthread_mutex_unlock(&exit_lock);
96
97         /* exit */
98         pthread_exit((void *) thread_no);
99 }
100
101
102 /*
103  * This is the initial thread
104  *
105  * It demonstrates pthread, mutex and condition variable creation,
106  * broadcast and pthread join APIs.
107  *
108  * This initial thread must always start life as an lthread.
109  *
110  * This thread creates many more threads then waits a short time
111  * before signalling them to exit using a broadcast.
112  *
113  * All of the pthread API functions used by this thread are actually
114  * resolved to corresponding lthread functions by the pthread shim
115  * implemented in pthread_shim.c
116  *
117  * After all threads have finished the lthread scheduler is shutdown
118  * and normal pthread operation is restored
119  */
120 __thread pthread_t tid[HELLOW_WORLD_MAX_LTHREADS];
121
122 static void *initial_lthread(void *args __rte_unused)
123 {
124         int lcore = (int) rte_lcore_id();
125         /*
126          *
127          * We can now enable pthread API override
128          * and start to use the pthread APIs
129          */
130         pthread_override_set(1);
131
132         uint64_t i;
133         int ret;
134
135         /* initialize mutex for shared counter */
136         print_count = 0;
137         pthread_mutex_init(&print_lock, NULL);
138
139         /* initialize mutex and condition variable controlling thread exit */
140         pthread_mutex_init(&exit_lock, NULL);
141         pthread_cond_init(&exit_cond, NULL);
142
143         /* spawn a number of threads */
144         for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
145
146                 /*
147                  * Not strictly necessary but
148                  * for the sake of this example
149                  * use an attribute to pass the desired lcore
150                  */
151                 pthread_attr_t attr;
152                 rte_cpuset_t cpuset;
153                 char name[THREAD_NAME_LEN];
154
155                 CPU_ZERO(&cpuset);
156                 CPU_SET(lcore, &cpuset);
157                 pthread_attr_init(&attr);
158                 pthread_attr_setaffinity_np(&attr, sizeof(rte_cpuset_t), &cpuset);
159
160                 /* create the thread */
161                 ret = pthread_create(&tid[i], &attr,
162                                 helloworld_pthread, (void *) i);
163                 if (ret != 0)
164                         rte_exit(EXIT_FAILURE, "Cannot create helloworld thread\n");
165
166                 snprintf(name, sizeof(name), "helloworld-%u", (uint32_t)i);
167                 rte_thread_setname(tid[i], name);
168         }
169
170         /* wait for 1s to allow threads
171          * to block on the condition variable
172          * N.B. nanosleep() is resolved to lthread_sleep()
173          * by the shim.
174          */
175         struct timespec time;
176
177         time.tv_sec = 1;
178         time.tv_nsec = 0;
179         nanosleep(&time, NULL);
180
181         /* wake up all the threads */
182         pthread_cond_broadcast(&exit_cond);
183
184         /* wait for them to finish */
185         for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
186
187                 uint64_t thread_no;
188
189                 pthread_join(tid[i], (void *) &thread_no);
190                 if (thread_no != i)
191                         printf("error on thread exit\n");
192         }
193
194         pthread_cond_destroy(&exit_cond);
195         pthread_mutex_destroy(&print_lock);
196         pthread_mutex_destroy(&exit_lock);
197
198         /* shutdown the lthread scheduler */
199         lthread_scheduler_shutdown(rte_lcore_id());
200         lthread_detach();
201         return NULL;
202 }
203
204
205
206 /* This thread creates a single initial lthread
207  * and then runs the scheduler
208  * An instance of this thread is created on each thread
209  * in the core mask
210  */
211 static int
212 lthread_scheduler(void *args __rte_unused)
213 {
214         /* create initial thread  */
215         struct lthread *lt;
216
217         lthread_create(&lt, -1, initial_lthread, (void *) NULL);
218
219         /* run the lthread scheduler */
220         lthread_run();
221
222         /* restore genuine pthread operation */
223         pthread_override_set(0);
224         return 0;
225 }
226
227 int main(int argc, char **argv)
228 {
229         int num_sched = 0;
230
231         /* basic DPDK initialization is all that is necessary to run lthreads*/
232         int ret = rte_eal_init(argc, argv);
233
234         if (ret < 0)
235                 rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
236
237         /* enable timer subsystem */
238         rte_timer_subsystem_init();
239
240 #if DEBUG_APP
241         lthread_diagnostic_set_mask(LT_DIAG_ALL);
242 #endif
243
244         /* create a scheduler on every core in the core mask
245          * and launch an initial lthread that will spawn many more.
246          */
247         unsigned lcore_id;
248
249         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
250                 if (rte_lcore_is_enabled(lcore_id))
251                         num_sched++;
252         }
253
254         /* set the number of schedulers, this forces all schedulers synchronize
255          * before entering their main loop
256          */
257         lthread_num_schedulers_set(num_sched);
258
259         /* launch all threads */
260         rte_eal_mp_remote_launch(lthread_scheduler, (void *)NULL, CALL_MAIN);
261
262         /* wait for threads to stop */
263         RTE_LCORE_FOREACH_WORKER(lcore_id) {
264                 rte_eal_wait_lcore(lcore_id);
265         }
266
267         /* clean up the EAL */
268         rte_eal_cleanup();
269
270         return 0;
271 }