dpdk/examples/performance-thread/pthread_shim/main.c
<<
>>
Prefs
   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 */
  59void *helloworld_pthread(void *arg);
  60void *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        sched_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
 122static 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 */
 211static int
 212lthread_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
 227int 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}
 272