cedric pushed a commit to branch master. http://git.enlightenment.org/website/www-content.git/commit/?id=43f6bc0db9d4c42708b7bfb00ec2d335a917cf9b
commit 43f6bc0db9d4c42708b7bfb00ec2d335a917cf9b Author: Clément Bénier <[email protected]> Date: Mon Sep 7 12:21:29 2015 +0200 Wiki pages threading_pg created: 1 main + 5 PG + index + 3 images Signed-off-by: Clément Bénier <[email protected]> --- media/threading_pool_lifecycle_1.png | Bin 0 -> 29767 bytes media/threading_pool_lifecycle_2.png | Bin 0 -> 33952 bytes media/threading_pool_lifecycle_3.png | Bin 0 -> 30532 bytes pages/docs.txt | 1 + pages/program_guide/index.txt | 1 + pages/program_guide/threading/index.txt | 5 + .../threading/low-level_functions.txt | 71 ++++++++++++ .../threading/thread_management_with_ecore.txt | 61 ++++++++++ pages/program_guide/threading/thread_pools.txt | 52 +++++++++ pages/program_guide/threading/thread_safety.txt | 30 +++++ .../program_guide/threading/thread_use_example.txt | 124 +++++++++++++++++++++ pages/program_guide/threading_pg.txt | 29 +++++ 12 files changed, 374 insertions(+) diff --git a/media/threading_pool_lifecycle_1.png b/media/threading_pool_lifecycle_1.png new file mode 100644 index 0000000..6a3d24c Binary files /dev/null and b/media/threading_pool_lifecycle_1.png differ diff --git a/media/threading_pool_lifecycle_2.png b/media/threading_pool_lifecycle_2.png new file mode 100644 index 0000000..71187d3 Binary files /dev/null and b/media/threading_pool_lifecycle_2.png differ diff --git a/media/threading_pool_lifecycle_3.png b/media/threading_pool_lifecycle_3.png new file mode 100644 index 0000000..51c6f86 Binary files /dev/null and b/media/threading_pool_lifecycle_3.png differ diff --git a/pages/docs.txt b/pages/docs.txt index 0ddab7b..c22c66d 100644 --- a/pages/docs.txt +++ b/pages/docs.txt @@ -68,6 +68,7 @@ Go check the current available version of EFL on each distro/platform: * [[program_guide/focus_ui_pg|Managing UI Component Focus PG]] * [[program_guide/customizing_ui_pg|Customizing UI Components PG]] * [[program_guide/main_loop_pg|Main Loop PG]] + * [[program_guide/threading_pg|Threading PG]] === Samples === diff --git a/pages/program_guide/index.txt b/pages/program_guide/index.txt index f563afc..666c82b 100644 --- a/pages/program_guide/index.txt +++ b/pages/program_guide/index.txt @@ -11,4 +11,5 @@ * [[program_guide/customizing_ui_pg|Customizing UI Components PG]] * [[program_guide/focus_ui_pg|Managing UI Component Focus PG]] * [[program_guide/main_loop_pg|Main Loop PG]] + * [[program_guide/threading_pg|Threading PG]] ++++ diff --git a/pages/program_guide/threading/index.txt b/pages/program_guide/threading/index.txt new file mode 100644 index 0000000..bd92dff --- /dev/null +++ b/pages/program_guide/threading/index.txt @@ -0,0 +1,5 @@ +++++ Threading Menu| +^ [[/program_guide/threading_pg|Threading PG]] ^^^^^ +| [[/program_guide/threading/thread_safety|Thread Safety]] | [[/program_guide/threading/thread_pools|Thread Pools]] | [[/program_guide/threading/thread_management_with_ecore|Thread Management with Ecore]] | [[/program_guide/threading/low-level_functions|Low-level Functions]] | [[/program_guide/threading/thread_use_example|Thread Use Example]] | +++++ + diff --git a/pages/program_guide/threading/low-level_functions.txt b/pages/program_guide/threading/low-level_functions.txt new file mode 100644 index 0000000..1ff4c8d --- /dev/null +++ b/pages/program_guide/threading/low-level_functions.txt @@ -0,0 +1,71 @@ +{{page>index}} +------- +===== Low-level Functions ===== + +Eina offers low-level functions that are portable across the operating system, +such as locks, conditions, semaphores, barriers, and spinlocks. The functions +follow closely the logic of pthreads. + +While these functions are useful, they are building blocks and not usually +useful in EFL applications considering the higher-level functions that are +available in Ecore. + +For an introduction to threads and pthreads in particular, see: + + * [[http://www.ibm.com/developerworks/library/l-pthred/index.html|Basic use of pthreads]] (IBM developerWorks) + * [[https://computing.llnl.gov/tutorials/pthreads/|POSIX Threads Programming]] (Lawrence Livermore National Laboratory) + * [[http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pthread.h.html|POSIX 2003 specification]] (opengroup) + +If you are already familiar with threads, see the standard pthreads +documentation and the Eina reference documentation, or the following function +lists. Remember that the Eina functions map very closely to the pthreads +functions. + +^ Lock (mutual exclusions) ^^ +| **pthreads function** | **eina equivalent** | +|''pthread_mutex_new()''|''eina_lock_new()''| +|''pthread_mutex_destroy()''|''eina_lock_free()''| +|''pthread_mutex_lock()''|''eina_lock_take()''| +|''pthread_mutex_trylock()''|''eina_lock_take_try()''| +|''pthread_mutex_unlock()''|''eina_lock_release()''| +|none (prints debug information on the lock)|''eina_lock_debug()''| + +^ Conditions (notifications when condition objects change) ^^ +| **pthreads function** | **eina equivalent** | +|''pthread_cond_init()'' |''eina_condition_new()'' | +|''pthread_cond_destroy'' |''eina_condition_free()'' | +|''pthread_cond_wait()'' |''eina_condition_wait()'' | +|''pthread_cond_timedwait()'' |''eina_condition_timedwait()'' | +|''pthread_cond_broadcast()'' |''eina_condition_broadcast()'' | +|''pthread_cond_signal()'' |''eina_condition_signal()'' | + +^ RWLocks (Read-write locks, for multiple-readers/single-writer scenarios) ^^ +| **pthreads function** | **eina equivalent** | +|''pthread_rwlock_init()'' |''eina_rwlock_new()'' | +|''pthread_rwlock_destroy()'' |''eina_rwlock_free()'' | +|''pthread_rwlock_rwlock_rdlock()''|''eina_rwlock_take_read()'' | +|''pthread_rwlock_rwlock_wrlock()''|''eina_rwlock_take_write()'' | +|''pthread_rwlock_unlock()'' |''eina_rwlock_release()'' | + +^ TLS (Thread-Local Storage) ^^ +| **pthreads function** | **eina equivalent** | +|''pthread_key_create()'' |''eina_tls_new()'' | +|''pthread_key_delete()'' |''eina_tls_free()'' | +|''pthread_getspecific()'' |''eina_tls_get()'' | +|''pthread_setspecific()'' |''eina_tls_set()'' | + +^ Semaphores (access restrictions for a set of resources) ^^ +| **pthreads function** | **eina equivalent** | +|''sem_init()'' |''eina_semaphore_new()'' | +|''sem_destroy()'' |''eina_semaphore_free()'' | +|''sem_wait()'' |''eina_semaphore_lock()'' | +|''sem_post()'' |''eina_semaphore_release()'' | + +^ Barriers ^^ +| **pthreads function** | **eina equivalent** | +|''pthread_barrier_init()'' |''eina_barrier_new()'' | +|''pthread_barrier_destroy()''|''eina_barrier_free()'' | +|''pthread_barrier_wait()'' |''eina_barrier_wait()'' | + +------- +{{page>index}} diff --git a/pages/program_guide/threading/thread_management_with_ecore.txt b/pages/program_guide/threading/thread_management_with_ecore.txt new file mode 100644 index 0000000..cee77d4 --- /dev/null +++ b/pages/program_guide/threading/thread_management_with_ecore.txt @@ -0,0 +1,61 @@ +{{page>index}} +===== Thread Management with Ecore ===== + +Ecore offers a simplified API for managing threads in EFL applications. The +Ecore API applies to a typical scenario where the main thread creates another +thread, which in turn sends data back to the main thread or calls GUI-related +functions. GUI-related functions are not thread-safe. + +==== Creating Threads with Ecore ==== + +The threads created with Ecore are by default integrated with the thread pool +and offer simple callback-based ways to interact with the main loop. New +threads are created as needed until the maximum capacity of the thread pool is +reached. + +== To return values to the main thread == + +Use the ''ecore_thread_feedback_run()'' function to send intermediate feedback +from the thread to the main loop. + +== To return only the final value to the main thread == + +To create and run a thread, use the ''ecore_thread_run()'' function. It runs a +function inside a thread from the thread pool and takes care of all the +low-level work. It returns the corresponding thread handler or ''NULL'' on +failure. + +The most common way to return data from one thread to the main thread is +to put a pointer to it in the data. When the thread is aborted or finishes, +either ''func_cancel()'' or ''func_end()'' is called from the main loop. The +functions are running in the simpler context of a single thread running at +once and therefore avoid race-conditions. + +The data pointer approach can only be used when the data is shared between the +one thread and the main thread only. However, this does not prevent you from +using the ''func_end()'' callback to merge the results into a single data +structure. For example, you can add all the values computed by the threads to +an ''Eina_List'', as all the operations on the list happen from a single +thread and therefore one after the other and not concurrently. + +==== Running Callbacks from the Main Loop ==== + +If you are performing operations in another thread and want to update a +progress bar, the update operation must be done from the main thread. The +simplest way is to use the ''ecore_main_loop_thread_safe_call_async()'' +function, which takes a function and some data as parameters and instructs the +main loop to execute the given function with the given data. + +Depending on the kind of thread the function is called from, the process +differs: + + * If the function is called from a thread that is not the main one, the function sends a message to the main loop and returns quickly. The message is processed in order, similarly to others. + * If the function is called from the main thread, the function is called immediately as if it were a direct call. + +If you want to wait until the callback is called and returns, use the +''ecore_main_loop_thread_safe_call_sync()'' function, which is similar but +synchronous. Since it is synchronous, it can also return the value returned by +the callback. + +----- +{{page>index}} diff --git a/pages/program_guide/threading/thread_pools.txt b/pages/program_guide/threading/thread_pools.txt new file mode 100644 index 0000000..c0a862c --- /dev/null +++ b/pages/program_guide/threading/thread_pools.txt @@ -0,0 +1,52 @@ +{{page>index}} +----------- +===== Thread Pools ===== + +Threads are operating system resources: while much lighter than processes, +they still have a cost. Moreover, spawning a thousand threads means that each +of them only gets 1/1000th of the total CPU time: each thread is progressed +slowly and, in the worst case, the system wastes all of its time switching +between threads without doing any actual work. + +Thread pools solve this problem. In thread pools, upto a maximum number of +threads are created on-demand and used to execute tasks. When the tasks are +finished, they are kept alive but sleeping. This avoids the cost of creating +and destroying them. + +In EFL, the thread pool is controlled by a ''thread_max'' parameter, which +defines the maximum number of threads running at the same time. Another +control feature is the ''func_end()'' callback that runs from the main loop +thread after a task has completed and is typically used to extract the data +from the finished task and make it available to the main loop. + +To manage the maximum number of threads: + + + * To retrieve the current value, use the ''ecore_thread_max_get()'' function. + * To set the value, use the ''ecore_thread_max_set()'' function. The value has a maximum of 16 times the CPU count. + * To reset the maximum number of threads, use the ''ecore_thread_max_reset()'' function. + * To get the number of available threads in the pool, use the ''ecore_thread_available_get()'' function. The function returns the current maximum number of threads minus the number of running threads. The number can be a negative value, if the maximum number of threads has been lowered. + +The following figures illustrate the thread pool. The first figure shows the +occupancy of a hypothetical thread pool. There are several tasks, of which 4 +are running. The ''thread_max'' parameter of the pool is 4, and the other +tasks are waiting. There is no thread with its ''func_end()'' callback +currently called. + +{{ :threading_pool_lifecycle_1.png }} + +When a task, applying the sepia filter on image1, finishes, the corresponding +''func_end()'' function is invoked from the main loop. + +{{ :threading_pool_lifecycle_2.png }} + +With the task done, one of the threads from the pool becomes available, and +another thread, adding the reverberation effect on audio3, can run in it. + +{{ :threading_pool_lifecycle_3.png }} + +As long as there are tasks to be done, the thread pool continues the same way, +running tasks in its threads whenever a thread is available. + +----- +{{page>index}} diff --git a/pages/program_guide/threading/thread_safety.txt b/pages/program_guide/threading/thread_safety.txt new file mode 100644 index 0000000..9d5175d --- /dev/null +++ b/pages/program_guide/threading/thread_safety.txt @@ -0,0 +1,30 @@ +{{page>index}} +------- +===== Thread Safety ===== + +If several strings have to work on the same resources, conflicts can happen as +the threads are run in parallel. For example, if thread A modifies several +values while thread B is reading them, it is likely that some of the values +read by B are outdated. Similar issues can happen if both threads are +modifying data concurrently. + +These kinds of conflicts are called race-conditions: depending on which thread +is faster, the output changes and can be incorrect. Avoiding such issues is +called thread safety. Thread safety involves critical sections, which are +blocks of code that operate on shared resources and must not be accessed +concurrently by another thread. + +The usual solution for ensuring exclusive access to shared resources is mutual +exclusion: only 1 thread can operate on the data at any given time. Mutual +exclusion is often implemented through locks. Before attempting to operate on +a shared resource, the thread waits until it can lock something called a mutex +(stands for mutual exclusion), then operates on the resource, and unlocks the +mutex. Operating systems guarantee that only 1 thread can lock a mutex at a +given time: this ensures that only 1 thread operates on the shared resource at +one time. + +For more information on thread safety, see +[[/program_guide/threading/low-level_functions|Low-level Functions]]. + +------ +{{page>index}} diff --git a/pages/program_guide/threading/thread_use_example.txt b/pages/program_guide/threading/thread_use_example.txt new file mode 100644 index 0000000..2e1617e --- /dev/null +++ b/pages/program_guide/threading/thread_use_example.txt @@ -0,0 +1,124 @@ +{{page>index}} +------- +===== Thread Use Example ===== + +The following examples display a window with a label. An auxiliary thread +semi-regularly changes the text of the label. If you want to display a regular +animation, use the Ecore animators described in the +[[/program_guide/main_loop_pg|Main Loop guide]]. + +To use the ''ecore_thread_feedback()'' function: + +**__1__**. Implement the GUI function that sets the text of a label and can be +called from the main thread. + +<code c> +static void +_set_label_text(void *data, Ecore_Thread *thread __UNUSED__, void *msgdata) +{ + char buf[64]; + Evas_Object *label = data; + snprintf(buf, sizeof(buf), "Tick %d", (int)(uintptr_t)msgdata); + elm_object_text_set(label, buf); +} +</code> + +**__2__**. Send the feedback from the other thread using the ''ecore_thread_feedback()'' +function. The following function does nothing besides sending the feedback and +sleeping. + +<code c> +static void +_long_function(void *data __UNUSED__, Ecore_Thread *thread) +{ + int iteration; + // Change the text roughly every 1 second. This is only an example; if you + // want regular animations, use Ecore animators! + for (iteration = 0; ; iteration++) + { + // Since you are running from another thread, you need to take special + // care and instead send data to the main thread and have it run the + // feedback function given when creating the thread + ecore_thread_feedback(thread, (void*)(uintptr_t)iteration); + // Sleep for roughly one second + sleep(1); + } +} +</code> + +**__3__**. Create an end function that is called when the thread exits. In +this example, the end function is called only right before the application +exits. However, if the blocking function is more complex, it can trigger the +end function. + +<code c> +static void +_end_func(void *data, Ecore_Thread *thread __UNUSED__) +{ + Evas_Object *label = data; + elm_object_text_set(label, "Ticks over"); +} +</code> + +**__4__**. Call the ''ecore_thread_feedback_run()'' function to start the thread: + +<code c> +ecore_thread_feedback_run(_long_function, _set_label_text, _end_func, NULL, label, EINA_FALSE); +</code> + +To use the ''ecore_main_loop_thread_safe_call_sync()'' function: + +**__1__**. Implement the GUI function that sets the text of a label and can be +called from the main thread. The function receives data as a structure and +alternatively displays "Tick d" or "Tock d". + +<code c> +struct thd +{ + Evas_Object *label; + Eina_Bool tick_not_tock; + int iteration; +}; + +static void * +_set_label_text_tick_tock(void *data) +{ + char buf[64]; + struct thd *thd = data; + snprintf(buf, sizeof(buf), "%s %d", (thd->tick_not_tock ? "Tick" : "Tock"), thd->iteration); + elm_object_text_set(thd->label, buf); + + return NULL; +} +</code> + +**__2__**. Use the ''ecore_main_loop_thread_safe_call_sync()'' function call +the GUI function. Differentiate between the ticks and the tocks: + +<code c> +static void +_long_function_tick_tock(void *data, Ecore_Thread *thread __UNUSED__) +{ + struct thd *thd = malloc(sizeof(struct thd)); + thd->label = data; + for (thd->iteration = 0; ; (thd->iteration)++) + { + thd->tick_not_tock = EINA_TRUE; + ecore_main_loop_thread_safe_call_sync(_set_label_text_tick_tock, thd); + sleep(1); + thd->tick_not_tock = EINA_FALSE; + ecore_main_loop_thread_safe_call_sync(_set_label_text_tick_tock, thd); + sleep(1); + } + free(thd); +} +</code> + +**__3__**. Start the thread through the ''ecore_thread_run()'' function: + +<code c> +ecore_thread_run(_long_function_tick_tock, _end_func, NULL, label); +</code> + +------- +{{page>index}} diff --git a/pages/program_guide/threading_pg.txt b/pages/program_guide/threading_pg.txt new file mode 100644 index 0000000..7c5d9a9 --- /dev/null +++ b/pages/program_guide/threading_pg.txt @@ -0,0 +1,29 @@ +{{page>index}} +===== Threading Programming Guide ===== + +Threads are concurrent execution environments that are lighter than full-blown +processes because they share some operating system resources. Threads make it +possible to do several things at the same time while using less resources and +offering simpler synchronization and data exchange compared to processes. + +If you move a blocking operation to a separate thread, it cannot block the +event loop and keeps the user interface reactive. Blocking the event loop and +using long-running callbacks means the application cannot update its graphical +user interface. + +While threads can be useful, they are not always the best choice: + + * The first rule to using threads is to avoid them as much as possible, as there are often better tools and approaches. For example, to do network transfers, use ''Ecore_Con'' that integrates with the event loop to use a function based on callbacks. Being able to use such a function means that specific support work has been done in libraries. In some cases, using a function and libraries is impossible, and in those situations threads are required. + * Use threads in CPU-intensive tasks and disk IOs. For example, a thread is the appropriate way to apply filters to an image or to a video without blocking the interface. + * Use threads to take advantage of multiple available CPU cores, if the workload can be split into several units of work and spread across the cores. A typical example for an image processing application on a quad-core CPU is to process 4 images at once, each on 1 thread. For such tasks, the thread pool helps with the creation and scheduling of the threads, handling all the grunt work. + +=== Table of Contents === + + * [[/program_guide/threading/thread_safety|Thread Safety]] + * [[/program_guide/threading/thread_pools|Thread Pools]] + * [[/program_guide/threading/thread_management_with_ecore|Thread Management with ECore]] + * [[/program_guide/threading/low-level_functions|Low-level Functions]] + * [[/program_guide/threading/thread_use_examples|Thread Use Example]] + +-------- +{{page>index}} --
