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