1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2015 Intel Corporation
11 #include <sys/queue.h>
19 #include <rte_common.h>
20 #include <rte_lcore.h>
21 #include <rte_per_lcore.h>
22 #include <rte_timer.h>
24 #include "lthread_api.h"
25 #include "lthread_diag_api.h"
26 #include "pthread_shim.h"
29 #define HELLOW_WORLD_MAX_LTHREADS 10
30 #define THREAD_NAME_LEN 16
32 #ifndef __GLIBC__ /* sched_getcpu() is glibc-specific */
33 #define sched_getcpu() rte_lcore_id()
36 __thread int print_count;
37 __thread pthread_mutex_t print_lock;
39 __thread pthread_mutex_t exit_lock;
40 __thread pthread_cond_t exit_cond;
43 * A simple thread that demonstrates use of a mutex, a condition
44 * variable, thread local storage, explicit yield, and thread exit.
46 * The thread uses a mutex to protect a shared counter which is incremented
47 * and then it waits on condition variable before exiting.
49 * The thread argument is stored in and retrieved from TLS, using
50 * the pthread key create, get and set specific APIs.
52 * The thread yields while holding the mutex, to provide opportunity
53 * for other threads to contend.
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
59 void *helloworld_pthread(void *arg);
60 void *helloworld_pthread(void *arg)
64 /* create a key for TLS */
65 pthread_key_create(&key, NULL);
67 /* store the arg in TLS */
68 pthread_setspecific(key, arg);
70 /* grab lock and increment shared counter */
71 pthread_mutex_lock(&print_lock);
74 /* yield thread to give opportunity for lock contention */
77 /* retrieve arg from TLS */
78 uint64_t thread_no = (uint64_t) pthread_getspecific(key);
80 printf("Hello - lcore = %d count = %d thread_no = %d thread_id = %p\n",
84 (void *)pthread_self());
86 /* release the lock */
87 pthread_mutex_unlock(&print_lock);
90 * wait on condition variable
93 pthread_mutex_lock(&exit_lock);
94 pthread_cond_wait(&exit_cond, &exit_lock);
95 pthread_mutex_unlock(&exit_lock);
98 pthread_exit((void *) thread_no);
103 * This is the initial thread
105 * It demonstrates pthread, mutex and condition variable creation,
106 * broadcast and pthread join APIs.
108 * This initial thread must always start life as an lthread.
110 * This thread creates many more threads then waits a short time
111 * before signalling them to exit using a broadcast.
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
117 * After all threads have finished the lthread scheduler is shutdown
118 * and normal pthread operation is restored
120 __thread pthread_t tid[HELLOW_WORLD_MAX_LTHREADS];
122 static void *initial_lthread(void *args __rte_unused)
124 int lcore = (int) rte_lcore_id();
127 * We can now enable pthread API override
128 * and start to use the pthread APIs
130 pthread_override_set(1);
135 /* initialize mutex for shared counter */
137 pthread_mutex_init(&print_lock, NULL);
139 /* initialize mutex and condition variable controlling thread exit */
140 pthread_mutex_init(&exit_lock, NULL);
141 pthread_cond_init(&exit_cond, NULL);
143 /* spawn a number of threads */
144 for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
147 * Not strictly necessary but
148 * for the sake of this example
149 * use an attribute to pass the desired lcore
153 char name[THREAD_NAME_LEN];
156 CPU_SET(lcore, &cpuset);
157 pthread_attr_init(&attr);
158 pthread_attr_setaffinity_np(&attr, sizeof(rte_cpuset_t), &cpuset);
160 /* create the thread */
161 ret = pthread_create(&tid[i], &attr,
162 helloworld_pthread, (void *) i);
164 rte_exit(EXIT_FAILURE, "Cannot create helloworld thread\n");
166 snprintf(name, sizeof(name), "helloworld-%u", (uint32_t)i);
167 rte_thread_setname(tid[i], name);
170 /* wait for 1s to allow threads
171 * to block on the condition variable
172 * N.B. nanosleep() is resolved to lthread_sleep()
175 struct timespec time;
179 nanosleep(&time, NULL);
181 /* wake up all the threads */
182 pthread_cond_broadcast(&exit_cond);
184 /* wait for them to finish */
185 for (i = 0; i < HELLOW_WORLD_MAX_LTHREADS; i++) {
189 pthread_join(tid[i], (void *) &thread_no);
191 printf("error on thread exit\n");
194 pthread_cond_destroy(&exit_cond);
195 pthread_mutex_destroy(&print_lock);
196 pthread_mutex_destroy(&exit_lock);
198 /* shutdown the lthread scheduler */
199 lthread_scheduler_shutdown(rte_lcore_id());
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
212 lthread_scheduler(void *args __rte_unused)
214 /* create initial thread */
217 lthread_create(<, -1, initial_lthread, (void *) NULL);
219 /* run the lthread scheduler */
222 /* restore genuine pthread operation */
223 pthread_override_set(0);
227 int main(int argc, char **argv)
231 /* basic DPDK initialization is all that is necessary to run lthreads*/
232 int ret = rte_eal_init(argc, argv);
235 rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
237 /* enable timer subsystem */
238 rte_timer_subsystem_init();
241 lthread_diagnostic_set_mask(LT_DIAG_ALL);
244 /* create a scheduler on every core in the core mask
245 * and launch an initial lthread that will spawn many more.
249 for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
250 if (rte_lcore_is_enabled(lcore_id))
254 /* set the number of schedulers, this forces all schedulers synchronize
255 * before entering their main loop
257 lthread_num_schedulers_set(num_sched);
259 /* launch all threads */
260 rte_eal_mp_remote_launch(lthread_scheduler, (void *)NULL, CALL_MAIN);
262 /* wait for threads to stop */
263 RTE_LCORE_FOREACH_WORKER(lcore_id) {
264 rte_eal_wait_lcore(lcore_id);
267 /* clean up the EAL */