<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ro">
	<id>http://wiki.dcae.pub.ro/api.php?hidebots=1&amp;urlversion=1&amp;days=7&amp;limit=50&amp;action=feedrecentchanges&amp;feedformat=atom</id>
	<title>WikiLabs  - Schimbări recente [ro]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.dcae.pub.ro/api.php?hidebots=1&amp;urlversion=1&amp;days=7&amp;limit=50&amp;action=feedrecentchanges&amp;feedformat=atom"/>
	<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php/Special:Schimb%C4%83ri_recente"/>
	<updated>2026-11-10T17:37:28Z</updated>
	<subtitle>Urmărește cele mai recente schimbări folosind acest flux.</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Arhitectura_Sistemelor_de_Calcul_(curs)&amp;diff=8446&amp;oldid=8430</id>
		<title>Arhitectura Sistemelor de Calcul (curs)</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Arhitectura_Sistemelor_de_Calcul_(curs)&amp;diff=8446&amp;oldid=8430"/>
		<updated>2026-11-09T20:27:08Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left diff-editfont-monospace&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ro&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versiunea anterioară&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versiunea de la data 9 noiembrie 2026 20:27&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l40&quot; &gt;Linia 40:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Linia 40:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=MUWAbpg1xLo CPU vs GPU vs TPU]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=MUWAbpg1xLo CPU vs GPU vs TPU]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=KVOc6369-Lo Flynn&amp;#039;s Taxonomy: SISD, SIMD, MISD &amp;amp; MIMD]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=KVOc6369-Lo Flynn&amp;#039;s Taxonomy: SISD, SIMD, MISD &amp;amp; MIMD]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt; &lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;* [https://www.youtube.com/watch?v=SWec3n9Jzh8 SISD, SIMD, MISD, MIMD Explained: Flynn&amp;#039;s Taxonomy &amp;amp; Shared vs Distributed Memory]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=PgDaJEjlBuI Multithreading vs Multiprocessing]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=PgDaJEjlBuI Multithreading vs Multiprocessing]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=0ttx0AuNezo Superscalar vs Superpipelined]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=0ttx0AuNezo Superscalar vs Superpipelined]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=6tbNew87fZU All CPU Architectures from von Neumann to Hybrid Multicores]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [https://www.youtube.com/watch?v=6tbNew87fZU All CPU Architectures from von Neumann to Hybrid Multicores]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Zhascsi</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:CID2_Platforma_APB_II.pdf&amp;diff=8445&amp;oldid=0</id>
		<title>Fișier:CID2 Platforma APB II.pdf</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:CID2_Platforma_APB_II.pdf&amp;diff=8445&amp;oldid=0"/>
		<updated>2026-11-09T09:33:33Z</updated>

		<summary type="html">&lt;p&gt;&lt;a href=&quot;/index.php?title=Utilizator:Gvpopescu&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;Utilizator:Gvpopescu (pagină inexistentă)&quot;&gt;&lt;bdi&gt;Gvpopescu&lt;/bdi&gt;&lt;/a&gt; a încărcat &lt;a href=&quot;/index.php/Fi%C8%99ier:CID2_Platforma_APB_II.pdf&quot; title=&quot;Fișier:CID2 Platforma APB II.pdf&quot;&gt;Fișier:CID2 Platforma APB II.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Gvpopescu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Circuite_Integrate_Digitale_2&amp;diff=8444&amp;oldid=8409</id>
		<title>Circuite Integrate Digitale 2</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Circuite_Integrate_Digitale_2&amp;diff=8444&amp;oldid=8409"/>
		<updated>2026-11-09T09:33:18Z</updated>

		<summary type="html">&lt;p&gt;&lt;span dir=&quot;auto&quot;&gt;&lt;span class=&quot;autocomment&quot;&gt;Platforme laborator&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left diff-editfont-monospace&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ro&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versiunea anterioară&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versiunea de la data 9 noiembrie 2026 09:33&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;4&quot; class=&quot;diff-multi&quot; lang=&quot;ro&quot;&gt;(Nu s-a afișat o versiune intermediară efectuată de același utilizator)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l1&quot; &gt;Linia 1:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Linia 1:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Platforme laborator ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Platforme laborator ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt;−&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [[Media:CID2_lab_1_2.pdf|&lt;del class=&quot;diffchange diffchange-inline&quot;&gt;Platformă lab &lt;/del&gt;1+2]]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* [[Media:CID2_lab_1_2.pdf|&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;Lucrarea &lt;/ins&gt;1 + 2&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt; &lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;* [[Media:CID2_Platforma_APB_I.pdf|Lucrarea 3]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt; &lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;* [[Media:CID2_Platforma_APB_II.pdf|Lucrarea 4&lt;/ins&gt;]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Gvpopescu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:CID2_Platforma_APB_I.pdf&amp;diff=8442&amp;oldid=0</id>
		<title>Fișier:CID2 Platforma APB I.pdf</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:CID2_Platforma_APB_I.pdf&amp;diff=8442&amp;oldid=0"/>
		<updated>2026-11-09T09:31:03Z</updated>

		<summary type="html">&lt;p&gt;&lt;a href=&quot;/index.php?title=Utilizator:Gvpopescu&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;Utilizator:Gvpopescu (pagină inexistentă)&quot;&gt;&lt;bdi&gt;Gvpopescu&lt;/bdi&gt;&lt;/a&gt; a încărcat &lt;a href=&quot;/index.php/Fi%C8%99ier:CID2_Platforma_APB_I.pdf&quot; title=&quot;Fișier:CID2 Platforma APB I.pdf&quot;&gt;Fișier:CID2 Platforma APB I.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Gvpopescu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_deadlock.png&amp;diff=8441&amp;oldid=0</id>
		<title>Fișier:OS Lab 5 - deadlock.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_deadlock.png&amp;diff=8441&amp;oldid=0"/>
		<updated>2026-11-06T14:15:12Z</updated>

		<summary type="html">&lt;p&gt;&lt;a href=&quot;/index.php?title=Utilizator:Vserbu&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;Utilizator:Vserbu (pagină inexistentă)&quot;&gt;&lt;bdi&gt;Vserbu&lt;/bdi&gt;&lt;/a&gt; a încărcat &lt;a href=&quot;/index.php/Fi%C8%99ier:OS_Lab_5_-_deadlock.png&quot; title=&quot;Fișier:OS Lab 5 - deadlock.png&quot;&gt;Fișier:OS Lab 5 - deadlock.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_futex.png&amp;diff=8440&amp;oldid=0</id>
		<title>Fișier:OS Lab 5 - futex.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_futex.png&amp;diff=8440&amp;oldid=0"/>
		<updated>2026-11-06T14:14:29Z</updated>

		<summary type="html">&lt;p&gt;&lt;a href=&quot;/index.php?title=Utilizator:Vserbu&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;Utilizator:Vserbu (pagină inexistentă)&quot;&gt;&lt;bdi&gt;Vserbu&lt;/bdi&gt;&lt;/a&gt; a încărcat &lt;a href=&quot;/index.php/Fi%C8%99ier:OS_Lab_5_-_futex.png&quot; title=&quot;Fișier:OS Lab 5 - futex.png&quot;&gt;Fișier:OS Lab 5 - futex.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_lost_update.png&amp;diff=8439&amp;oldid=0</id>
		<title>Fișier:OS Lab 5 - lost update.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_lost_update.png&amp;diff=8439&amp;oldid=0"/>
		<updated>2026-11-06T14:13:58Z</updated>

		<summary type="html">&lt;p&gt;&lt;a href=&quot;/index.php?title=Utilizator:Vserbu&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;Utilizator:Vserbu (pagină inexistentă)&quot;&gt;&lt;bdi&gt;Vserbu&lt;/bdi&gt;&lt;/a&gt; a încărcat &lt;a href=&quot;/index.php/Fi%C8%99ier:OS_Lab_5_-_lost_update.png&quot; title=&quot;Fișier:OS Lab 5 - lost update.png&quot;&gt;Fișier:OS Lab 5 - lost update.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_threads.png&amp;diff=8438&amp;oldid=0</id>
		<title>Fișier:OS Lab 5 - threads.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:OS_Lab_5_-_threads.png&amp;diff=8438&amp;oldid=0"/>
		<updated>2026-11-06T14:07:56Z</updated>

		<summary type="html">&lt;p&gt;&lt;a href=&quot;/index.php?title=Utilizator:Vserbu&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new mw-userlink&quot; title=&quot;Utilizator:Vserbu (pagină inexistentă)&quot;&gt;&lt;bdi&gt;Vserbu&lt;/bdi&gt;&lt;/a&gt; a încărcat &lt;a href=&quot;/index.php/Fi%C8%99ier:OS_Lab_5_-_threads.png&quot; title=&quot;Fișier:OS Lab 5 - threads.png&quot;&gt;Fișier:OS Lab 5 - threads.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Threads._Mutexes&amp;diff=8437&amp;oldid=0</id>
		<title>OS Lab 5 - Threads. Mutexes</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=OS_Lab_5_-_Threads._Mutexes&amp;diff=8437&amp;oldid=0"/>
		<updated>2026-11-06T14:04:59Z</updated>

		<summary type="html">&lt;p&gt;Pagină nouă: In &lt;a href=&quot;/index.php/OS_Lab_4_-_Processes._Signals&quot; title=&quot;OS Lab 4 - Processes. Signals&quot;&gt;the last lab&lt;/a&gt;, we created new processes with [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], and each of them had a si...&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pagină nouă&lt;/b&gt;&lt;/p&gt;&lt;div&gt;In [[OS_Lab_4_-_Processes._Signals|the last lab]], we created new processes with [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;], and each of them had a single task. In this lab, we will give one process several tasks, its &amp;#039;&amp;#039;&amp;#039;threads&amp;#039;&amp;#039;&amp;#039;, which share its memory. We will create them with [https://man7.org/linux/man-pages/man3/pthread_create.3.html &amp;lt;code&amp;gt;pthread_create(3)&amp;lt;/code&amp;gt;], wait for them with [https://man7.org/linux/man-pages/man3/pthread_join.3.html &amp;lt;code&amp;gt;pthread_join(3)&amp;lt;/code&amp;gt;], and protect the data they share with &amp;#039;&amp;#039;&amp;#039;mutexes&amp;#039;&amp;#039;&amp;#039;. We will also see what these functions ask of the kernel: [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/futex.2.html &amp;lt;code&amp;gt;futex(2)&amp;lt;/code&amp;gt;]. As an application, we will simulate a small bank.&lt;br /&gt;
&lt;br /&gt;
== Learning objectives ==&lt;br /&gt;
&lt;br /&gt;
By the end of this lab you should be able to:&lt;br /&gt;
&lt;br /&gt;
# create threads with [https://man7.org/linux/man-pages/man3/pthread_create.3.html &amp;lt;code&amp;gt;pthread_create(3)&amp;lt;/code&amp;gt;], and wait for them with [https://man7.org/linux/man-pages/man3/pthread_join.3.html &amp;lt;code&amp;gt;pthread_join(3)&amp;lt;/code&amp;gt;];&lt;br /&gt;
# explain which [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;] flags make a new task a thread of the same process;&lt;br /&gt;
# recognize a &amp;#039;&amp;#039;&amp;#039;race&amp;#039;&amp;#039;&amp;#039; on shared data, and fix it with a mutex;&lt;br /&gt;
# explain how a mutex uses [https://man7.org/linux/man-pages/man2/futex.2.html &amp;lt;code&amp;gt;futex(2)&amp;lt;/code&amp;gt;], and why an uncontended lock needs no system call;&lt;br /&gt;
# recognize a &amp;#039;&amp;#039;&amp;#039;deadlock&amp;#039;&amp;#039;&amp;#039;, and avoid it by always locking in the same order.&lt;br /&gt;
&lt;br /&gt;
== Threads ==&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;thread&amp;#039;&amp;#039;&amp;#039; is a task which shares memory with other tasks in a task group (process). All the threads of a process run the same program and see the same global variables and heap, but each has its own registers and its own stack, so each can be at a different point of the program, in a different function.&lt;br /&gt;
&lt;br /&gt;
On Linux, threads are created and managed with the &amp;#039;&amp;#039;POSIX threads&amp;#039;&amp;#039; (&amp;#039;&amp;#039;pthreads&amp;#039;&amp;#039;) functions, which are part of &amp;#039;&amp;#039;glibc&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
=== pthread_create(3) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,&lt;br /&gt;
                   void *(*start_routine)(void *), void *arg);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;thread&amp;lt;/code&amp;gt; || Where to store the ID of the new thread, used later to wait for it&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; || Options, such as the size of the stack, or &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt; for the defaults&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;start_routine&amp;lt;/code&amp;gt; || The function the new thread runs. When it returns, the thread ends&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt; || The argument passed to &amp;lt;code&amp;gt;start_routine&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The new thread starts running &amp;lt;code&amp;gt;start_routine(arg)&amp;lt;/code&amp;gt;, while the thread that created it continues after [https://man7.org/linux/man-pages/man3/pthread_create.3.html &amp;lt;code&amp;gt;pthread_create(3)&amp;lt;/code&amp;gt;]. Both &amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt; and the return value of &amp;lt;code&amp;gt;start_routine&amp;lt;/code&amp;gt; are &amp;lt;code&amp;gt;void *&amp;lt;/code&amp;gt;, so a thread can be given, and can give back, a pointer to anything.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; The pthreads functions do &amp;#039;&amp;#039;&amp;#039;not&amp;#039;&amp;#039;&amp;#039; set &amp;lt;code&amp;gt;errno&amp;lt;/code&amp;gt;. They return 0 on success, or the error number itself (for example, &amp;lt;code&amp;gt;EAGAIN&amp;lt;/code&amp;gt;) on error. The examples below use a &amp;lt;code&amp;gt;print_error()&amp;lt;/code&amp;gt; which takes that number as a parameter.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following program starts three threads, gives each one its own number, and each of them prints its PID and its &amp;#039;&amp;#039;&amp;#039;TID&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;thread ID&amp;#039;&amp;#039;), the ID of the task:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define N_THREADS 3&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what, int err)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(err));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static void *worker(void *arg)&lt;br /&gt;
{&lt;br /&gt;
    int id = *(int *) arg;&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;thread %d: PID %d, TID %d\n&amp;quot;, id, getpid(), gettid());&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return nullptr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pthread_t threads[N_THREADS];&lt;br /&gt;
    int ids[N_THREADS];&lt;br /&gt;
&lt;br /&gt;
    for (int i = 0; i &amp;lt; N_THREADS; i++) {&lt;br /&gt;
        ids[i] = i;&lt;br /&gt;
        int err = pthread_create(&amp;amp;threads[i], nullptr, worker, &amp;amp;ids[i]);&lt;br /&gt;
        if (err != 0) {&lt;br /&gt;
            print_error(&amp;quot;pthread_create&amp;quot;, err);&lt;br /&gt;
            return EXIT_FAILURE;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    for (int i = 0; i &amp;lt; N_THREADS; i++) {&lt;br /&gt;
        pthread_join(threads[i], nullptr);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;main:     PID %d, TID %d\n&amp;quot;, getpid(), gettid());&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
thread 0: PID 4307, TID 4308&lt;br /&gt;
thread 1: PID 4307, TID 4309&lt;br /&gt;
thread 2: PID 4307, TID 4310&lt;br /&gt;
main:     PID 4307, TID 4307&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All the threads have the same PID: they are one process. Each has its own TID, and the first thread of the process, the one which runs &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;, has a TID equal to the PID. The three lines may come in any order: the threads run independently.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Each thread is given a pointer to its own element of &amp;lt;code&amp;gt;ids&amp;lt;/code&amp;gt;. Passing &amp;lt;code&amp;gt;&amp;amp;i&amp;lt;/code&amp;gt; instead would be a bug: all the threads would get a pointer to the same variable, which the loop keeps changing, leading to a race condition on access.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== pthread_join(3) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
int pthread_join(pthread_t thread, void **retval);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man3/pthread_join.3.html &amp;lt;code&amp;gt;pthread_join(3)&amp;lt;/code&amp;gt;] waits for a thread to end, the way [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;] waits for a child process. If &amp;lt;code&amp;gt;retval&amp;lt;/code&amp;gt; is not &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt;, it stores there the value returned by the thread&amp;#039;s function.&lt;br /&gt;
&lt;br /&gt;
A simpler way to get results back is to give each thread a structure, with both its input and a place for its output. The following program adds up the numbers from 0 to 99,999,999 with four threads, each adding a quarter of them:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define N_THREADS 4&lt;br /&gt;
#define N 100000000&lt;br /&gt;
&lt;br /&gt;
static void print_error(const char *what, int err)&lt;br /&gt;
{&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;%s: %s\n&amp;quot;, what, strerror(err));&lt;br /&gt;
    write(STDERR_FILENO, msg, strlen(msg));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// What one thread works on: its input, and a place for its result.&lt;br /&gt;
struct part {&lt;br /&gt;
    long from, to; // Add the numbers in [from, to)&lt;br /&gt;
    long sum;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static void *add_range(void *arg)&lt;br /&gt;
{&lt;br /&gt;
    struct part *p = arg;&lt;br /&gt;
    p-&amp;gt;sum = 0;&lt;br /&gt;
    for (long i = p-&amp;gt;from; i &amp;lt; p-&amp;gt;to; i++) {&lt;br /&gt;
        p-&amp;gt;sum += i;&lt;br /&gt;
    }&lt;br /&gt;
    return nullptr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pthread_t threads[N_THREADS];&lt;br /&gt;
    struct part parts[N_THREADS];&lt;br /&gt;
&lt;br /&gt;
    for (int i = 0; i &amp;lt; N_THREADS; i++) {&lt;br /&gt;
        parts[i].from = (long) N / N_THREADS * i;&lt;br /&gt;
        parts[i].to = (long) N / N_THREADS * (i + 1);&lt;br /&gt;
        int err = pthread_create(&amp;amp;threads[i], nullptr, add_range, &amp;amp;parts[i]);&lt;br /&gt;
        if (err != 0) {&lt;br /&gt;
            print_error(&amp;quot;pthread_create&amp;quot;, err);&lt;br /&gt;
            return EXIT_FAILURE;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    long sum = 0;&lt;br /&gt;
    for (int i = 0; i &amp;lt; N_THREADS; i++) {&lt;br /&gt;
        pthread_join(threads[i], nullptr);&lt;br /&gt;
        sum += parts[i].sum;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;sum of 0..%d: %ld\n&amp;quot;, N - 1, sum);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
sum of 0..99999999: 4999999950000000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each thread writes only to its own &amp;lt;code&amp;gt;struct part&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; reads it only after [https://man7.org/linux/man-pages/man3/pthread_join.3.html &amp;lt;code&amp;gt;pthread_join(3)&amp;lt;/code&amp;gt;], when the thread has ended. The threads never touch the same data at the same time, so nothing more is needed in this case.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; Returning from &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;, or calling [https://man7.org/linux/man-pages/man3/exit.3.html &amp;lt;code&amp;gt;exit(3)&amp;lt;/code&amp;gt;] from any thread, ends the &amp;#039;&amp;#039;&amp;#039;whole process&amp;#039;&amp;#039;&amp;#039;, with all its threads (it uses the &amp;lt;code&amp;gt;exit_group&amp;lt;/code&amp;gt; system call). To end only the calling thread, return from its function, or call [https://man7.org/linux/man-pages/man3/pthread_exit.3.html &amp;lt;code&amp;gt;pthread_exit(3)&amp;lt;/code&amp;gt;]. This is why &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; must join the threads before it returns.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Under the hood: clone(2) ===&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man3/pthread_create.3.html &amp;lt;code&amp;gt;pthread_create(3)&amp;lt;/code&amp;gt;] creates the new task with the [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;] system call, the same one [https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] uses. The difference is in the flags, which say what the new task shares with the one that created it:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Flag !! The new task shares&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CLONE_VM&amp;lt;/code&amp;gt; || The memory: the code, the global variables, the heap, and the stacks of the other threads&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CLONE_FILES&amp;lt;/code&amp;gt; || The file descriptor table: a file opened by one thread can be used by all of them&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CLONE_SIGHAND&amp;lt;/code&amp;gt; || The signal handlers&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;CLONE_THREAD&amp;lt;/code&amp;gt; || The process: the new task gets a new TID, but the same PID, and is not a child to be waited for with [https://man7.org/linux/man-pages/man2/waitpid.2.html &amp;lt;code&amp;gt;waitpid(2)&amp;lt;/code&amp;gt;]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[https://man7.org/linux/man-pages/man2/fork.2.html &amp;lt;code&amp;gt;fork(2)&amp;lt;/code&amp;gt;] calls [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;] with none of these, so the child gets a copy of everything instead. Since the memory is shared, the new thread cannot use the stack of the thread which created it. Before calling [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;], glibc allocates a new stack (8 MiB by default) with [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;], and gives its address to [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;].&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_5_-_threads.png|center|thumb|500px|One process with three threads. They share all the memory of the process, but each one has its own registers, and its own stack, which glibc maps with [https://man7.org/linux/man-pages/man2/mmap.2.html &amp;lt;code&amp;gt;mmap(2)&amp;lt;/code&amp;gt;] before calling [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;].]]&lt;br /&gt;
&lt;br /&gt;
== Races ==&lt;br /&gt;
&lt;br /&gt;
Since threads share their memory, two threads can change the same variable at the same time. The following program starts two threads, and each of them increments a global counter one million times:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define N 1000000&lt;br /&gt;
&lt;br /&gt;
static long counter = 0;&lt;br /&gt;
&lt;br /&gt;
static void *increment(void *arg)&lt;br /&gt;
{&lt;br /&gt;
    (void) arg;&lt;br /&gt;
    for (int i = 0; i &amp;lt; N; i++) {&lt;br /&gt;
        counter++;&lt;br /&gt;
    }&lt;br /&gt;
    return nullptr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pthread_t a, b;&lt;br /&gt;
    pthread_create(&amp;amp;a, nullptr, increment, nullptr);&lt;br /&gt;
    pthread_create(&amp;amp;b, nullptr, increment, nullptr);&lt;br /&gt;
    pthread_join(a, nullptr);&lt;br /&gt;
    pthread_join(b, nullptr);&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;counter = %ld (expected %d)\n&amp;quot;, counter, 2 * N);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
counter = 1018661 (expected 2000000)&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
counter = 1008721 (expected 2000000)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result is wrong, and different every time. &amp;lt;code&amp;gt;counter++&amp;lt;/code&amp;gt; looks like a single operation, but the processor does it in three steps: &amp;#039;&amp;#039;&amp;#039;load&amp;#039;&amp;#039;&amp;#039; the value from memory into a register, &amp;#039;&amp;#039;&amp;#039;add&amp;#039;&amp;#039;&amp;#039; 1 to the register, and &amp;#039;&amp;#039;&amp;#039;store&amp;#039;&amp;#039;&amp;#039; the register back to memory. When two threads do these steps at the same time, they can both load the same value, and one of the increments is lost.&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_5_-_lost_update.png|center|thumb|600px|&amp;lt;code&amp;gt;counter++&amp;lt;/code&amp;gt; is three steps. If two threads interleave them, both load 5 and both store 6: one of the two increments is lost.]]&lt;br /&gt;
&lt;br /&gt;
This is a &amp;#039;&amp;#039;&amp;#039;race&amp;#039;&amp;#039;&amp;#039;: the result depends on how the steps of the threads happen to interleave. A part of the program which uses shared data, and must not be run by two threads at the same time, is called a &amp;#039;&amp;#039;&amp;#039;critical section&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Mutexes ==&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;&amp;#039;mutex&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;mutual exclusion&amp;#039;&amp;#039;) is a lock which only one thread can hold at a time. A thread &amp;#039;&amp;#039;&amp;#039;locks&amp;#039;&amp;#039;&amp;#039; the mutex before the critical section, and &amp;#039;&amp;#039;&amp;#039;unlocks&amp;#039;&amp;#039;&amp;#039; it after. If another thread already holds it, [https://man7.org/linux/man-pages/man3/pthread_mutex_lock.3.html &amp;lt;code&amp;gt;pthread_mutex_lock(3)&amp;lt;/code&amp;gt;] waits until it is unlocked.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *attr);&lt;br /&gt;
int pthread_mutex_lock(pthread_mutex_t *m);&lt;br /&gt;
int pthread_mutex_unlock(pthread_mutex_t *m);&lt;br /&gt;
int pthread_mutex_destroy(pthread_mutex_t *m);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A mutex must be set up with [https://man7.org/linux/man-pages/man3/pthread_mutex_init.3.html &amp;lt;code&amp;gt;pthread_mutex_init(3)&amp;lt;/code&amp;gt;] before it is used, usually before the threads which use it are created. &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; can choose other kinds of mutexes, or be &amp;lt;code&amp;gt;nullptr&amp;lt;/code&amp;gt; for a normal one, which starts unlocked. When the mutex is no longer needed, [https://man7.org/linux/man-pages/man3/pthread_mutex_destroy.3.html &amp;lt;code&amp;gt;pthread_mutex_destroy(3)&amp;lt;/code&amp;gt;] frees anything the mutex may hold.&lt;br /&gt;
&lt;br /&gt;
To fix the counter, we add a mutex, and lock it around the increment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define N 1000000&lt;br /&gt;
&lt;br /&gt;
static long counter = 0;&lt;br /&gt;
static pthread_mutex_t lock;&lt;br /&gt;
&lt;br /&gt;
static void *increment(void *arg)&lt;br /&gt;
{&lt;br /&gt;
    (void) arg;&lt;br /&gt;
    for (int i = 0; i &amp;lt; N; i++) {&lt;br /&gt;
        pthread_mutex_lock(&amp;amp;lock);&lt;br /&gt;
        counter++;&lt;br /&gt;
        pthread_mutex_unlock(&amp;amp;lock);&lt;br /&gt;
    }&lt;br /&gt;
    return nullptr;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
    pthread_mutex_init(&amp;amp;lock, nullptr);&lt;br /&gt;
&lt;br /&gt;
    pthread_t a, b;&lt;br /&gt;
    pthread_create(&amp;amp;a, nullptr, increment, nullptr);&lt;br /&gt;
    pthread_create(&amp;amp;b, nullptr, increment, nullptr);&lt;br /&gt;
    pthread_join(a, nullptr);&lt;br /&gt;
    pthread_join(b, nullptr);&lt;br /&gt;
&lt;br /&gt;
    pthread_mutex_destroy(&amp;amp;lock);&lt;br /&gt;
&lt;br /&gt;
    char msg[256];&lt;br /&gt;
    snprintf(msg, sizeof msg, &amp;quot;counter = %ld (expected %d)\n&amp;quot;, counter, 2 * N);&lt;br /&gt;
    write(STDOUT_FILENO, msg, strlen(msg));&lt;br /&gt;
    return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
counter = 2000000 (expected 2000000)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The result is now correct, but locking has a cost: on our machine, the program became as much as 100 times slower. Every increment now also locks and unlocks the mutex, and while one thread holds it, the other has to wait. Critical sections should be kept as short as possible.&lt;br /&gt;
&lt;br /&gt;
=== Under the hood: futex(2) ===&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;code&amp;gt;pthread_mutex_t&amp;lt;/code&amp;gt; is, at its core, just an integer in the memory of the process. glibc uses three values:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Unlocked&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Locked, and no other thread is waiting&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Locked, and other threads may be waiting&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The processor has &amp;#039;&amp;#039;&amp;#039;atomic&amp;#039;&amp;#039;&amp;#039; instructions, which read and change a value in memory in a single step, so that no other thread can come in between. C provides them in &amp;lt;code&amp;gt;&amp;amp;lt;stdatomic.h&amp;amp;gt;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;stdatomic.h&amp;gt;. A is an atomic type, such as _Atomic uint32_t, and C the plain one, uint32_t.&lt;br /&gt;
// If *obj == *expected, sets *obj to desired and returns true.&lt;br /&gt;
// Otherwise, copies *obj into *expected and returns false.&lt;br /&gt;
bool atomic_compare_exchange_strong(volatile A *obj, C *expected, C desired);&lt;br /&gt;
// Sets *obj to desired, and returns its old value.&lt;br /&gt;
C atomic_exchange(volatile A *obj, C desired);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Locking a free mutex is a single atomic instruction, which changes it from 0 to 1. This is the &amp;#039;&amp;#039;&amp;#039;fast path&amp;#039;&amp;#039;&amp;#039;, and it needs &amp;#039;&amp;#039;&amp;#039;no system call&amp;#039;&amp;#039;&amp;#039;. The kernel is only needed when a thread has to wait, since only the kernel can put a thread to sleep and wake it up. For this, Linux provides the [https://man7.org/linux/man-pages/man2/futex.2.html &amp;lt;code&amp;gt;futex(2)&amp;lt;/code&amp;gt;] (&amp;#039;&amp;#039;fast user-space mutex&amp;#039;&amp;#039;) system call:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// In: &amp;lt;linux/futex.h&amp;gt; and &amp;lt;sys/syscall.h&amp;gt;. glibc has no wrapper for it.&lt;br /&gt;
long syscall(SYS_futex, uint32_t *addr, int op, uint32_t val, ...);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! &amp;lt;code&amp;gt;op&amp;lt;/code&amp;gt; !! What the kernel does&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FUTEX_WAIT&amp;lt;/code&amp;gt; || If &amp;lt;code&amp;gt;*addr&amp;lt;/code&amp;gt; is still equal to &amp;lt;code&amp;gt;val&amp;lt;/code&amp;gt;, puts the thread to sleep, until another thread wakes it. Otherwise, returns right away&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FUTEX_WAKE&amp;lt;/code&amp;gt; || Wakes up to &amp;lt;code&amp;gt;val&amp;lt;/code&amp;gt; threads sleeping on &amp;lt;code&amp;gt;addr&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For the threads of a single process, the &amp;lt;code&amp;gt;FUTEX_WAIT_PRIVATE&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FUTEX_WAKE_PRIVATE&amp;lt;/code&amp;gt; versions are a little faster. The kernel does not know anything about mutexes: it only keeps a list of sleeping threads for each address. Using the three values above:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;lock&amp;#039;&amp;#039;&amp;#039;: try to change the mutex from 0 to 1. If that worked, we hold it. Otherwise, set it to 2, to say that someone is waiting, and call &amp;lt;code&amp;gt;FUTEX_WAIT&amp;lt;/code&amp;gt; with 2. When woken, try again.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;unlock&amp;#039;&amp;#039;&amp;#039;: set the mutex to 0. If it was 2, someone may be waiting, so call &amp;lt;code&amp;gt;FUTEX_WAKE&amp;lt;/code&amp;gt; to wake one of them.&lt;br /&gt;
&lt;br /&gt;
The check done by &amp;lt;code&amp;gt;FUTEX_WAIT&amp;lt;/code&amp;gt; is what makes this work: if the mutex was unlocked between our failed attempt and the system call, the value is no longer 2, and the thread does not go to sleep waiting for a wake-up which already happened.&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_5_-_futex.png|center|thumb|750px|Thread A locks a free mutex in user space, with no system call. Thread B finds it taken, marks it with 2 and sleeps in the kernel. When A unlocks it, it sees the 2, and asks the kernel to wake B, which then takes the mutex.]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;regula&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; [https://man7.org/linux/man-pages/man3/pthread_join.3.html &amp;lt;code&amp;gt;pthread_join(3)&amp;lt;/code&amp;gt;] also uses [https://man7.org/linux/man-pages/man2/futex.2.html &amp;lt;code&amp;gt;futex(2)&amp;lt;/code&amp;gt;]. When [https://man7.org/linux/man-pages/man3/pthread_create.3.html &amp;lt;code&amp;gt;pthread_create(3)&amp;lt;/code&amp;gt;] calls [https://man7.org/linux/man-pages/man2/clone.2.html &amp;lt;code&amp;gt;clone(2)&amp;lt;/code&amp;gt;], it asks the kernel (with &amp;lt;code&amp;gt;CLONE_CHILD_CLEARTID&amp;lt;/code&amp;gt;) to set a variable holding the TID of the thread to 0 when the thread ends, and to do a &amp;lt;code&amp;gt;FUTEX_WAKE&amp;lt;/code&amp;gt; on it. [https://man7.org/linux/man-pages/man3/pthread_join.3.html &amp;lt;code&amp;gt;pthread_join(3)&amp;lt;/code&amp;gt;] waits on that variable with &amp;lt;code&amp;gt;FUTEX_WAIT&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deadlock ===&lt;br /&gt;
&lt;br /&gt;
A program may need to hold several mutexes at once. If thread A locks mutex 5 and then waits for mutex 7, while thread B has locked mutex 7 and waits for mutex 5, neither of them can ever continue. This is a &amp;#039;&amp;#039;&amp;#039;deadlock&amp;#039;&amp;#039;&amp;#039;. The program does not crash, it just stops, using no CPU time at all.&lt;br /&gt;
&lt;br /&gt;
[[File:OS_Lab_5_-_deadlock.png|center|thumb|550px|Thread A holds one lock and waits for the other, while thread B holds the other and waits for the first. Each waits for the other, forever.]]&lt;br /&gt;
&lt;br /&gt;
The simplest way to avoid it is to always take the mutexes &amp;#039;&amp;#039;&amp;#039;in the same order&amp;#039;&amp;#039;&amp;#039;, for example in increasing order of their number. Then no thread can hold a mutex while waiting for one that comes before it, so the threads can never wait for each other in a circle.&lt;br /&gt;
&lt;br /&gt;
== Exercises ==&lt;br /&gt;
&lt;br /&gt;
Write the program in a new copy of the project template. As a reminder from [[OS_Lab_1_-_Linux_setup._Bash_primer._C_primer#getting-the-template|the first lab]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
mkdir -p ~/oslab &amp;amp;&amp;amp; cd ~/oslab&lt;br /&gt;
curl -OL https://wiki.dcae.pub.ro/images/9/93/Os_lab_skeleton.zip&lt;br /&gt;
unzip -d lab05 Os_lab_skeleton.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this lab:&lt;br /&gt;
&lt;br /&gt;
* As in the last labs, do not use the standard I/O library for output: build the text with [https://man7.org/linux/man-pages/man3/snprintf.3.html &amp;lt;code&amp;gt;snprintf(3)&amp;lt;/code&amp;gt;] and [https://man7.org/linux/man-pages/man2/write.2.html &amp;lt;code&amp;gt;write(2)&amp;lt;/code&amp;gt;] it.&lt;br /&gt;
* Check the value returned by [https://man7.org/linux/man-pages/man3/pthread_create.3.html &amp;lt;code&amp;gt;pthread_create(3)&amp;lt;/code&amp;gt;], and remember that it is an error number, not -1.&lt;br /&gt;
* Use &amp;lt;code&amp;gt;time ./build/debug/app&amp;lt;/code&amp;gt; to measure how long the program runs.&lt;br /&gt;
&lt;br /&gt;
=== Bank transfers ===&lt;br /&gt;
&lt;br /&gt;
A bank has 100 accounts, each starting with 1000. 8 threads, the &amp;#039;&amp;#039;&amp;#039;tellers&amp;#039;&amp;#039;&amp;#039;, each make 1,000,000 random transfers between the accounts. Money only moves from one account to another, so at the end, the total must still be 100,000.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define N_ACCOUNTS 100&lt;br /&gt;
#define N_THREADS 8&lt;br /&gt;
#define N_TRANSFERS 1000000&lt;br /&gt;
#define INITIAL_BALANCE 1000&lt;br /&gt;
&lt;br /&gt;
struct account {&lt;br /&gt;
    pthread_mutex_t lock; // Not used until step 3&lt;br /&gt;
    int balance;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
static struct account accounts[N_ACCOUNTS];&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each transfer picks two &amp;#039;&amp;#039;&amp;#039;different&amp;#039;&amp;#039;&amp;#039; accounts, &amp;lt;code&amp;gt;from&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;to&amp;lt;/code&amp;gt;, and an amount between 1 and 100. If &amp;lt;code&amp;gt;from&amp;lt;/code&amp;gt; has enough money, the amount moves from &amp;lt;code&amp;gt;from&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;to&amp;lt;/code&amp;gt;. Otherwise, nothing happens. Use [https://man7.org/linux/man-pages/man3/rand_r.3.html &amp;lt;code&amp;gt;rand_r(3)&amp;lt;/code&amp;gt;] for the random numbers, with a different seed for each thread: [https://man7.org/linux/man-pages/man3/rand.3.html &amp;lt;code&amp;gt;rand(3)&amp;lt;/code&amp;gt;] keeps its state in a global variable, shared by all the threads.&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;No locks.&amp;#039;&amp;#039;&amp;#039; Write the tellers, and print the total after joining them. Run the program a few times: what happens to the total?&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;One mutex for the whole bank.&amp;#039;&amp;#039;&amp;#039; Lock a single global mutex around each transfer. The total is now correct. How long does the program take? Why is the &amp;lt;code&amp;gt;sys&amp;lt;/code&amp;gt; time so large?&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;One mutex per account.&amp;#039;&amp;#039;&amp;#039; Instead of the global mutex, lock &amp;lt;code&amp;gt;accounts[from].lock&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;accounts[to].lock&amp;lt;/code&amp;gt;, do the transfer, and unlock them. Run the program: it will probably never end (Ctrl+C stops it). Explain why, in a comment.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Lock ordering.&amp;#039;&amp;#039;&amp;#039; Fix step 3 by always locking the account with the lower number first. The total must be correct, and the program should now be faster than in step 2. Why?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
total: 766978 (expected 100000)&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
total: 751625 (expected 100000)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ time ./build/debug/app&lt;br /&gt;
total: 100000 (expected 100000)&lt;br /&gt;
&lt;br /&gt;
real	0m0.304s&lt;br /&gt;
user	0m0.427s&lt;br /&gt;
sys	0m0.081s&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first output is from step 1, and the second from step 4. The times depend on the machine, but step 4 should be clearly faster than step 2.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Bonus:&amp;#039;&amp;#039;&amp;#039; Write your own mutex, on top of [https://man7.org/linux/man-pages/man2/futex.2.html &amp;lt;code&amp;gt;futex(2)&amp;lt;/code&amp;gt;], as described in [[#Under the hood: futex(2)|the futex section]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef _Atomic uint32_t mutex_t; // 0, 1 or 2, as above&lt;br /&gt;
&lt;br /&gt;
void mutex_lock(mutex_t *m);&lt;br /&gt;
void mutex_unlock(mutex_t *m);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use it instead of &amp;lt;code&amp;gt;pthread_mutex_t&amp;lt;/code&amp;gt; in step 4. The total must still be correct, and the time should be close to that of step 4.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Extra:&amp;#039;&amp;#039;&amp;#039; Add an &amp;#039;&amp;#039;&amp;#039;auditor&amp;#039;&amp;#039;&amp;#039; thread, which checks the bank while the tellers work. Every 50 ms ([https://man7.org/linux/man-pages/man2/nanosleep.2.html &amp;lt;code&amp;gt;nanosleep(2)&amp;lt;/code&amp;gt;]), it locks all the accounts, adds up their balances, unlocks them, and prints the total, which must always be 100,000. When the tellers are done, &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; tells the auditor to stop through a global &amp;lt;code&amp;gt;atomic_bool&amp;lt;/code&amp;gt; (from &amp;lt;code&amp;gt;&amp;amp;lt;stdatomic.h&amp;amp;gt;&amp;lt;/code&amp;gt;): a variable which one thread writes while another reads it must be atomic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./build/debug/app&lt;br /&gt;
audit: 100000&lt;br /&gt;
audit: 100000&lt;br /&gt;
audit: 100000&lt;br /&gt;
audit: 100000&lt;br /&gt;
audit: 100000&lt;br /&gt;
audit: 100000&lt;br /&gt;
total: 100000 (expected 100000)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In which order must the auditor lock the accounts?&lt;br /&gt;
* Why must it hold all the locks at once? Try locking, reading and unlocking one account at a time instead: what totals do you get, and why?&lt;/div&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
	<entry>
		<id>http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8436&amp;oldid=8434</id>
		<title>Operating Systems</title>
		<link rel="alternate" type="text/html" href="http://wiki.dcae.pub.ro/index.php?title=Operating_Systems&amp;diff=8436&amp;oldid=8434"/>
		<updated>2026-11-06T14:04:44Z</updated>

		<summary type="html">&lt;p&gt;&lt;span dir=&quot;auto&quot;&gt;&lt;span class=&quot;autocomment&quot;&gt;Lab Sessions&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left diff-editfont-monospace&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ro&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Versiunea anterioară&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Versiunea de la data 6 noiembrie 2026 14:04&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l4&quot; &gt;Linia 4:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Linia 4:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Lab 2 - [[OS Lab 2 - System calls. The initial stack]]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Lab 2 - [[OS Lab 2 - System calls. The initial stack]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Lab 3 - [[OS Lab 3 - Files. Memory mapping]]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Lab 3 - [[OS Lab 3 - Files. Memory mapping]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt;−&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Lab &lt;del class=&quot;diffchange diffchange-inline&quot;&gt;3 &lt;/del&gt;- [[OS Lab 4 - Processes. Signals]]&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Lab &lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;4 &lt;/ins&gt;- [[OS Lab 4 - Processes. Signals&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;]]&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt; &lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt;+&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins class=&quot;diffchange diffchange-inline&quot;&gt;* Lab 5 - [[OS Lab 5 - Threads. Mutexes&lt;/ins&gt;]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Old Lab Sessions (2025) ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&#039;diff-marker&#039;&gt; &lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Old Lab Sessions (2025) ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Vserbu</name></author>
	</entry>
</feed>