examples/performance-thread: add pthread_shim app
[dpdk.git] / examples / performance-thread / pthread_shim / main.c
1
2 /*-
3  *   BSD LICENSE
4  *
5  *   Copyright(c) 2015 Intel Corporation. All rights reserved.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #define _GNU_SOURCE
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 #include <sys/types.h>
41 #include <string.h>
42 #include <sys/queue.h>
43 #include <stdarg.h>
44 #include <errno.h>
45 #include <getopt.h>
46 #include <unistd.h>
47 #include <sched.h>
48 #include <pthread.h>
49
50 #include <rte_config.h>
51 #include <rte_common.h>
52 #include <rte_lcore.h>
53 #include <rte_per_lcore.h>
54 #include <rte_timer.h>
55
56 #include "lthread_api.h"
57 #include "lthread_diag_api.h"
58 #include "pthread_shim.h"
59
60 #define DEBUG_APP 0
61 #define HELLOW_WORLD_MAX_LTHREADS 10
62
63 __thread int print_count;
64 __thread pthread_mutex_t print_lock;
65
66 __thread pthread_mutex_t exit_lock;
67 __thread pthread_cond_t exit_cond;
68
69 /*
70  * A simple thread that demonstrates use of a mutex, a condition
71  * variable, thread local storage, explicit yield, and thread exit.
72  *
73  * The thread uses a mutex to protect a shared counter which is incremented
74  * and then it waits on condition variable before exiting.
75  *
76  * The thread argument is stored in and retrieved from TLS, using
77  * the pthread key create, get and set specific APIs.
78  *
79  * The thread yields while holding the mutex, to provide opportunity
80  * for other threads to contend.
81  *
82  * All of the pthread API functions used by this thread are actually
83  * resolved to corresponding lthread functions by the pthread shim
84  * implemented in pthread_shim.c
85  */
86 void *helloworld_pthread(void *arg);
87 void *helloworld_pthread(void *arg)
88 {
89         pthread_key_t key;
90
91         /* create a key for TLS */
92         pthread_key_create(&key, NULL);
93
94         /* store the arg in TLS */
95         pthread_setspecific(key, arg);
96
97         /* grab lock and increment shared counter */
98         pthread_mutex_lock(&print_lock);
99         print_count++;
100
101         /* yield thread to give opportunity for lock contention */
102         pthread_yield();
103
104         /* retrieve arg from TLS */
105         uint64_t thread_no = (uint64_t) pthread_getspecific(key);
106
107         printf("Hello - lcore = %d count = %d thread_no = %d thread_id = %p\n",
108                         sched_getcpu(),
109                         print_count,
110                         (int) thread_no,
111                         (void *)pthread_self());
112
113         /* release the lock */
114         pthread_mutex_unlock(&print_lock);
115
116         /*
117          * wait on condition variable
118          * before exiting
119          */
120         pthread_mutex_lock(&exit_lock);
121         pthread_cond_wait(&exit_cond, &exit_lock);
122         pthread_mutex_unlock(&exit_lock);
123
124         /* exit */
125         pthread_exit((void *) thread_no);
126 }
127
128
129 /*
130  * This is the initial thread
131  *
132  * It demonstrates pthread, mutex and condition variable creation,
133  * broadcast and pthread join APIs.
134  *
135  * This initial thread must always start life as an lthread.
136  *
137  * This thread creates many more threads then waits a short time
138  * before signalling them to exit using a broadcast.
139  *
140  * All of the pthread API functions used by this thread are actually
141  * resolved to corresponding lthread functions by the pthread shim
142  * implemented in pthread_shim.c
143  *
144  * After all threads have finished the lthread scheduler is shutdown
145  * and normal pthread operation is restored
146  */
147 __thread pthread_t tid[HELLOW_WORLD_MAX_LTHREADS];
148
149 static void initial_lthread(void *args);
150 static void initial_lthread(void *args __attribute__((unused)))
151 {
152         int lcore = (int) rte_lcore_id();
153         /*
154          *
155          * We can now enable pthread API override
156          * and start to use the pthread APIs
157          */
158         pthread_override_set(1);
159
160         uint64_t i;
161
162         /* initialize mutex for shared counter */
163         print_count = 0;
164         pthread_mutex_init(&print_lock, NULL);
165
166         /* initialize mutex and condition variable controlling thread exit */
167         pthread_mutex_init(&exit_lock, NULL);
168         pthread_cond_init(&exit_cond, NULL);
169
170         /* spawn a number of threads */
171         for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
172
173                 /*
174                  * Not strictly necessary but
175                  * for the sake of this example
176                  * use an attribute to pass the desired lcore
177                  */
178                 pthread_attr_t attr;
179                 cpu_set_t cpuset;
180
181                 CPU_ZERO(&cpuset);
182                 CPU_SET(lcore, &cpuset);
183                 pthread_attr_init(&attr);
184                 pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
185
186                 /* create the thread */
187                 pthread_create(&tid[i], &attr, helloworld_pthread, (void *) i);
188         }
189
190         /* wait for 1s to allow threads
191          * to block on the condition variable
192          * N.B. nanosleep() is resolved to lthread_sleep()
193          * by the shim.
194          */
195         struct timespec time;
196
197         time.tv_sec = 1;
198         time.tv_nsec = 0;
199         nanosleep(&time, NULL);
200
201         /* wake up all the threads */
202         pthread_cond_broadcast(&exit_cond);
203
204         /* wait for them to finish */
205         for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
206
207                 uint64_t thread_no;
208
209                 pthread_join(tid[i], (void *) &thread_no);
210                 if (thread_no != i)
211                         printf("error on thread exit\n");
212         }
213
214         /* shutdown the lthread scheduler */
215         lthread_scheduler_shutdown(rte_lcore_id());
216         lthread_detach();
217 }
218
219
220
221 /* This thread creates a single initial lthread
222  * and then runs the scheduler
223  * An instance of this thread is created on each thread
224  * in the core mask
225  */
226 static int
227 lthread_scheduler(void *args);
228 static int
229 lthread_scheduler(void *args __attribute__((unused)))
230 {
231         /* create initial thread  */
232         struct lthread *lt;
233
234         lthread_create(&lt, -1, initial_lthread, (void *) NULL);
235
236         /* run the lthread scheduler */
237         lthread_run();
238
239         /* restore genuine pthread operation */
240         pthread_override_set(0);
241         return 0;
242 }
243
244 int main(int argc, char **argv)
245 {
246         int num_sched = 0;
247
248         /* basic DPDK initialization is all that is necessary to run lthreads*/
249         int ret = rte_eal_init(argc, argv);
250
251         if (ret < 0)
252                 rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
253
254         /* enable timer subsystem */
255         rte_timer_subsystem_init();
256
257 #if DEBUG_APP
258         lthread_diagnostic_set_mask(LT_DIAG_ALL);
259 #endif
260
261         /* create a scheduler on every core in the core mask
262          * and launch an initial lthread that will spawn many more.
263          */
264         unsigned lcore_id;
265
266         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
267                 if (rte_lcore_is_enabled(lcore_id))
268                         num_sched++;
269         }
270
271         /* set the number of schedulers, this forces all schedulers synchronize
272          * before entering their main loop
273          */
274         lthread_num_schedulers_set(num_sched);
275
276         /* launch all threads */
277         rte_eal_mp_remote_launch(lthread_scheduler, (void *)NULL, CALL_MASTER);
278
279         /* wait for threads to stop */
280         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
281                 rte_eal_wait_lcore(lcore_id);
282         }
283         return 0;
284 }