mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-30 19:53:46 +00:00
108 lines
7.5 KiB
HTML
108 lines
7.5 KiB
HTML
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|||
|
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Multiple Thread Example</title><meta name="generator" content="DocBook XSL-NS Stylesheets V1.76.1"/><meta name="keywords" content=" ISO C++ , allocator "/><meta name="keywords" content=" ISO C++ , library "/><meta name="keywords" content=" ISO C++ , runtime , library "/><link rel="home" href="../index.html" title="The GNU C++ Library"/><link rel="up" href="mt_allocator.html" title="Chapter 20. The mt_allocator"/><link rel="prev" href="bk01pt03ch20s04.html" title="Single Thread Example"/><link rel="next" href="bitmap_allocator.html" title="Chapter 21. The bitmap_allocator"/></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Multiple Thread Example</th></tr><tr><td align="left"><a accesskey="p" href="bk01pt03ch20s04.html">Prev</a> </td><th width="60%" align="center">Chapter 20. The mt_allocator</th><td align="right"> <a accesskey="n" href="bitmap_allocator.html">Next</a></td></tr></table><hr/></div><div class="section" title="Multiple Thread Example"><div class="titlepage"><div><div><h2 class="title"><a id="allocator.mt.example_multi"/>Multiple Thread Example</h2></div></div></div><p>
|
|||
|
In the ST example we never used the thread_id variable present in each block.
|
|||
|
Let's start by explaining the purpose of this in a MT application.
|
|||
|
</p><p>
|
|||
|
The concept of "ownership" was introduced since many MT applications
|
|||
|
allocate and deallocate memory to shared containers from different
|
|||
|
threads (such as a cache shared amongst all threads). This introduces
|
|||
|
a problem if the allocator only returns memory to the current threads
|
|||
|
freelist (I.e., there might be one thread doing all the allocation and
|
|||
|
thus obtaining ever more memory from the system and another thread
|
|||
|
that is getting a longer and longer freelist - this will in the end
|
|||
|
consume all available memory).
|
|||
|
</p><p>
|
|||
|
Each time a block is moved from the global list (where ownership is
|
|||
|
irrelevant), to a threads freelist (or when a new freelist is built
|
|||
|
from a chunk directly onto a threads freelist or when a deallocation
|
|||
|
occurs on a block which was not allocated by the same thread id as the
|
|||
|
one doing the deallocation) the thread id is set to the current one.
|
|||
|
</p><p>
|
|||
|
What's the use? Well, when a deallocation occurs we can now look at
|
|||
|
the thread id and find out if it was allocated by another thread id
|
|||
|
and decrease the used counter of that thread instead, thus keeping the
|
|||
|
free and used counters correct. And keeping the free and used counters
|
|||
|
corrects is very important since the relationship between these two
|
|||
|
variables decides if memory should be returned to the global pool or
|
|||
|
not when a deallocation occurs.
|
|||
|
</p><p>
|
|||
|
When the application requests memory (calling allocate()) we first
|
|||
|
look at the requested size and if this is >_S_max_bytes we call new()
|
|||
|
directly and return.
|
|||
|
</p><p>
|
|||
|
If the requested size is within limits we start by finding out from which
|
|||
|
bin we should serve this request by looking in _S_binmap.
|
|||
|
</p><p>
|
|||
|
A call to _S_get_thread_id() returns the thread id for the calling thread
|
|||
|
(and if no value has been set in _S_thread_key, a new id is assigned and
|
|||
|
returned).
|
|||
|
</p><p>
|
|||
|
A quick look at _S_bin[ bin ].first[ thread_id ] tells us if there are
|
|||
|
any blocks of this size on the current threads freelist. If this is
|
|||
|
not NULL - fine, just remove the block that _S_bin[ bin ].first[
|
|||
|
thread_id ] points to from the list, update _S_bin[ bin ].first[
|
|||
|
thread_id ], update the free and used counters and return a pointer to
|
|||
|
that blocks data.
|
|||
|
</p><p>
|
|||
|
If the freelist is empty (the pointer is NULL) we start by looking at
|
|||
|
the global freelist (0). If there are blocks available on the global
|
|||
|
freelist we lock this bins mutex and move up to block_count (the
|
|||
|
number of blocks of this bins size that will fit into a _S_chunk_size)
|
|||
|
or until end of list - whatever comes first - to the current threads
|
|||
|
freelist and at the same time change the thread_id ownership and
|
|||
|
update the counters and pointers. When the bins mutex has been
|
|||
|
unlocked, we remove the block that _S_bin[ bin ].first[ thread_id ]
|
|||
|
points to from the list, update _S_bin[ bin ].first[ thread_id ],
|
|||
|
update the free and used counters, and return a pointer to that blocks
|
|||
|
data.
|
|||
|
</p><p>
|
|||
|
The reason that the number of blocks moved to the current threads
|
|||
|
freelist is limited to block_count is to minimize the chance that a
|
|||
|
subsequent deallocate() call will return the excess blocks to the
|
|||
|
global freelist (based on the _S_freelist_headroom calculation, see
|
|||
|
below).
|
|||
|
</p><p>
|
|||
|
However if there isn't any memory on the global pool we need to get
|
|||
|
memory from the system - this is done in exactly the same way as in a
|
|||
|
single threaded application with one major difference; the list built
|
|||
|
in the newly allocated memory (of _S_chunk_size size) is added to the
|
|||
|
current threads freelist instead of to the global.
|
|||
|
</p><p>
|
|||
|
The basic process of a deallocation call is simple: always add the
|
|||
|
block to the front of the current threads freelist and update the
|
|||
|
counters and pointers (as described earlier with the specific check of
|
|||
|
ownership that causes the used counter of the thread that originally
|
|||
|
allocated the block to be decreased instead of the current threads
|
|||
|
counter).
|
|||
|
</p><p>
|
|||
|
And here comes the free and used counters to service. Each time a
|
|||
|
deallocation() call is made, the length of the current threads
|
|||
|
freelist is compared to the amount memory in use by this thread.
|
|||
|
</p><p>
|
|||
|
Let's go back to the example of an application that has one thread
|
|||
|
that does all the allocations and one that deallocates. Both these
|
|||
|
threads use say 516 32-byte blocks that was allocated during thread
|
|||
|
creation for example. Their used counters will both say 516 at this
|
|||
|
point. The allocation thread now grabs 1000 32-byte blocks and puts
|
|||
|
them in a shared container. The used counter for this thread is now
|
|||
|
1516.
|
|||
|
</p><p>
|
|||
|
The deallocation thread now deallocates 500 of these blocks. For each
|
|||
|
deallocation made the used counter of the allocating thread is
|
|||
|
decreased and the freelist of the deallocation thread gets longer and
|
|||
|
longer. But the calculation made in deallocate() will limit the length
|
|||
|
of the freelist in the deallocation thread to _S_freelist_headroom %
|
|||
|
of it's used counter. In this case, when the freelist (given that the
|
|||
|
_S_freelist_headroom is at it's default value of 10%) exceeds 52
|
|||
|
(516/10) blocks will be returned to the global pool where the
|
|||
|
allocating thread may pick them up and reuse them.
|
|||
|
</p><p>
|
|||
|
In order to reduce lock contention (since this requires this bins
|
|||
|
mutex to be locked) this operation is also made in chunks of blocks
|
|||
|
(just like when chunks of blocks are moved from the global freelist to
|
|||
|
a threads freelist mentioned above). The "formula" used can probably
|
|||
|
be improved to further reduce the risk of blocks being "bounced back
|
|||
|
and forth" between freelists.
|
|||
|
</p></div><div class="navfooter"><hr/><table width="100%" summary="Navigation footer"><tr><td align="left"><a accesskey="p" href="bk01pt03ch20s04.html">Prev</a> </td><td align="center"><a accesskey="u" href="mt_allocator.html">Up</a></td><td align="right"> <a accesskey="n" href="bitmap_allocator.html">Next</a></td></tr><tr><td align="left" valign="top">Single Thread Example </td><td align="center"><a accesskey="h" href="../index.html">Home</a></td><td align="right" valign="top"> Chapter 21. The bitmap_allocator</td></tr></table></div></body></html>
|