diff options
Diffstat (limited to 'examples/performance-thread/common/lthread_api.h')
-rw-r--r-- | examples/performance-thread/common/lthread_api.h | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/examples/performance-thread/common/lthread_api.h b/examples/performance-thread/common/lthread_api.h new file mode 100644 index 00000000..ec976103 --- /dev/null +++ b/examples/performance-thread/common/lthread_api.h @@ -0,0 +1,832 @@ +/*- + * BSD LICENSE + * + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Some portions of this software may have been derived from the + * https://github.com/halayli/lthread which carrys the following license. + * + * Copyright (C) 2012, Hasan Alayli <halayli@gmail.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * @file lthread_api.h + * + * @warning + * @b EXPERIMENTAL: this API may change without prior notice + * + * This file contains the public API for the L-thread subsystem + * + * The L_thread subsystem provides a simple cooperative scheduler to + * enable arbitrary functions to run as cooperative threads within a + * single P-thread. + * + * The subsystem provides a P-thread like API that is intended to assist in + * reuse of legacy code written for POSIX p_threads. + * + * The L-thread subsystem relies on cooperative multitasking, as such + * an L-thread must possess frequent rescheduling points. Often these + * rescheduling points are provided transparently when the application + * invokes an L-thread API. + * + * In some applications it is possible that the program may enter a loop the + * exit condition for which depends on the action of another thread or a + * response from hardware. In such a case it is necessary to yield the thread + * periodically in the loop body, to allow other threads an opportunity to + * run. This can be done by inserting a call to lthread_yield() or + * lthread_sleep(n) in the body of the loop. + * + * If the application makes expensive / blocking system calls or does other + * work that would take an inordinate amount of time to complete, this will + * stall the cooperative scheduler resulting in very poor performance. + * + * In such cases an L-thread can be migrated temporarily to another scheduler + * running in a different P-thread on another core. When the expensive or + * blocking operation is completed it can be migrated back to the original + * scheduler. In this way other threads can continue to run on the original + * scheduler and will be completely unaffected by the blocking behaviour. + * To migrate an L-thread to another scheduler the API lthread_set_affinity() + * is provided. + * + * If L-threads that share data are running on the same core it is possible + * to design programs where mutual exclusion mechanisms to protect shared data + * can be avoided. This is due to the fact that the cooperative threads cannot + * preempt each other. + * + * There are two cases where mutual exclusion mechanisms are necessary. + * + * a) Where the L-threads sharing data are running on different cores. + * b) Where code must yield while updating data shared with another thread. + * + * The L-thread subsystem provides a set of mutex APIs to help with such + * scenarios, however excessive reliance on on these will impact performance + * and is best avoided if possible. + * + * L-threads can synchronise using a fast condition variable implementation + * that supports signal and broadcast. An L-thread running on any core can + * wait on a condition. + * + * L-threads can have L-thread local storage with an API modelled on either the + * P-thread get/set specific API or using PER_LTHREAD macros modelled on the + * RTE_PER_LCORE macros. Alternatively a simple user data pointer may be set + * and retrieved from a thread. + */ +#ifndef LTHREAD_H +#define LTHREAD_H + +#include <stdint.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <netinet/in.h> + +#include <rte_cycles.h> + + +struct lthread; +struct lthread_cond; +struct lthread_mutex; + +struct lthread_condattr; +struct lthread_mutexattr; + +typedef void (*lthread_func_t) (void *); + +/* + * Define the size of stack for an lthread + * Then this is the size that will be allocated on lthread creation + * This is a fixed size and will not grow. + */ +#define LTHREAD_MAX_STACK_SIZE (1024*64) + +/** + * Define the maximum number of TLS keys that can be created + * + */ +#define LTHREAD_MAX_KEYS 1024 + +/** + * Define the maximum number of attempts to destroy an lthread's + * TLS data on thread exit + */ +#define LTHREAD_DESTRUCTOR_ITERATIONS 4 + + +/** + * Define the maximum number of lcores that will support lthreads + */ +#define LTHREAD_MAX_LCORES RTE_MAX_LCORE + +/** + * How many lthread objects to pre-allocate as the system grows + * applies to lthreads + stacks, TLS, mutexs, cond vars. + * + * @see _lthread_alloc() + * @see _cond_alloc() + * @see _mutex_alloc() + * + */ +#define LTHREAD_PREALLOC 100 + +/** + * Set the number of schedulers in the system. + * + * This function may optionally be called before starting schedulers. + * + * If the number of schedulers is not set, or set to 0 then each scheduler + * will begin scheduling lthreads immediately it is started. + + * If the number of schedulers is set to greater than 0, then each scheduler + * will wait until all schedulers have started before beginning to schedule + * lthreads. + * + * If an application wishes to have threads migrate between cores using + * lthread_set_affinity(), or join threads running on other cores using + * lthread_join(), then it is prudent to set the number of schedulers to ensure + * that all schedulers are initialised beforehand. + * + * @param num + * the number of schedulers in the system + * @return + * the number of schedulers in the system + */ +int lthread_num_schedulers_set(int num); + +/** + * Return the number of schedulers currently running + * @return + * the number of schedulers in the system + */ +int lthread_active_schedulers(void); + +/** + * Shutdown the specified scheduler + * + * This function tells the specified scheduler to + * exit if/when there is no more work to do. + * + * Note that although the scheduler will stop + * resources are not freed. + * + * @param lcore + * The lcore of the scheduler to shutdown + * + * @return + * none + */ +void lthread_scheduler_shutdown(unsigned lcore); + +/** + * Shutdown all schedulers + * + * This function tells all schedulers including the current scheduler to + * exit if/when there is no more work to do. + * + * Note that although the schedulers will stop + * resources are not freed. + * + * @return + * none + */ +void lthread_scheduler_shutdown_all(void); + +/** + * Run the lthread scheduler + * + * Runs the lthread scheduler. + * This function returns only if/when all lthreads have exited. + * This function must be the main loop of an EAL thread. + * + * @return + * none + */ + +void lthread_run(void); + +/** + * Create an lthread + * + * Creates an lthread and places it in the ready queue on a particular + * lcore. + * + * If no scheduler exists yet on the curret lcore then one is created. + * + * @param new_lt + * Pointer to an lthread pointer that will be initialized + * @param lcore + * the lcore the thread should be started on or the current clore + * -1 the current lcore + * 0 - LTHREAD_MAX_LCORES any other lcore + * @param lthread_func + * Pointer to the function the for the thread to run + * @param arg + * Pointer to args that will be passed to the thread + * + * @return + * 0 success + * EAGAIN no resources available + * EINVAL NULL thread or function pointer, or lcore_id out of range + */ +int +lthread_create(struct lthread **new_lt, + int lcore, lthread_func_t func, void *arg); + +/** + * Cancel an lthread + * + * Cancels an lthread and causes it to be terminated + * If the lthread is detached it will be freed immediately + * otherwise its resources will not be released until it is joined. + * + * @param new_lt + * Pointer to an lthread that will be cancelled + * + * @return + * 0 success + * EINVAL thread was NULL + */ +int lthread_cancel(struct lthread *lt); + +/** + * Join an lthread + * + * Joins the current thread with the specified lthread, and waits for that + * thread to exit. + * Passes an optional pointer to collect returned data. + * + * @param lt + * Pointer to the lthread to be joined + * @param ptr + * Pointer to pointer to collect returned data + * +0 * @return + * 0 success + * EINVAL lthread could not be joined. + */ +int lthread_join(struct lthread *lt, void **ptr); + +/** + * Detach an lthread + * + * Detaches the current thread + * On exit a detached lthread will be freed immediately and will not wait + * to be joined. The default state for a thread is not detached. + * + * @return + * none + */ +void lthread_detach(void); + +/** + * Exit an lthread + * + * Terminate the current thread, optionally return data. + * The data may be collected by lthread_join() + * + * After calling this function the lthread will be suspended until it is + * joined. After it is joined then its resources will be freed. + * + * @param ptr + * Pointer to pointer to data to be returned + * + * @return + * none + */ +void lthread_exit(void *val); + +/** + * Cause the current lthread to sleep for n nanoseconds + * + * The current thread will be suspended until the specified time has elapsed + * or has been exceeded. + * + * Execution will switch to the next lthread that is ready to run + * + * @param nsecs + * Number of nanoseconds to sleep + * + * @return + * none + */ +void lthread_sleep(uint64_t nsecs); + +/** + * Cause the current lthread to sleep for n cpu clock ticks + * + * The current thread will be suspended until the specified time has elapsed + * or has been exceeded. + * + * Execution will switch to the next lthread that is ready to run + * + * @param clks + * Number of clock ticks to sleep + * + * @return + * none + */ +void lthread_sleep_clks(uint64_t clks); + +/** + * Yield the current lthread + * + * The current thread will yield and execution will switch to the + * next lthread that is ready to run + * + * @return + * none + */ +void lthread_yield(void); + +/** + * Migrate the current thread to another scheduler + * + * This function migrates the current thread to another scheduler. + * Execution will switch to the next lthread that is ready to run on the + * current scheduler. The current thread will be resumed on the new scheduler. + * + * @param lcore + * The lcore to migrate to + * + * @return + * 0 success we are now running on the specified core + * EINVAL the destination lcore was not valid + */ +int lthread_set_affinity(unsigned lcore); + +/** + * Return the current lthread + * + * Returns the current lthread + * + * @return + * pointer to the current lthread + */ +struct lthread +*lthread_current(void); + +/** + * Associate user data with an lthread + * + * This function sets a user data pointer in the current lthread + * The pointer can be retrieved with lthread_get_data() + * It is the users responsibility to allocate and free any data referenced + * by the user pointer. + * + * @param data + * pointer to user data + * + * @return + * none + */ +void lthread_set_data(void *data); + +/** + * Get user data for the current lthread + * + * This function returns a user data pointer for the current lthread + * The pointer must first be set with lthread_set_data() + * It is the users responsibility to allocate and free any data referenced + * by the user pointer. + * + * @return + * pointer to user data + */ +void +*lthread_get_data(void); + +struct lthread_key; +typedef void (*tls_destructor_func) (void *); + +/** + * Create a key for lthread TLS + * + * This function is modelled on pthread_key_create + * It creates a thread-specific data key visible to all lthreads on the + * current scheduler. + * + * Key values may be used to locate thread-specific data. + * The same key value may be used by different threads, the values bound + * to the key by lthread_setspecific() are maintained on a per-thread + * basis and persist for the life of the calling thread. + * + * An optional destructor function may be associated with each key value. + * At thread exit, if a key value has a non-NULL destructor pointer, and the + * thread has a non-NULL value associated with the key, the function pointed + * to is called with the current associated value as its sole argument. + * + * @param key + * Pointer to the key to be created + * @param destructor + * Pointer to destructor function + * + * @return + * 0 success + * EINVAL the key ptr was NULL + * EAGAIN no resources available + */ +int lthread_key_create(unsigned int *key, tls_destructor_func destructor); + +/** + * Delete key for lthread TLS + * + * This function is modelled on pthread_key_delete(). + * It deletes a thread-specific data key previously returned by + * lthread_key_create(). + * The thread-specific data values associated with the key need not be NULL + * at the time that lthread_key_delete is called. + * It is the responsibility of the application to free any application + * storage or perform any cleanup actions for data structures related to the + * deleted key. This cleanup can be done either before or after + * lthread_key_delete is called. + * + * @param key + * The key to be deleted + * + * @return + * 0 Success + * EINVAL the key was invalid + */ +int lthread_key_delete(unsigned int key); + +/** + * Get lthread TLS + * + * This function is modelled on pthread_get_specific(). + * It returns the value currently bound to the specified key on behalf of the + * calling thread. Calling lthread_getspecific() with a key value not + * obtained from lthread_key_create() or after key has been deleted with + * lthread_key_delete() will result in undefined behaviour. + * lthread_getspecific() may be called from a thread-specific data destructor + * function. + * + * @param key + * The key for which data is requested + * + * @return + * Pointer to the thread specific data associated with that key + * or NULL if no data has been set. + */ +void +*lthread_getspecific(unsigned int key); + +/** + * Set lthread TLS + * + * This function is modelled on pthread_set_sepcific() + * It associates a thread-specific value with a key obtained via a previous + * call to lthread_key_create(). + * Different threads may bind different values to the same key. These values + * are typically pointers to dynamically allocated memory that have been + * reserved by the calling thread. Calling lthread_setspecific with a key + * value not obtained from lthread_key_create or after the key has been + * deleted with lthread_key_delete will result in undefined behaviour. + * + * @param key + * The key for which data is to be set + * @param key + * Pointer to the user data + * + * @return + * 0 success + * EINVAL the key was invalid + */ + +int lthread_setspecific(unsigned int key, const void *value); + +/** + * The macros below provide an alternative mechanism to access lthread local + * storage. + * + * The macros can be used to declare define and access per lthread local + * storage in a similar way to the RTE_PER_LCORE macros which control storage + * local to an lcore. + * + * Memory for per lthread variables declared in this way is allocated when the + * lthread is created and a pointer to this memory is stored in the lthread. + * The per lthread variables are accessed via the pointer + the offset of the + * particular variable. + * + * The total size of per lthread storage, and the variable offsets are found by + * defining the variables in a unique global memory section, the start and end + * of which is known. This global memory section is used only in the + * computation of the addresses of the lthread variables, and is never actually + * used to store any data. + * + * Due to the fact that variables declared this way may be scattered across + * many files, the start and end of the section and variable offsets are only + * known after linking, thus the computation of section size and variable + * addresses is performed at run time. + * + * These macros are primarily provided to aid porting of code that makes use + * of the existing RTE_PER_LCORE macros. In principle it would be more efficient + * to gather all lthread local variables into a single structure and + * set/retrieve a pointer to that struct using the alternative + * lthread_data_set/get APIs. + * + * These macros are mutually exclusive with the lthread_data_set/get APIs. + * If you define storage using these macros then the lthread_data_set/get APIs + * will not perform as expected, the lthread_data_set API does nothing, and the + * lthread_data_get API returns the start of global section. + * + */ +/* start and end of per lthread section */ +extern char __start_per_lt; +extern char __stop_per_lt; + + +#define RTE_DEFINE_PER_LTHREAD(type, name) \ +__typeof__(type)__attribute((section("per_lt"))) per_lt_##name + +/** + * Macro to declare an extern per lthread variable "var" of type "type" + */ +#define RTE_DECLARE_PER_LTHREAD(type, name) \ +extern __typeof__(type)__attribute((section("per_lt"))) per_lt_##name + +/** + * Read/write the per-lcore variable value + */ +#define RTE_PER_LTHREAD(name) ((typeof(per_lt_##name) *)\ +((char *)lthread_get_data() +\ +((char *) &per_lt_##name - &__start_per_lt))) + +/** + * Initialize a mutex + * + * This function provides a mutual exclusion device, the need for which + * can normally be avoided in a cooperative multitasking environment. + * It is provided to aid porting of legacy code originally written for + * preemptive multitasking environments such as pthreads. + * + * A mutex may be unlocked (not owned by any thread), or locked (owned by + * one thread). + * + * A mutex can never be owned by more than one thread simultaneously. + * A thread attempting to lock a mutex that is already locked by another + * thread is suspended until the owning thread unlocks the mutex. + * + * lthread_mutex_init() initializes the mutex object pointed to by mutex + * Optional mutex attributes specified in mutexattr, are reserved for future + * use and are currently ignored. + * + * If a thread calls lthread_mutex_lock() on the mutex, then if the mutex + * is currently unlocked, it becomes locked and owned by the calling + * thread, and lthread_mutex_lock returns immediately. If the mutex is + * already locked by another thread, lthread_mutex_lock suspends the calling + * thread until the mutex is unlocked. + * + * lthread_mutex_trylock behaves identically to rte_thread_mutex_lock, except + * that it does not block the calling thread if the mutex is already locked + * by another thread. + * + * lthread_mutex_unlock() unlocks the specified mutex. The mutex is assumed + * to be locked and owned by the calling thread. + * + * lthread_mutex_destroy() destroys a mutex object, freeing its resources. + * The mutex must be unlocked with nothing blocked on it before calling + * lthread_mutex_destroy. + * + * @param name + * Optional pointer to string describing the mutex + * @param mutex + * Pointer to pointer to the mutex to be initialized + * @param attribute + * Pointer to attribute - unused reserved + * + * @return + * 0 success + * EINVAL mutex was not a valid pointer + * EAGAIN insufficient resources + */ + +int +lthread_mutex_init(char *name, struct lthread_mutex **mutex, + const struct lthread_mutexattr *attr); + +/** + * Destroy a mutex + * + * This function destroys the specified mutex freeing its resources. + * The mutex must be unlocked before calling lthread_mutex_destroy. + * + * @see lthread_mutex_init() + * + * @param mutex + * Pointer to pointer to the mutex to be initialized + * + * @return + * 0 success + * EINVAL mutex was not an initialized mutex + * EBUSY mutex was still in use + */ +int lthread_mutex_destroy(struct lthread_mutex *mutex); + +/** + * Lock a mutex + * + * This function attempts to lock a mutex. + * If a thread calls lthread_mutex_lock() on the mutex, then if the mutex + * is currently unlocked, it becomes locked and owned by the calling + * thread, and lthread_mutex_lock returns immediately. If the mutex is + * already locked by another thread, lthread_mutex_lock suspends the calling + * thread until the mutex is unlocked. + * + * @see lthread_mutex_init() + * + * @param mutex + * Pointer to pointer to the mutex to be initialized + * + * @return + * 0 success + * EINVAL mutex was not an initialized mutex + * EDEADLOCK the mutex was already owned by the calling thread + */ + +int lthread_mutex_lock(struct lthread_mutex *mutex); + +/** + * Try to lock a mutex + * + * This function attempts to lock a mutex. + * lthread_mutex_trylock behaves identically to rte_thread_mutex_lock, except + * that it does not block the calling thread if the mutex is already locked + * by another thread. + * + * + * @see lthread_mutex_init() + * + * @param mutex + * Pointer to pointer to the mutex to be initialized + * + * @return + * 0 success + * EINVAL mutex was not an initialized mutex + * EBUSY the mutex was already locked by another thread + */ +int lthread_mutex_trylock(struct lthread_mutex *mutex); + +/** + * Unlock a mutex + * + * This function attempts to unlock the specified mutex. The mutex is assumed + * to be locked and owned by the calling thread. + * + * The oldest of any threads blocked on the mutex is made ready and may + * compete with any other running thread to gain the mutex, it fails it will + * be blocked again. + * + * @param mutex + * Pointer to pointer to the mutex to be initialized + * + * @return + * 0 mutex was unlocked + * EINVAL mutex was not an initialized mutex + * EPERM the mutex was not owned by the calling thread + */ + +int lthread_mutex_unlock(struct lthread_mutex *mutex); + +/** + * Initialize a condition variable + * + * This function initializes a condition variable. + * + * Condition variables can be used to communicate changes in the state of data + * shared between threads. + * + * @see lthread_cond_wait() + * + * @param name + * Pointer to optional string describing the condition variable + * @param c + * Pointer to pointer to the condition variable to be initialized + * @param attr + * Pointer to optional attribute reserved for future use, currently ignored + * + * @return + * 0 success + * EINVAL cond was not a valid pointer + * EAGAIN insufficient resources + */ +int +lthread_cond_init(char *name, struct lthread_cond **c, + const struct lthread_condattr *attr); + +/** + * Destroy a condition variable + * + * This function destroys a condition variable that was created with + * lthread_cond_init() and releases its resources. + * + * @param cond + * Pointer to pointer to the condition variable to be destroyed + * + * @return + * 0 Success + * EBUSY condition variable was still in use + * EINVAL was not an initialised condition variable + */ +int lthread_cond_destroy(struct lthread_cond *cond); + +/** + * Wait on a condition variable + * + * The function blocks the current thread waiting on the condition variable + * specified by cond. The waiting thread unblocks only after another thread + * calls lthread_cond_signal, or lthread_cond_broadcast, specifying the + * same condition variable. + * + * @param cond + * Pointer to pointer to the condition variable to be waited on + * + * @param reserved + * reserved for future use + * + * @return + * 0 The condition was signalled ( Success ) + * EINVAL was not a an initialised condition variable + */ +int lthread_cond_wait(struct lthread_cond *c, uint64_t reserved); + +/** + * Signal a condition variable + * + * The function unblocks one thread waiting for the condition variable cond. + * If no threads are waiting on cond, the rte_lthead_cond_signal() function + * has no effect. + * + * @param cond + * Pointer to pointer to the condition variable to be signalled + * + * @return + * 0 The condition was signalled ( Success ) + * EINVAL was not a an initialised condition variable + */ +int lthread_cond_signal(struct lthread_cond *c); + +/** + * Broadcast a condition variable + * + * The function unblocks all threads waiting for the condition variable cond. + * If no threads are waiting on cond, the rte_lthead_cond_broadcast() + * function has no effect. + * + * @param cond + * Pointer to pointer to the condition variable to be signalled + * + * @return + * 0 The condition was signalled ( Success ) + * EINVAL was not a an initialised condition variable + */ +int lthread_cond_broadcast(struct lthread_cond *c); + +#endif /* LTHREAD_H */ |