.. _ThreadArgs:

.. raw:: html

   <script>ODSA.SETTINGS.DISP_MOD_COMP = true;ODSA.SETTINGS.MODULE_NAME = "ThreadArgs";ODSA.SETTINGS.MODULE_LONG_NAME = "Thread Arguments and Return Values";ODSA.SETTINGS.MODULE_CHAPTER = "Concurrency with Multithreading"; ODSA.SETTINGS.BUILD_DATE = "2021-06-01 12:51:47"; ODSA.SETTINGS.BUILD_CMAP = false;JSAV_OPTIONS['lang']='en';JSAV_EXERCISE_OPTIONS['code']='java';</script>


.. |--| unicode:: U+2013   .. en dash
.. |---| unicode:: U+2014  .. em dash, trimming surrounding whitespace
   :trim:


.. This file is part of the OpenCSF eTextbook project. It was
.. auto-generated by scripts from the OpenDSA eTextbook project.
.. See https://OpenCSF.org for more details. OpenCSF is distributed
.. under a Creative Commons Attribution-NonCommercial 4.0 International
.. License (see http://creativecommons.org/licenses/by-nc/4.0/),
.. Copyright (c) 2019-2021 by Michael S. Kirkpatrick. OpenDSA is
.. distributed under an MIT open source license, Copyright (c) 2012-2021
.. by the OpenDSA Project Contributors.

.. avmetadata::
   :author: Michael S. Kirkpatrick
   :requires:
   :satisfies: 
   :topic: 

Thread Arguments and Return Values
==================================

The ``pthread_create()`` imposes a strict format on the prototype of the
function that will run in the new thread. It must take a single ``void*``
parameter and return a single ``void*`` value. The last parameter of
``pthread_create()`` is passed as the argument to the function, whereas the
return value is passed using ``pthread_exit()`` and ``pthread_join()``. This
section looks at the details of these mechanisms and their implications.

Passing a Single Argument to Threads
------------------------------------

Passing a single argument to a thread seems straightforward, but is easy to do
incorrectly. As a simple example to illustrate the danger, `Code Listing 6.5 <#cl6-5>`_
is designed to run in a separate thread:

.. _cl6-5:

.. codeinclude:: Threads/CodeListing-6-5.c
   :linenos: true

The danger of this code can be illustrated with the loop in `Code Listing 6.6 <#cl6-6>`_.
The intent is to pass the value 1 to the first thread, 2 to the
second, and so on. However, it is critical to note that **there is only a single
copy of the** ``i`` **variable**. That is, this code passes the address of the
single variable to all 10 threads; the code almost certainly does not pass the
intended values.

.. _cl6-6:

.. codeinclude:: Threads/CodeListing-6-6.c
   :linenos: true

The key problem is that thread creation and execution is :term:`asynchronous`.
That means that it is impossible to predict when each of the new threads start
running. One possible timing is that all 10 threads are created first, leading
to ``i`` storing the value 11. At that point, each of the threads dereference
their respective ``argptr`` variable and all get the same value of 11.

One common solution to this problem is to cast numeric values as pointers, as
shown in `Code Listing 6.7 <#cl6-7>`_. That is, the int ``i`` variable gets cast
as a ``(void*)`` argument in the call to ``pthread_create()``. Then, the
``void*`` argument to ``child_thread()`` casts the argument back to a ``int`` instance.

.. _cl6-7:

.. codeinclude:: Threads/CodeListing-6-7.c
   :linenos: true

What makes this code work is the fact that scalar variables (e.g., ``int``
variables) are passed using call-by-value semantics. When this code prepares for
the ``pthread_create()`` call, a separate copy of the current value of the ``i``
variable is placed into a register or onto the stack. `Code Listing 6.8
<#cl6-8>`_ shows the corrected version of `Code Listing 6.5 <#cl6-5>`_. The
``child_thread()`` function then gets this copy, regardless of any changes to
the original ``i`` variable. When the child thread then casts its ``args``
parameter to a local ``arg_value``, it is working with the correct value that was passed.

.. _cl6-8:

.. codeinclude:: Threads/CodeListing-6-8.c
   :linenos: true

.. topic:: Bug Warning

   .. figure:: Images/CSF-Images-BugWarning.png
      :align: left
      :width: 90%
      :alt: Decorative bug warning

   Casting integral values to pointers and back again is a common practice for
   passing parameters to pthreads. However, while it is generally safe in
   practice, it is potentially a bug on some platforms. Specifically, this
   technique relies on the fact that pointers are at least as large as standard
   integer types. That is, ``int`` variables are typically (but not required to
   be) 32 bits in size. Modern CPU architectures tend to use 32- or 64-bit
   addresses. As such, casting a 32-bit ``int`` up to a ``void*`` then back to a
   32-bit ``int`` is safe.

   On the other hand, assume the argument was declared as a ``long`` variable
   instance. If the code is running on a 32-bit architecture (which is not
   uncommon for virtualized systems) but the ``long`` type is 64 bits in size,
   then half of the argument is lost by down-casting to the pointer for the call
   to ``pthread_create()``!
 

Passing Multiple Arguments to Threads
-------------------------------------

When passing multiple arguments to a child thread, the standard approach is to
group the arguments within a ``struct`` declaration, as shown in `Code Listing
6.9 <#cl6-9>`_. The address of the ``struct`` instance gets passed as the
``arg`` to ``pthread_create()``. The new thread's entry point receives a
``void*`` parameter that can then be cast into the ``struct`` type.

.. _cl6-9:

.. codeinclude:: Threads/CodeListing-6-9.c
   :linenos: true

`Code Listing 6.10 <#cl6-10>`_ shows the new thread receiving the pointer to the
``struct`` and freeing the allocated memory when it is finished with the data.

.. _cl6-10:

.. codeinclude:: Threads/CodeListing-6-10.c
   :linenos: true

.. topic:: Bug Warning

   .. figure:: Images/CSF-Images-BugWarning.png
      :align: left
      :width: 90%
      :alt: Decorative bug warning

   A common mistake with passing arguments in this manner is to declare the
   ``struct`` instance as a local variable instead of using dynamic allocation.
   The problem, again, is the asynchronous nature of ``pthread_create()``.
   Consider this sample code:

   .. codeinclude:: Threads/BugWarning-6-2.c
      :linenos: true

If the child thread runs immediately before ``pthread_create()`` returns, then
everything would be fine. However, there is no guarantee that this happens.
Instead, it is just as likely that ``pthread_create()`` returns and the parent
thread exits. Once that happens, all data on the parent thread's stack
(including the ``struct thread_args`` instance) become invalid. The child thread
now has a dangling pointer to potentially corrupted data. This is another
example of a race condition that can happen with threads.

Returning Values from Threads
-----------------------------

There are three common ways to get return values back from a thread. All three
use techniques that are similar to those used for passing arguments.
`Code Listing 6.11 <#cl6-11>`_ shows one simple technique, which is to augment
the ``struct`` declaration to include space for any return values.

.. _cl6-11:

.. codeinclude:: Threads/CodeListing-6-11.c
   :linenos: true

The child thread receives a pointer to the ``struct`` instance, using the input
parameters as needed. In this case, the values of ``a`` and ``b`` are added, and
the resulting sum is copied back into the ``struct``. As shown in `Code Listing
6.12 <#cl6-12>`_, the main thread uses ``pthread_join()`` to wait until the
child thread exits. Once the child finishes, the main thread can retrieve all
three values (``a``, ``b``, and ``sum``) from the ``struct`` itself.

.. _cl6-12:

.. codeinclude:: Threads/CodeListing-6-12.c
   :linenos: true

There are three key observations about this approach:

   * The main and the child threads have access to both the input and the output.
     This fact means that the main thread has information about how this particular
     child thread was invoked. If the main thread is keeping track of many threads,
     this additional information may be helpful.
   * Responsibility for memory management resides in one location: the main thread.
     If responsibility is split between the programmer maintaining the main thread
     and the programmer maintaining the child thread, there is the possibility for
     miscommunication leading to memory leaks (or worse, premature de-allocation).
   * The major disadvantage of this approach is that the input parameters may be
     kept on the heap for much longer than needed, particularly if the child thread
     runs for a significant amount of time.

`Code Listing 6.13 <#cl6-13>`_ shows an alternative approach for simple scalar
return types, which is to reuse the trick of casting to and from the ``void*``
type. When a thread calls ``pthread_exit()``, it can specify a pointer to return
as an argument.

.. _cl6-13:

.. codeinclude:: Threads/CodeListing-6-13.c
   :linenos: true

`Code Listing 6.14 <#cl6-14>`_ shows how the main thread calls
``pthread_join()`` to retrieve the pointer. Unless the thread has been detached
(or it was created with the ``PTHREAD_CREATE_DETACHED`` attribute), the pointer
returned with ``pthread_exit()`` will remain associated with the thread until it
is joined.

.. _cl6-14:

.. codeinclude:: Threads/CodeListing-6-14.c
   :linenos: true

`Code Listing 6.15 <#cl6-15>`_ shows a third approach to returning values from
the thread. In this style, the child thread allocates a separate ``struct``
dynamically to hold the return values. This technique allows a thread to return
multiple values rather than a single scalar. For instance, consider the
following ``calculator`` thread. It receives two ``int`` values as input and
returns the results of five simple arithmetic operations.

.. _cl6-15:

.. codeinclude:: Threads/CodeListing-6-15.c
   :linenos: true

It is critical to note that the struct instance here must be allocated
dynamically. Once the thread calls ``pthread_exit()``, everything on its stack
becomes invalid. A thread should never pass a pointer to a local variable with
``pthread_exit()``.

Retrieving the returned data can be accomplished with ``pthread_join()``. In the
following example, the main thread creates five separate instances of the
``calculator`` thread. Each of these child threads gets a pointer to a unique
``struct args`` instance with the corresponding parameters. Each child then
allocates its own ``struct results`` instance on the heap. This allows the data
to persist after the thread has finished. In `Code Listing 6.14 <#cl6-14>`_, the
main thread gets each thread's pointer one at a time, with a separate call to
``pthread_join()``. Since the child thread has already finished at this point,
the main thread must bear the responsibility for calling ``free()`` to
de-allocate the ``struct`` results instance.

.. _cl6-16:

.. codeinclude:: Threads/CodeListing-6-16.c
   :linenos: true

.. topic:: Bug Warning

   .. figure:: Images/CSF-Images-BugWarning.png
      :align: left
      :width: 90%
      :alt: Decorative bug warning

   All of the functions for creating threads, passing arguments, and getting return
   values involve a lot of pointers. Furthermore, the pointers are dereferenced
   and manipulated asynchronously because of the nature of multithreading. It is
   vital to remember the types and lifetimes of each pointer and the corresponding data structure.

      * The first parameter for ``pthread_create()`` is a ``pthread_t*``. The argument
        should typically be an existing ``pthread_t`` passed by reference with the ``&`` operator.
      * The final parameter to ``pthread_create()`` must either be a scalar (cast as a
        pointer) or a pointer to data that persists until the child thread runs. That
        is, the target of the pointer must not be modified by the main thread until the
        child thread has been joined (to guarantee the child has run).
      * The parameter to ``pthread_exit()`` must be a scalar value (cast as a pointer)
        or a pointer to non-stack data. The data must be guaranteed to be valid even
        after the thread has been completely destroyed.
      * The final parameter to ``pthread_join()`` must be a pointer that is passed by
        reference. That is, ``pthread_join()`` will change this pointer to point to the
        returned data structure.
      
.. avembed:: Exercises/Threads/ThreadArgsSumm.html ka
   :module: ThreadArgs
   :points: 1.0
   :required: True
   :exer_opts: JXOP-debug=true&amp;JOP-lang=en&amp;JXOP-code=java
   :long_name: Thread argument questions
   :threshold: 3

