Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
2266 lines (2115 loc) · 179 KB
/
Copy pathindex.html
File metadata and controls
2266 lines (2115 loc) · 179 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta name="google-site-verification" content="I-v-239LCd_QsToC1oPc1-MSE3Ay52i3LIlDESz8ayQ" />
<title>Codexion Learning Guide — POSIX Threads, Mutexes & Concurrency</title>
<style>
:root {
--bg-primary: #0d1117;
--bg-secondary: #161b22;
--bg-tertiary: #21262d;
--border: #30363d;
--text-primary: #c9d1d9;
--text-secondary: #8b949e;
--accent: #58a6ff;
--accent-hover: #79c0ff;
--success: #3fb950;
--warning: #d29922;
--danger: #f85149;
--purple: #a371f7;
--code-bg: #0d1117;
--code-border: #30363d;
--sidebar-width: 290px;
}
* { margin:0; padding:0; box-sizing:border-box; }
html { scroll-behavior:smooth; scroll-padding-top:20px; }
body {
font-family:'Segoe UI',system-ui,-apple-system,sans-serif;
background:var(--bg-primary);
color:var(--text-primary);
line-height:1.7;
overflow-x:hidden;
}
::-webkit-scrollbar { width:10px; }
::-webkit-scrollbar-track { background:var(--bg-primary); }
::-webkit-scrollbar-thumb { background:var(--border); border-radius:5px; }
::-webkit-scrollbar-thumb:hover { background:var(--text-secondary); }
.container { display:flex; min-height:100vh; }
.sidebar {
width:var(--sidebar-width);
background:var(--bg-secondary);
border-right:1px solid var(--border);
position:fixed;
height:100vh;
overflow-y:auto;
overflow-x:hidden;
padding:24px 0;
z-index:100;
transition:width 0.25s ease, transform 0.25s ease;
white-space:nowrap;
}
.sidebar-header { padding:0 24px 20px; border-bottom:1px solid var(--border); margin-bottom:16px; }
.sidebar-header h1 { font-size:1.3rem; color:var(--accent); margin-bottom:4px; }
.sidebar-header p { font-size:0.8rem; color:var(--text-secondary); }
.nav-section { margin-bottom:8px; }
.nav-section-title {
padding:8px 24px; font-size:0.75rem; text-transform:uppercase;
letter-spacing:0.08em; color:var(--text-secondary); font-weight:600;
}
.nav-link {
display:block; padding:8px 24px 8px 32px; color:var(--text-primary);
text-decoration:none; font-size:0.88rem;
border-left:3px solid transparent; transition:all 0.2s;
}
.nav-link:hover { background:var(--bg-tertiary); color:var(--accent-hover); border-left-color:var(--accent); }
.nav-link.active { background:var(--bg-tertiary); color:var(--accent); border-left-color:var(--accent); font-weight:500; }
.main { margin-left:var(--sidebar-width); flex:1; max-width:920px; padding:48px 48px 80px; transition:margin-left 0.25s ease; }
/* Collapsed (desktop) state */
.sidebar.collapsed { width:0; padding-left:0; padding-right:0; border-right:none; }
.sidebar.collapsed .sidebar-header,
.sidebar.collapsed .nav-section { display:none; }
body.sidebar-collapsed .main { margin-left:auto; margin-right:auto; }
/* Floating toggle button, always on top, works for both desktop collapse and mobile open/close */
.sidebar-toggle-btn {
position:fixed; top:20px; left:20px; z-index:200;
background:var(--bg-tertiary); border:1px solid var(--border); color:var(--text-primary);
width:38px; height:38px; border-radius:8px; cursor:pointer;
display:flex; align-items:center; justify-content:center; font-size:1.1rem;
transition:all 0.2s, left 0.25s ease;
}
.sidebar-toggle-btn:hover { background:var(--accent); color:var(--bg-primary); border-color:var(--accent); }
.sidebar-toggle-btn.shifted { left:calc(var(--sidebar-width) + 12px); }
.lang-toggle-btn {
position:fixed; top:20px; right:20px; z-index:200;
background:var(--bg-tertiary); border:1px solid var(--border); color:var(--text-primary);
min-width:44px; height:38px; padding:0 12px; border-radius:8px; cursor:pointer;
display:flex; align-items:center; justify-content:center; font-size:0.85rem; font-weight:700;
letter-spacing:0.02em; transition:all 0.2s;
}
.lang-toggle-btn:hover { background:var(--accent); color:var(--bg-primary); border-color:var(--accent); }
h1 { font-size:2.5rem; margin-bottom:16px; color:var(--text-primary); font-weight:700; letter-spacing:-0.02em; }
h2 { font-size:1.7rem; margin-top:48px; margin-bottom:20px; color:var(--accent); font-weight:600; padding-bottom:12px; border-bottom:1px solid var(--border); }
h3 { font-size:1.25rem; margin-top:32px; margin-bottom:12px; color:var(--text-primary); font-weight:600; }
h4 { font-size:1.05rem; margin-top:24px; margin-bottom:10px; color:var(--warning); font-weight:600; }
p { margin-bottom:16px; color:var(--text-primary); }
a { color:var(--accent); text-decoration:none; }
a:hover { text-decoration:underline; }
pre {
background:var(--code-bg); border:1px solid var(--code-border);
border-radius:10px; padding:20px; overflow-x:auto; margin:20px 0; position:relative;
}
pre::before {
content:attr(data-lang); position:absolute; top:8px; right:12px;
font-size:0.7rem; color:var(--text-secondary); text-transform:uppercase; letter-spacing:0.05em;
}
code { font-family:'JetBrains Mono','Fira Code','Consolas',monospace; font-size:0.85rem; line-height:1.6; }
pre code { color:#e6edf3; }
p code, li code, td code { background:var(--bg-tertiary); padding:2px 8px; border-radius:4px; font-size:0.85em; color:var(--accent-hover); }
.keyword { color:#ff7b72; }
.string { color:#a5d6ff; }
.comment { color:#8b949e; font-style:italic; }
.function { color:#d2a8ff; }
.number { color:#79c0ff; }
.type { color:#ffa657; }
.macro { color:#ff7b72; }
.info-box {
border-radius:10px; padding:20px 24px; margin:24px 0; border-left:4px solid;
}
.info-box.tip { background:rgba(47,129,247,0.1); border-color:var(--accent); }
.info-box.warning { background:rgba(210,153,34,0.1); border-color:var(--warning); }
.info-box.danger { background:rgba(248,81,73,0.1); border-color:var(--danger); }
.info-box.success { background:rgba(63,185,80,0.1); border-color:var(--success); }
.info-box.purple { background:rgba(163,113,247,0.1); border-color:var(--purple); }
.info-box-title { font-weight:700; margin-bottom:8px; display:flex; align-items:center; gap:8px; }
.info-box.tip .info-box-title { color:var(--accent); }
.info-box.warning .info-box-title { color:var(--warning); }
.info-box.danger .info-box-title { color:var(--danger); }
.info-box.success .info-box-title { color:var(--success); }
.info-box.purple .info-box-title { color:var(--purple); }
ul, ol { margin:16px 0; padding-left:28px; }
li { margin-bottom:8px; color:var(--text-primary); }
table { width:100%; border-collapse:collapse; margin:20px 0; border-radius:10px; overflow:hidden; border:1px solid var(--border); }
th { background:var(--bg-tertiary); padding:12px 16px; text-align:left; font-weight:600; color:var(--accent); border-bottom:1px solid var(--border); }
td { padding:12px 16px; border-bottom:1px solid var(--border); color:var(--text-primary); }
tr:hover td { background:var(--bg-tertiary); }
.hero { text-align:center; padding:40px 0 32px; border-bottom:1px solid var(--border); margin-bottom:40px; }
.hero h1 { font-size:3rem; background:linear-gradient(135deg,var(--accent),var(--purple)); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.hero p { font-size:1.2rem; color:var(--text-secondary); max-width:650px; margin:0 auto; }
.badge { display:inline-block; padding:4px 12px; border-radius:20px; font-size:0.75rem; font-weight:600; margin:4px; }
.badge.blue { background:rgba(88,166,255,0.15); color:var(--accent); }
.badge.green { background:rgba(63,185,80,0.15); color:var(--success); }
.badge.orange { background:rgba(210,153,34,0.15); color:var(--warning); }
.badge.purple { background:rgba(163,113,247,0.15); color:var(--purple); }
.chapter-number { display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px; background:var(--accent); color:var(--bg-primary); border-radius:50%; font-weight:700; font-size:1rem; margin-right:12px; }
kbd { background:var(--bg-tertiary); border:1px solid var(--border); border-radius:6px; padding:2px 8px; font-family:monospace; font-size:0.85em; box-shadow:0 2px 0 var(--border); }
hr { border:none; height:1px; background:var(--border); margin:48px 0; }
.img-caption { text-align:center; color:var(--text-secondary); font-size:0.85rem; margin-top:8px; margin-bottom:20px; }
.concept-img { max-width:100%; border-radius:10px; border:1px solid var(--border); display:block; margin:20px auto; }
section { scroll-margin-top:20px; }
/* Simulator styles */
.simulator-container { background:var(--bg-secondary); border:1px solid var(--border); border-radius:12px; padding:24px; margin:24px 0; }
.simulator-title { font-size:1.1rem; color:var(--accent); font-weight:600; margin-bottom:16px; }
.sim-canvas { width:100%; height:320px; background:var(--bg-primary); border-radius:8px; border:1px solid var(--border); position:relative; overflow:hidden; }
.sim-controls { display:flex; gap:12px; margin-top:16px; flex-wrap:wrap; }
.sim-btn { background:var(--bg-tertiary); color:var(--text-primary); border:1px solid var(--border); padding:8px 16px; border-radius:6px; cursor:pointer; font-family:inherit; font-size:0.9rem; transition:all 0.2s; }
.sim-btn:hover { background:var(--accent); color:var(--bg-primary); border-color:var(--accent); }
.sim-btn.active { background:var(--success); color:var(--bg-primary); border-color:var(--success); }
.sim-stats { display:flex; gap:24px; margin-top:12px; font-size:0.85rem; color:var(--text-secondary); flex-wrap:wrap; }
.sim-stat span { color:var(--text-primary); font-weight:600; }
.sim-controls label { display:flex; align-items:center; gap:8px; }
.sim-controls input[type="range"] { accent-color:var(--accent); }
.sim-val { color:var(--accent-hover); font-weight:700; min-width:2.5em; display:inline-block; text-align:right; }
@media (max-width:1024px) {
.sidebar { transform:translateX(-100%); transition:transform 0.3s; }
.sidebar.open { transform:translateX(0); }
.main { margin-left:0; padding:24px; }
.sidebar-toggle-btn.shifted { left:20px; }
}
</style>
</head>
<body>
<button aria-label="Replier/déplier le menu" class="sidebar-toggle-btn shifted" id="sidebarToggleBtn" onclick="toggleSidebar()" title="Replier/déplier le menu">☰</button>
<button aria-label="Switch language / Changer de langue" class="lang-toggle-btn" id="langToggleBtn" onclick="toggleLang()" title="Switch language / Changer de langue">FR</button>
<div class="container">
<nav class="sidebar">
<div class="sidebar-header">
<h1 data-i18n="sh_title">Codexion Guide</h1>
<p data-i18n="sh_sub">POSIX Threads & Concurrency</p>
</div>
<div class="nav-section">
<div class="nav-section-title" data-i18n="nav_start">Getting Started</div>
<a class="nav-link" data-i18n="nav_intro" href="#intro">Introduction</a>
<a class="nav-link" data-i18n="nav_setup" href="#setup">Setup & Compilation</a>
</div>
<div class="nav-section">
<div class="nav-section-title" data-i18n="nav_core">Core Concepts</div>
<a class="nav-link" data-i18n="nav_ch1" href="#ch1">1. POSIX Threads</a>
<a class="nav-link" data-i18n="nav_ch2" href="#ch2">2. Mutexes</a>
<a class="nav-link" data-i18n="nav_ch3" href="#ch3">3. Condition Variables</a>
<a class="nav-link" data-i18n="nav_ch4" href="#ch4">4. Dining Philosophers</a>
<a class="nav-link" data-i18n="nav_ch5" href="#ch5">5. Time in C</a>
<a class="nav-link" data-i18n="nav_ch6" href="#ch6">6. Scheduling: FIFO vs EDF</a>
<a class="nav-link" data-i18n="nav_ch7" href="#ch7">7. Priority Queue / Heap</a>
<a class="nav-link" data-i18n="nav_ch8" href="#ch8">8. Monitor Pattern</a>
</div>
<div class="nav-section">
<div class="nav-section-title" data-i18n="nav_project">Codexion Project</div>
<a class="nav-link" data-i18n="nav_ch9" href="#ch9">9. Architecture</a>
<a class="nav-link" data-i18n="nav_ch10" href="#ch10">10. Data Structures</a>
<a class="nav-link" data-i18n="nav_ch11" href="#ch11">11. Full Implementation</a>
<a class="nav-link" data-i18n="nav_ch12" href="#ch12">12. Interactive Simulator</a>
</div>
<div class="nav-section">
<div class="nav-section-title" data-i18n="nav_ref">Reference</div>
<a class="nav-link" data-i18n="nav_cheat" href="#cheatsheet">Quick API Reference</a>
<a class="nav-link" data-i18n="nav_res" href="#resources">Resources</a>
</div>
</nav>
<main class="main">
<div class="hero">
<h1 data-i18n="hero_title">Codexion Learning Guide</h1>
<p data-i18n="hero_sub">Master POSIX threads, mutexes, condition variables, and real-time scheduling by building a concurrent resource simulator</p>
<div style="margin-top:20px;">
<span class="badge blue">C89 / C99</span>
<span class="badge green">POSIX Threads</span>
<span class="badge orange">Concurrency</span>
<span class="badge purple">Real-Time Scheduling</span>
</div>
</div>
<section id="intro">
<h2 data-i18n="intro_h2">Introduction</h2>
<p data-i18n="intro_p1"><strong>Codexion</strong> is a concurrency challenge that models a real-world resource contention problem. Multiple coders (threads) sit around a shared Quantum Compiler and compete for USB dongles (mutex-protected resources) to compile their code. The simulation must prevent burnout (deadline misses) while fairly arbitrating access to limited resources.</p>
<div class="info-box purple">
<div class="info-box-title" data-i18n="intro_box1_title">If you've never touched threads before, start here</div>
<p data-i18n="intro_box1_p">Every program you've written so far probably ran <strong>one instruction at a time, in order</strong> — like a single cook working alone in a kitchen. Concurrency means putting several cooks in that same kitchen at the same time, sharing the same knives, the same stove, the same counter space. Nothing about the recipes changes, but suddenly you have to answer new questions: what happens if two cooks reach for the same knife at once? What if one cook is waiting for the stove to free up — should they stand there staring at it, or go chop vegetables and check back later? Codexion is a deliberately small, concrete kitchen (8 coders, N dongles) built so you can reason about these questions without getting lost in a huge codebase. Every concept in this guide (threads, mutexes, condition variables, scheduling) is really just a formal answer to "how do multiple cooks share one kitchen safely and efficiently?"</p>
</div>
<p data-i18n="intro_p2">Don't worry if terms like "thread", "mutex", or "race condition" mean nothing to you yet — Chapters 1 to 8 build them up one at a time, starting from first principles, before Chapters 9 to 12 assemble them into the full Codexion project. Read them in order the first time through; each chapter leans on the previous one.</p>
<p data-i18n="intro_p3">This guide will teach you every concept you need to build Codexion from scratch:</p>
<ul data-i18n="intro_ul">
<li><strong>POSIX Threads</strong> — Creating and managing concurrent execution flows</li>
<li><strong>Mutexes</strong> — Protecting shared resources from race conditions</li>
<li><strong>Condition Variables</strong> — Efficient waiting and signaling between threads</li>
<li><strong>The Dining Philosophers Problem</strong> — Classic synchronization puzzle</li>
<li><strong>Real-Time Scheduling</strong> — FIFO and Earliest Deadline First (EDF)</li>
<li><strong>Priority Queues</strong> — Heap-based scheduling implementation</li>
<li><strong>Time Management</strong> — Precise timing with <code>gettimeofday</code></li>
</ul>
<img alt="Coders and Dongles Layout" class="concept-img" src="images/coders_circle.png"/>
<p class="img-caption" data-i18n="intro_fig1">Figure 1: Coders arranged in a circle around the Quantum Compiler, each needing two adjacent dongles to compile.</p>
<div class="info-box tip">
<div class="info-box-title" data-i18n="intro_box2_title">Why Learn This?</div>
<p data-i18n="intro_box2_p">Concurrency is everywhere: operating systems, databases, web servers, game engines. Understanding threads, locks, and scheduling is essential for any systems programmer. Codexion distills these concepts into a tangible, visual problem.</p>
</div>
<h3 data-i18n="intro_h3_problem">The Problem Statement</h3>
<ul data-i18n="intro_ul_problem">
<li><code>N</code> coders sit in a circle around a Quantum Compiler</li>
<li>There are <code>N</code> USB dongles on the table — one between each pair of adjacent coders</li>
<li>Each coder needs <strong>both</strong> their left and right dongle to compile</li>
<li>After compiling, a coder debugs, then refactors, then compiles again</li>
<li>Each coder has a <strong>burnout deadline</strong>: if they don't compile within <code>time_to_burnout</code> ms of their last compile, they burn out</li>
<li>Dongles have a <strong>cooldown</strong>: after being released, they cannot be taken again for <code>dongle_cooldown</code> ms</li>
<li>Scheduling can be <code>fifo</code> (arrival order) or <code>edf</code> (earliest deadline first)</li>
<li>A <strong>monitor thread</strong> detects burnout and stops the simulation</li>
</ul>
<h3 data-i18n="intro_h3_walk">Walking Through One Coder's Life, Step by Step</h3>
<p data-i18n="intro_walk_p">Before diving into code, it helps to trace exactly what happens to a single coder, in plain English, from the moment the program starts:</p>
<ol data-i18n="intro_ol_walk">
<li>The coder is created as a thread and immediately records the current time as their "last compile" timestamp — the burnout clock starts ticking right away, even before they've compiled anything.</li>
<li>They try to pick up their left and right dongle. If either is currently held by a neighbour, or is still in cooldown, they cannot proceed — they must wait (Chapters 2 and 4 explain exactly how that waiting should happen without wasting CPU).</li>
<li>Once they hold both dongles, they <strong>compile</strong> for <code>time_to_compile</code> ms. This is the only moment that resets their burnout clock and increments their compile counter.</li>
<li>They release both dongles (each now enters cooldown for <code>dongle_cooldown</code> ms) and move on to <strong>debug</strong> for <code>time_to_debug</code> ms, then <strong>refactor</strong> for <code>time_to_refactor</code> ms — these two phases don't need any dongle at all.</li>
<li>They loop back to step 2 and try to compile again, unless they've already reached <code>compiles_required</code> compiles, in which case their thread function returns and they're done.</li>
<li>In parallel, completely independently, a <strong>monitor thread</strong> is constantly comparing "now" against every coder's last-compile timestamp plus <code>time_to_burnout</code>. The instant any coder crosses that line without having compiled again, the monitor declares a burnout and the whole simulation stops.</li>
</ol>
<p data-i18n="intro_walk_end">Every later chapter is really about implementing one piece of this list correctly and safely when it runs <code>N</code> times in parallel instead of once.</p>
<div class="info-box warning">
<div class="info-box-title" data-i18n="intro_box3_title">A subtlety beginners often miss</div>
<p data-i18n="intro_box3_p">The burnout deadline is not "N seconds after the program starts" — it's a <em>rolling</em> deadline measured from each coder's <em>own last compile</em>. A coder who just compiled is perfectly safe even if another coder is about to burn out. This is exactly why a scheduler that ignores deadlines (FIFO) can let a coder starve to death while happily serving everyone else in arrival order.</p>
</div>
</section>
<section id="setup">
<h2 data-i18n="h2_setup">Setup & Compilation</h2>
<p data-i18n="setup_0">Since Codexion uses POSIX threads, you need to link against the pthread library:</p>
<pre data-lang="bash"><code>cc -Wall -Wextra -Werror -pthread codexion.c -o codexion</code></pre>
<p data-i18n="setup_1">The <code>-pthread</code> flag tells the compiler to link the POSIX thread library and set up thread-safe compilation. On macOS, you may need <code>-lpthread</code> instead.</p>
<h3 data-i18n="setup_2">Required Headers</h3>
<pre data-lang="c"><code><span class="macro">#include</span> <span class="string"><pthread.h></span> <span class="comment">/* Threads, mutexes, condition variables */</span>
<span class="macro">#include</span> <span class="string"><sys/time.h></span> <span class="comment">/* gettimeofday */</span>
<span class="macro">#include</span> <span class="string"><unistd.h></span> <span class="comment">/* usleep */</span>
<span class="macro">#include</span> <span class="string"><stdio.h></span> <span class="comment">/* printf */</span>
<span class="macro">#include</span> <span class="string"><stdlib.h></span> <span class="comment">/* malloc, free, atoi */</span>
<span class="macro">#include</span> <span class="string"><string.h></span> <span class="comment">/* memset */</span></code></pre>
<div class="info-box warning">
<div class="info-box-title" data-i18n="setup_3">C89 Compatibility</div>
<p data-i18n="setup_4">The project requires C89, which means: declare all variables at the top of a block, no <code>//</code> comments (use <code>/* */</code>), no mixed declarations and code, and no variable-length arrays.</p>
</div>
</section>
<hr/>
<section id="ch1">
<h2><span class="chapter-number">1</span><span data-i18n="h2_ch1">POSIX Threads</span></h2>
<p data-i18n="ch1_0">A <strong>thread</strong> is an independent execution flow within a process. While a process has its own memory space, threads within the same process share that memory. This makes threads lightweight but requires synchronization to prevent conflicts.</p>
<h3 data-i18n="ch1_1">Process vs Thread — The Foundation</h3>
<p data-i18n="ch1_2">Before threads make sense, it helps to be precise about what a <strong>process</strong> is. When you run <code>./codexion</code>, the operating system creates a process: it gets its own private virtual memory space (its own view of RAM, isolated from every other program), its own file descriptors, its own program counter. Two processes cannot accidentally overwrite each other's variables — the OS enforces that isolation with page tables and hardware memory protection.</p>
<p data-i18n="ch1_3">A <strong>thread</strong> lives <em>inside</em> a process. When you call <code>pthread_create</code>, you are not creating a new isolated program — you are creating a second (or third, or eighth) execution flow that runs concurrently but shares the <em>same</em> memory space as every other thread in that process: the same global variables, the same heap-allocated structures, the same file descriptors. This is precisely why threads are useful (no expensive copying or message-passing needed to share data) and precisely why they are dangerous (any thread can silently corrupt data another thread is using, with no OS protection stopping it).</p>
<table data-i18n="ch1_4">
<tr><th>Aspect</th><th>Process</th><th>Thread</th></tr>
<tr><td>Memory space</td><td>Private, isolated</td><td>Shared with sibling threads</td></tr>
<tr><td>Creation cost</td><td>Expensive (new address space)</td><td>Cheap (reuses process memory)</td></tr>
<tr><td>Communication</td><td>Needs IPC (pipes, sockets, shared memory)</td><td>Direct — just read/write a shared variable</td></tr>
<tr><td>Crash isolation</td><td>One process crashing doesn't affect others</td><td>One thread crashing (segfault) kills the whole process</td></tr>
</table>
<h3 data-i18n="ch1_5">What "Concurrent" Actually Means on Real Hardware</h3>
<p data-i18n="ch1_6">On a machine with a single CPU core, the operating system's scheduler rapidly switches between threads (a few milliseconds each), giving the <em>illusion</em> of simultaneous execution — this is <strong>concurrency</strong>. On a machine with multiple cores, threads can genuinely run at the exact same instant on different cores — this is <strong>parallelism</strong>. Codexion's correctness requirements (no lost updates, no missed deadlines) must hold in both cases: your synchronization code cannot assume "only one thread is truly running at a time", because on a multi-core machine that assumption is simply false.</p>
<img alt="Thread Lifecycle" class="concept-img" src="images/thread_lifecycle.png"/>
<p class="img-caption" data-i18n="ch1_7">Figure 2: The lifecycle of a POSIX thread from creation to termination.</p>
<h3 data-i18n="ch1_8">The Return Type Puzzle: <code>void *</code></h3>
<p data-i18n="ch1_9">Beginners are often confused by why a thread's start function must have the exact signature <code>void *f(void *arg)</code>. The answer is that <code>pthread_create</code> is a single, generic C function that has no idea what data your thread needs or returns — it can only work with a signature that fits <em>any</em> use case. <code>void *</code> ("pointer to anything") is C's way of saying "I don't know or care about the type, you cast it back to whatever it really is inside the function." The argument you pass in comes back out unchanged in the corresponding <code>pthread_join</code>'s second parameter if you called <code>pthread_exit</code> with a return value, letting a thread hand a result back to whoever joins it.</p>
<h3 data-i18n="ch1_10">Creating a Thread</h3>
<pre data-lang="c"><code><span class="macro">#include</span> <span class="string"><pthread.h></span>
<span class="type">void</span> *<span class="function">thread_function</span>(<span class="type">void</span> *arg)
{
<span class="type">int</span> id = *(<span class="type">int</span> *)arg;
<span class="function">printf</span>(<span class="string">"Thread %d is running\n"</span>, id);
<span class="keyword">return</span> (<span class="type">void</span> *)<span class="number">0</span>;
}
<span class="type">int</span> <span class="function">main</span>(<span class="type">void</span>)
{
<span class="type">pthread_t</span> thread;
<span class="type">int</span> id = <span class="number">1</span>;
<span class="comment">/* Create a new thread */</span>
<span class="function">pthread_create</span>(&thread, <span class="macro">NULL</span>, thread_function, &id);
<span class="comment">/* Wait for the thread to finish */</span>
<span class="function">pthread_join</span>(thread, <span class="macro">NULL</span>);
<span class="keyword">return</span> <span class="number">0</span>;
}</code></pre>
<h3 data-i18n="ch1_11">Key Functions</h3>
<table data-i18n="ch1_12">
<tr><th>Function</th><th>Purpose</th></tr>
<tr><td><code>pthread_create</code></td><td>Creates a new thread. Takes: thread ID pointer, attributes, start function, argument.</td></tr>
<tr><td><code>pthread_join</code></td><td>Waits for a thread to finish. Blocks until the target thread terminates.</td></tr>
<tr><td><code>pthread_exit</code></td><td>Terminates the calling thread. Other threads continue running.</td></tr>
<tr><td><code>pthread_detach</code></td><td>Marks a thread as detached — resources are freed automatically on exit. Cannot be joined.</td></tr>
</table>
<h3 data-i18n="ch1_13">Passing Arguments to Threads</h3>
<p data-i18n="ch1_14">Never pass a local variable's address if it might change before the thread reads it. Use dynamically allocated memory or an array:</p>
<pre data-lang="c"><code><span class="type">int</span> <span class="function">main</span>(<span class="type">void</span>)
{
<span class="type">pthread_t</span> threads[<span class="number">5</span>];
<span class="type">int</span> ids[<span class="number">5</span>];
<span class="type">int</span> i;
<span class="keyword">for</span> (i = <span class="number">0</span>; i < <span class="number">5</span>; i++)
{
ids[i] = i + <span class="number">1</span>;
<span class="function">pthread_create</span>(&threads[i], <span class="macro">NULL</span>, thread_function, &ids[i]);
}
<span class="keyword">for</span> (i = <span class="number">0</span>; i < <span class="number">5</span>; i++)
<span class="function">pthread_join</span>(threads[i], <span class="macro">NULL</span>);
<span class="keyword">return</span> <span class="number">0</span>;
}</code></pre>
<div class="info-box danger">
<div class="info-box-title" data-i18n="ch1_15">Race Condition Warning</div>
<p data-i18n="ch1_16">If you pass <code>&i</code> instead of <code>&ids[i]</code> in the loop above, all threads might read the same value because <code>i</code> changes in the main thread while child threads are still starting up. This is a classic concurrency bug.</p>
</div>
<h3 data-i18n="ch1_17">What Happens If You Forget <code>pthread_join</code>?</h3>
<p data-i18n="ch1_18">If <code>main()</code> returns (or calls <code>exit()</code>) before joining every thread it created, the whole process — and every thread inside it — is terminated immediately, mid-work, with no cleanup. This is one of the most common beginner bugs: the program appears to "not print anything" or "stop halfway", when really the child threads simply never got the chance to finish because <code>main</code> raced ahead and ended the process first. In Codexion, <code>main</code> must join every coder thread <em>and</em> the monitor thread before returning, in the right order (see Chapter 11, Part F), or you will lose output non-deterministically — the bug will even seem to appear and disappear between runs, which is a hallmark of concurrency bugs in general.</p>
<h3 data-i18n="ch1_19">Joinable vs Detached — Two Different Lifecycles</h3>
<p data-i18n="ch1_20">By default, a thread is <strong>joinable</strong>: its resources (like its exit status) stay alive after it finishes, until some other thread calls <code>pthread_join</code> on it — exactly like a zombie process waiting to be reaped by <code>wait()</code>. If nobody ever joins it, that memory leaks for the lifetime of the process. A <strong>detached</strong> thread (via <code>pthread_detach</code>) is the opposite: the system cleans it up automatically the moment it finishes, but as a trade-off you can never <code>pthread_join</code> it afterwards to retrieve a result or simply know it's done. Codexion uses joinable threads throughout, because the monitor and main function genuinely need to know when coders have finished.</p>
<div class="info-box tip">
<div class="info-box-title" data-i18n="ch1_21">Mental model to remember</div>
<p data-i18n="ch1_22">Think of <code>pthread_create</code> as "spawn a worker and hand me a ticket", and <code>pthread_join</code> as "wait in line and redeem that ticket." If you throw the ticket away without redeeming it (never calling <code>pthread_join</code> on a joinable thread), the worker's completion status just sits there, unclaimed, wasting a small amount of kernel memory until your process exits.</p>
</div>
</section>
<section id="ch2">
<h2><span class="chapter-number">2</span><span data-i18n="h2_ch2">Mutexes</span></h2>
<p data-i18n="ch2_0">A <strong>mutex</strong> (mutual exclusion lock) ensures that only one thread can access a critical section at a time. Think of it as a lock on a door: whoever holds the key can enter, everyone else waits outside.</p>
<h3 data-i18n="ch2_1">What Exactly Is a "Critical Section"?</h3>
<p data-i18n="ch2_2">A <strong>critical section</strong> is any piece of code that reads or writes shared state (a global variable, a struct on the heap, a shared array) in a way that would break if two threads executed it at the exact same time. Not all code needs protection — a thread doing purely local, private computation (like sleeping for <code>time_to_compile</code> ms, which touches no shared memory) needs no lock at all. The skill of concurrent programming is largely about correctly identifying <em>which</em> lines are critical sections and locking exactly those — no more (or you lose parallelism for no reason), no less (or you get bugs).</p>
<p data-i18n="ch2_3">In Codexion, examples of critical sections include: incrementing a coder's <code>compile_count</code>, changing a dongle's <code>holder_id</code>, checking or setting <code>sim_over</code>, and printing a log line (so two coders' output lines don't interleave character-by-character). Each of these needs its own mutex, or a shared one, protecting it.</p>
<img alt="Mutex Concept" class="concept-img" src="images/mutex_concept.png"/>
<p class="img-caption" data-i18n="ch2_4">Figure 3: Thread 1 holds the mutex and accesses the shared resource. Threads 2 and 3 wait.</p>
<h3 data-i18n="ch2_5">Basic Mutex Operations</h3>
<pre data-lang="c"><code><span class="type">pthread_mutex_t</span> lock;
<span class="comment">/* Initialize */</span>
<span class="function">pthread_mutex_init</span>(&lock, <span class="macro">NULL</span>);
<span class="comment">/* Lock (blocks if already locked) */</span>
<span class="function">pthread_mutex_lock</span>(&lock);
<span class="comment">/* Critical section — only one thread here at a time */</span>
shared_counter++;
<span class="comment">/* Unlock */</span>
<span class="function">pthread_mutex_unlock</span>(&lock);
<span class="comment">/* Destroy when done */</span>
<span class="function">pthread_mutex_destroy</span>(&lock);</code></pre>
<h3 data-i18n="ch2_6">Why Mutexes Matter: The Lost Update</h3>
<p data-i18n="ch2_7">Without a mutex, two threads incrementing the same variable can lose updates:</p>
<pre data-lang="c"><code><span class="comment">/* Thread A reads counter = 5 */</span>
<span class="comment">/* Thread B reads counter = 5 (before A writes) */</span>
<span class="comment">/* Thread A writes counter = 6 */</span>
<span class="comment">/* Thread B writes counter = 6 (overwrites A's update!) */</span>
<span class="comment">/* Expected: 7, Actual: 6 — LOST UPDATE */</span></code></pre>
<h3 data-i18n="ch2_8">Blocking Is the Whole Point</h3>
<p data-i18n="ch2_9">It's worth pausing on what "<code>pthread_mutex_lock</code> blocks if already locked" really means in practice: the calling thread is put to sleep by the operating system — it consumes essentially zero CPU while waiting — and is automatically woken up by the kernel the moment the mutex becomes free. You do not need (and should never write) a loop like <code>while (locked) {}</code> around a mutex; that would be a <strong>busy-wait</strong> that burns 100% of a CPU core doing nothing useful. The mutex primitive already does the efficient sleep-and-wake for you.</p>
<h3 data-i18n="ch2_10">Granularity: One Big Lock vs Many Small Locks</h3>
<p data-i18n="ch2_11">A common beginner instinct is to protect an entire program with a single global mutex, locked at the very start of every thread's work and unlocked at the very end. This is <em>correct</em> (no race condition can happen) but destroys the whole point of using threads: if only one coder can ever be "inside the lock" at a time, your 8 coders run one after another, not concurrently, and you gain nothing over a single-threaded program. Codexion is specifically designed to force you to think about granularity: each dongle typically gets its <em>own</em> mutex, so a coder waiting for dongle 3 does not block a completely unrelated coder who only needs dongles 5 and 6. Locking too coarsely kills performance; locking too finely (or inconsistently) reintroduces race conditions and deadlocks. There is no formula — it's a design decision you justify by reasoning about which operations truly must be mutually exclusive.</p>
<h3 data-i18n="ch2_12">Try-Lock (Non-Blocking)</h3>
<p data-i18n="ch2_13"><code>pthread_mutex_trylock</code> attempts to acquire the lock without blocking. It returns <code>0</code> on success, <code>EBUSY</code> if already locked:</p>
<pre data-lang="c"><code><span class="keyword">if</span> (<span class="function">pthread_mutex_trylock</span>(&lock) == <span class="number">0</span>)
{
<span class="comment">/* Got the lock */</span>
<span class="function">pthread_mutex_unlock</span>(&lock);
}
<span class="keyword">else</span>
{
<span class="comment">/* Lock was busy, do something else */</span>
}</code></pre>
<h3 data-i18n="ch2_14">Mutex Attributes (Error Checking)</h3>
<pre data-lang="c"><code><span class="type">pthread_mutexattr_t</span> attr;
<span class="function">pthread_mutexattr_init</span>(&attr);
<span class="function">pthread_mutexattr_settype</span>(&attr, PTHREAD_MUTEX_ERRORCHECK);
<span class="function">pthread_mutex_init</span>(&lock, &attr);
<span class="function">pthread_mutexattr_destroy</span>(&attr);</code></pre>
<div class="info-box tip">
<div class="info-box-title" data-i18n="ch2_15">Mutex Best Practices</div>
<ul data-i18n="ch2_16">
<li>Always pair <code>lock</code> with <code>unlock</code> — use the same thread for both</li>
<li>Keep critical sections as short as possible</li>
<li>Lock order matters: always acquire locks in the same order to prevent deadlock</li>
<li>Initialize before use, destroy after all threads are done</li>
</ul>
</div>
</section>
<section id="ch3">
<h2><span class="chapter-number">3</span><span data-i18n="h2_ch3">Condition Variables</span></h2>
<p data-i18n="ch3_0">A <strong>condition variable</strong> allows threads to wait for a condition to become true without consuming CPU. Unlike a mutex which only provides mutual exclusion, a condition variable provides <strong>synchronization</strong> — threads can sleep until signaled to wake up.</p>
<div class="info-box purple">
<div class="info-box-title" data-i18n="ch3_1">Analogy: the waiting room</div>
<p data-i18n="ch3_2">A mutex is like a single-occupancy restroom key: it only ever answers "can I go in right now?". A condition variable is the waiting room next to it, with a receptionist: a thread that can't proceed yet doesn't just stand at the door repeatedly rattling the handle (that would be spin-waiting — see below), it sits down and is <em>woken up by the receptionist</em> exactly when something relevant changes. It still has to re-check for itself that it's really its turn (someone else might have slipped in first), but it isn't wasting energy while it waits. This "sit and be woken up" behaviour is exactly what a coder thread should do while waiting for its two dongles to become free, instead of hammering <code>pthread_mutex_trylock</code> in a tight loop.</p>
</div>
<h3 data-i18n="ch3_3">Why Not Just Spin?</h3>
<pre data-lang="c"><code><span class="comment">/* BAD: Spin-waiting wastes 100% CPU */</span>
<span class="keyword">while</span> (!ready)
; <span class="comment">/* CPU burns waiting */</span>
<span class="comment">/* GOOD: Condition variable puts thread to sleep */</span>
<span class="function">pthread_mutex_lock</span>(&lock);
<span class="keyword">while</span> (!ready)
<span class="function">pthread_cond_wait</span>(&cond, &lock); <span class="comment">/* Thread sleeps, releases lock */</span>
<span class="function">pthread_mutex_unlock</span>(&lock);</code></pre>
<h3 data-i18n="ch3_4">The Three Condition Variable Operations</h3>
<table data-i18n="ch3_5">
<tr><th>Function</th><th>Purpose</th></tr>
<tr><td><code>pthread_cond_wait</code></td><td>Atomically release mutex and block until signaled. Re-acquires mutex before returning.</td></tr>
<tr><td><code>pthread_cond_signal</code></td><td>Wake up <strong>one</strong> waiting thread.</td></tr>
<tr><td><code>pthread_cond_broadcast</code></td><td>Wake up <strong>all</strong> waiting threads.</td></tr>
<tr><td><code>pthread_cond_timedwait</code></td><td>Wait with a timeout. Returns <code>ETIMEDOUT</code> if deadline passes.</td></tr>
</table>
<h3 data-i18n="ch3_6">Producer-Consumer Example</h3>
<p data-i18n="ch3_7">This classic pattern shows how condition variables coordinate work between threads:</p>
<pre data-lang="c"><code><span class="type">pthread_mutex_t</span> lock = PTHREAD_MUTEX_INITIALIZER;
<span class="type">pthread_cond_t</span> cond = PTHREAD_COND_INITIALIZER;
<span class="type">int</span> data_ready = <span class="number">0</span>;
<span class="type">void</span> *<span class="function">consumer</span>(<span class="type">void</span> *arg)
{
(<span class="type">void</span>)arg;
<span class="function">pthread_mutex_lock</span>(&lock);
<span class="keyword">while</span> (!data_ready)
{
<span class="comment">/* Atomically: unlock mutex + sleep on cond + relock on wakeup */</span>
<span class="function">pthread_cond_wait</span>(&cond, &lock);
}
<span class="comment">/* Now we hold the lock and data_ready is true */</span>
<span class="function">printf</span>(<span class="string">"Consumer: got data!\n"</span>);
data_ready = <span class="number">0</span>;
<span class="function">pthread_mutex_unlock</span>(&lock);
<span class="keyword">return</span> <span class="macro">NULL</span>;
}
<span class="type">void</span> *<span class="function">producer</span>(<span class="type">void</span> *arg)
{
(<span class="type">void</span>)arg;
<span class="function">sleep</span>(<span class="number">1</span>); <span class="comment">/* Simulate work */</span>
<span class="function">pthread_mutex_lock</span>(&lock);
data_ready = <span class="number">1</span>;
<span class="function">pthread_cond_signal</span>(&cond); <span class="comment">/* Wake up consumer */</span>
<span class="function">pthread_mutex_unlock</span>(&lock);
<span class="keyword">return</span> <span class="macro">NULL</span>;
}</code></pre>
<h3 data-i18n="ch3_8">Why <code>while</code> and Not <code>if</code>?</h3>
<p data-i18n="ch3_9">Always use <code>while</code> with <code>pthread_cond_wait</code>. This protects against <strong>spurious wakeups</strong> — rare events where a thread wakes up without being signaled. The while loop re-checks the condition:</p>
<pre data-lang="c"><code><span class="comment">/* WRONG: If spurious wakeup happens, condition may still be false */</span>
<span class="keyword">if</span> (!ready)
<span class="function">pthread_cond_wait</span>(&cond, &lock);
<span class="comment">/* CORRECT: Re-check condition after every wakeup */</span>
<span class="keyword">while</span> (!ready)
<span class="function">pthread_cond_wait</span>(&cond, &lock);</code></pre>
<h3 data-i18n="ch3_10">Timed Wait</h3>
<p data-i18n="ch3_11"><code>pthread_cond_timedwait</code> is essential for Codexion's burnout detection. It waits but returns <code>ETIMEDOUT</code> if the deadline passes:</p>
<pre data-lang="c"><code><span class="type">struct timespec</span> deadline;
<span class="function">clock_gettime</span>(CLOCK_REALTIME, &deadline);
deadline.tv_sec += <span class="number">1</span>; <span class="comment">/* Wait up to 1 second */</span>
<span class="type">int</span> rc = <span class="function">pthread_cond_timedwait</span>(&cond, &lock, &deadline);
<span class="keyword">if</span> (rc == ETIMEDOUT)
<span class="function">printf</span>(<span class="string">"Timeout!\n"</span>);</code></pre>
<h3 data-i18n="ch3_12">The "Lost Wakeup" Bug — Why the Mutex Is Mandatory</h3>
<p data-i18n="ch3_13">It might seem redundant that <code>pthread_cond_wait</code> forces you to hold a mutex. Here's exactly the bug that requirement prevents. Imagine, incorrectly, that checking the condition and waiting on it were two separate, unprotected steps:</p>
<pre data-lang="c"><code><span class="comment">/* BROKEN: condition check and wait are not atomic */</span>
<span class="keyword">if</span> (!ready) <span class="comment">/* (1) consumer checks: not ready yet */</span>
{
<span class="comment">/* --- producer runs right here: sets ready=1, signals --- */</span>
<span class="comment">/* but NOBODY is waiting on the cond var yet! */</span>
<span class="function">pthread_cond_wait</span>(&cond, &lock); <span class="comment">/* (2) consumer waits — forever */</span>
}</code></pre>
<p data-i18n="ch3_14">Between steps (1) and (2), the producer thread could run to completion, set the flag, and signal — but since the consumer wasn't registered as a waiter yet, that signal is lost forever, and the consumer sleeps forever waiting for a wakeup that already happened. This is called a <strong>lost wakeup</strong>, and it's exactly why <code>pthread_cond_wait</code> takes the mutex as a parameter: it guarantees that "checking the condition" and "starting to wait" happen as one atomic, uninterruptible step relative to the thread doing the signalling (which must also hold the same mutex while it changes the shared condition and signals). This is the single most important invariant to internalize about condition variables.</p>
<div class="info-box warning">
<div class="info-box-title" data-i18n="ch3_15">Important</div>
<p data-i18n="ch3_16"><code>pthread_cond_wait</code> always requires a locked mutex. It atomically releases the mutex and puts the thread to sleep. When woken, it re-acquires the mutex before returning. This prevents lost wakeups.</p>
</div>
</section>
<section id="ch4">
<h2><span class="chapter-number">4</span><span data-i18n="h2_ch4">The Dining Philosophers Problem</span></h2>
<p data-i18n="ch4_0">Codexion is a variation of the <strong>Dining Philosophers</strong> — a classic concurrency problem where N philosophers sit at a round table with N forks. Each philosopher needs two forks to eat. The challenge: prevent deadlock and starvation.</p>
<h3 data-i18n="ch4_1">Two Different Failure Modes — Don't Confuse Them</h3>
<p data-i18n="ch4_2">Beginners often lump "deadlock" and "starvation" together, but they are distinct failures with distinct causes, and Codexion explicitly tests for both:</p>
<table data-i18n="ch4_3">
<tr><th>Failure</th><th>What happens</th><th>How Codexion would show it</th></tr>
<tr><td><strong>Deadlock</strong></td><td>Every thread is permanently blocked waiting on another thread in the group — nobody can ever make progress again.</td><td>All coders hold one dongle and wait forever for their second. The whole program hangs, with no more output, forever.</td></tr>
<tr><td><strong>Starvation</strong></td><td>The system as a whole keeps making progress, but one specific thread is unlucky enough to never (or rarely) get served.</td><td>Coders 1, 2, 3... keep compiling happily, but coder 4 keeps losing the race for dongles every time and eventually burns out — the program keeps running right up until that burnout is detected.</td></tr>
</table>
<p data-i18n="ch4_4">A scheduler can fix one without fixing the other: naive FIFO prevents deadlock (nobody holds a dongle forever without eventually being served) but does not guarantee that the coder closest to burnout gets served first — which is exactly the gap that EDF scheduling (Chapter 6) closes.</p>
<img alt="Deadlock Scenario" class="concept-img" src="images/deadlock.png"/>
<p class="img-caption" data-i18n="ch4_5">Figure 4: Deadlock — every coder holds one dongle and waits for another.</p>
<h3 data-i18n="ch4_6">The Deadlock Scenario</h3>
<p data-i18n="ch4_7">If every coder picks up their left dongle simultaneously, then tries to pick up their right dongle, everyone waits forever:</p>
<pre data-lang="c"><code><span class="comment">/* DANGEROUS: Can cause deadlock */</span>
<span class="function">pthread_mutex_lock</span>(&left_dongle); <span class="comment">/* Everyone grabs left */</span>
<span class="function">pthread_mutex_lock</span>(&right_dongle); <span class="comment">/* Everyone waits for right -> DEADLOCK */</span>
<span class="comment">/* Compile... */</span>
<span class="function">pthread_mutex_unlock</span>(&right_dongle);
<span class="function">pthread_mutex_unlock</span>(&left_dongle);</code></pre>
<h3 data-i18n="ch4_8">Why Deadlock Happens: The Four Coffman Conditions</h3>
<p data-i18n="ch4_9">Deadlock is not random bad luck — it is a precise, well-understood phenomenon that can <em>only</em> occur if all four of the following conditions hold simultaneously. Understanding them tells you exactly which lever to pull to prevent it:</p>
<ol data-i18n="ch4_10">
<li><strong>Mutual exclusion</strong> — a resource (a dongle) can only be held by one thread at a time.</li>
<li><strong>Hold and wait</strong> — a thread holding one resource (its left dongle) is waiting to acquire another (its right dongle) without releasing what it already has.</li>
<li><strong>No preemption</strong> — a resource cannot be forcibly taken away from the thread holding it; it can only be released voluntarily.</li>
<li><strong>Circular wait</strong> — there exists a cycle of threads where each is waiting for a resource held by the next one in the cycle (coder 1 waits for coder 2's dongle, who waits for coder 3's, ..., who waits for coder 1's).</li>
</ol>
<p data-i18n="ch4_11">Break <em>any one</em> of these four conditions and deadlock becomes structurally impossible, no matter how unlucky your thread scheduling is. Each solution below breaks a different one.</p>
<h3 data-i18n="ch4_12">Solution 1: Asymmetric Lock Ordering</h3>
<p data-i18n="ch4_13">This solution breaks <strong>circular wait</strong>: if every coder agrees on a global ordering for picking up dongles (instead of everyone symmetrically starting with "my left one"), the cycle in condition 4 can never form — there will always be at least one coder who picked up the lower-numbered dongle first and can therefore always get the second one.</p>
<p data-i18n="ch4_14">Force an ordering: odd-numbered coders pick left first, even-numbered pick right first. This breaks the circular wait condition:</p>
<pre data-lang="c"><code><span class="keyword">if</span> (coder_id % <span class="number">2</span> == <span class="number">0</span>)
{
<span class="function">pthread_mutex_lock</span>(&right_dongle);
<span class="function">pthread_mutex_lock</span>(&left_dongle);
}
<span class="keyword">else</span>
{
<span class="function">pthread_mutex_lock</span>(&left_dongle);
<span class="function">pthread_mutex_lock</span>(&right_dongle);
}</code></pre>
<h3 data-i18n="ch4_15">Solution 2: Try-Lock with Backoff</h3>
<p data-i18n="ch4_16">Pick up the first dongle, then try the second. If unavailable, release the first and retry:</p>
<pre data-lang="c"><code><span class="keyword">while</span> (<span class="number">1</span>)
{
<span class="function">pthread_mutex_lock</span>(&left_dongle);
<span class="keyword">if</span> (<span class="function">pthread_mutex_trylock</span>(&right_dongle) == <span class="number">0</span>)
<span class="keyword">break</span>; <span class="comment">/* Got both! */</span>
<span class="function">pthread_mutex_unlock</span>(&left_dongle); <span class="comment">/* Release and retry */</span>
<span class="function">usleep</span>(<span class="number">100</span>); <span class="comment">/* Small delay to prevent livelock */</span>
}</code></pre>
<h3 data-i18n="ch4_17">Solution 3: Condition Variables with Scheduler</h3>
<p data-i18n="ch4_18">This is the Codexion approach. Instead of blindly grabbing dongles, coders request access through a centralized scheduler that grants permission based on FIFO or EDF ordering:</p>
<pre data-lang="c"><code><span class="comment">/* Coder requests both dongles from scheduler */</span>
<span class="function">request_dongles</span>(coder_id);
<span class="comment">/* Scheduler grants access, coder compiles */</span>
<span class="function">compile</span>();
<span class="comment">/* Release dongles back to scheduler */</span>
<span class="function">release_dongles</span>(coder_id);</code></pre>
<div class="info-box success">
<div class="info-box-title" data-i18n="ch4_19">Deadlock Prevention Summary</div>
<p data-i18n="ch4_20">To prevent deadlock, eliminate one of the four Coffman conditions:</p>
<ol data-i18n="ch4_21">
<li><strong>Mutual Exclusion</strong> — Dongles are exclusive (required)</li>
<li><strong>Hold and Wait</strong> — Break by releasing held resources if second unavailable</li>
<li><strong>No Preemption</strong> — Break by allowing forced release (not used here)</li>
<li><strong>Circular Wait</strong> — Break by enforcing lock ordering or centralized scheduling</li>
</ol>
</div>
</section>
<section id="ch5">
<h2><span class="chapter-number">5</span><span data-i18n="h2_ch5">Time in C</span></h2>
<p data-i18n="ch5_0">Precise timing is critical for Codexion. You need to measure elapsed time in milliseconds, compute deadlines, and implement cooldowns.</p>
<h3 data-i18n="ch5_1">Why Milliseconds, and Why <code>long long</code>?</h3>
<p data-i18n="ch5_2">Codexion's arguments (<code>time_to_burnout</code>, <code>time_to_compile</code>, etc.) are given in milliseconds because that's precise enough for human-observable timing (sub-millisecond precision would be pointless — thread scheduling jitter alone is often a millisecond or more) while staying easy to reason about mentally, unlike raw microseconds. The timestamps themselves are stored as <code>long long</code> (a 64-bit integer, at least 8 bytes) rather than a plain <code>int</code> (usually 4 bytes, max ~2.1 billion) because <code>gettimeofday</code> returns seconds since 1 January 1970 — that value alone is already over 1.7 billion and counting, and once you multiply it by 1000 to get milliseconds it would silently overflow a 32-bit <code>int</code> and wrap around to a negative number, corrupting every deadline comparison in your program. This is a real, common beginner bug — always use a 64-bit type for absolute millisecond timestamps.</p>
<h3 data-i18n="ch5_3">gettimeofday</h3>
<p data-i18n="ch5_4">Returns the current time with microsecond precision:</p>
<pre data-lang="c"><code><span class="macro">#include</span> <span class="string"><sys/time.h></span>
<span class="type">long</span> <span class="type">long</span> <span class="function">get_time_ms</span>(<span class="type">void</span>)
{
<span class="type">struct timeval</span> tv;
<span class="function">gettimeofday</span>(&tv, <span class="macro">NULL</span>);
<span class="keyword">return</span> ((<span class="type">long</span> <span class="type">long</span>)tv.tv_sec * <span class="number">1000</span>) + (tv.tv_usec / <span class="number">1000</span>);
}</code></pre>
<h3 data-i18n="ch5_5">usleep</h3>
<p data-i18n="ch5_6">Suspends the calling thread for a specified number of microseconds:</p>
<pre data-lang="c"><code><span class="macro">#include</span> <span class="string"><unistd.h></span>
<span class="function">usleep</span>(<span class="number">200000</span>); <span class="comment">/* Sleep for 200 milliseconds */</span>
<span class="function">usleep</span>(<span class="number">5000</span>); <span class="comment">/* Sleep for 5 milliseconds */</span></code></pre>
<div class="info-box warning">
<div class="info-box-title" data-i18n="ch5_7">Precision Warning</div>
<p data-i18n="ch5_8"><code>usleep</code> is not guaranteed to sleep for exactly the requested time. The OS scheduler may delay resumption. For Codexion, use <code>gettimeofday</code> to measure actual elapsed time rather than relying solely on <code>usleep</code>.</p>
</div>
<h3 data-i18n="ch5_9">Computing Deadlines</h3>
<pre data-lang="c"><code><span class="type">long</span> <span class="type">long</span> last_compile_start = <span class="function">get_time_ms</span>();
<span class="type">long</span> <span class="type">long</span> time_to_burnout = <span class="number">800</span>; <span class="comment">/* ms */</span>
<span class="comment">/* Check if burned out */</span>
<span class="keyword">if</span> (<span class="function">get_time_ms</span>() - last_compile_start > time_to_burnout)
<span class="function">printf</span>(<span class="string">"BURNOUT!\n"</span>);</code></pre>
<h3 data-i18n="ch5_10">Timed Condition Wait with Absolute Time</h3>
<pre data-lang="c"><code><span class="type">struct timespec</span> <span class="function">ms_to_timespec</span>(<span class="type">long</span> <span class="type">long</span> ms)
{
<span class="type">struct timespec</span> ts;
<span class="type">struct timeval</span> tv;
<span class="function">gettimeofday</span>(&tv, <span class="macro">NULL</span>);
ts.tv_sec = tv.tv_sec + (ms / <span class="number">1000</span>);
ts.tv_nsec = (tv.tv_usec * <span class="number">1000</span>) + ((ms % <span class="number">1000</span>) * <span class="number">1000000</span>);
<span class="keyword">if</span> (ts.tv_nsec >= <span class="number">1000000000</span>)
{
ts.tv_sec++;
ts.tv_nsec -= <span class="number">1000000000</span>;
}
<span class="keyword">return</span> ts;
}</code></pre>
<div class="info-box danger">
<div class="info-box-title" data-i18n="ch5_11">Wall Clock vs Monotonic Clock</div>
<p data-i18n="ch5_12"><code>gettimeofday</code> reads the system's <strong>wall clock</strong> (real calendar time), which can jump backwards or forwards if the OS resynchronizes it (e.g. via NTP) while your simulation is running — a burnout check like <code>now - last_compile > time_to_burnout</code> could then behave unpredictably. In production-grade real-time code you would prefer <code>clock_gettime(CLOCK_MONOTONIC, ...)</code>, which never jumps backwards, precisely because it doesn't represent calendar time at all — only "time since some arbitrary fixed point." Many school subjects (and this guide) still teach <code>gettimeofday</code> for simplicity since the risk is negligible over a program that runs for a few seconds, but it's worth knowing the more robust alternative exists.</p>
</div>
</section>
<section id="ch6">
<h2><span class="chapter-number">6</span><span data-i18n="h2_ch6">Scheduling: FIFO vs EDF</span></h2>
<p data-i18n="ch6_0">When multiple coders request the same dongle, the scheduler decides who gets it first. Codexion implements two scheduling policies.</p>
<img alt="FIFO vs EDF Scheduling" class="concept-img" src="images/fifo_vs_edf.png"/>
<p class="img-caption" data-i18n="ch6_1">Figure 5: FIFO serves requests in arrival order. EDF reorders by urgency (earliest deadline).</p>
<h3 data-i18n="ch6_2">FIFO (First In, First Out)</h3>
<p data-i18n="ch6_3">Requests are queued in arrival order. Simple, fair, but doesn't account for urgency:</p>
<pre data-lang="c"><code><span class="comment">/* Simple linked list queue */</span>
<span class="keyword">typedef struct</span> <span class="type">s_request</span>
{
<span class="type">int</span> coder_id;
<span class="keyword">struct</span> <span class="type">s_request</span> *next;
} <span class="type">t_request</span>;
<span class="keyword">typedef struct</span>
{
<span class="type">t_request</span> *head;
<span class="type">t_request</span> *tail;
} <span class="type">t_fifo_queue</span>;
<span class="type">void</span> <span class="function">fifo_enqueue</span>(<span class="type">t_fifo_queue</span> *q, <span class="type">int</span> coder_id)
{
<span class="type">t_request</span> *req = <span class="function">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">t_request</span>));
req->coder_id = coder_id;
req->next = <span class="macro">NULL</span>;
<span class="keyword">if</span> (q->tail)
q->tail->next = req;
<span class="keyword">else</span>
q->head = req;
q->tail = req;
}
<span class="type">int</span> <span class="function">fifo_dequeue</span>(<span class="type">t_fifo_queue</span> *q)
{
<span class="type">t_request</span> *req;
<span class="type">int</span> id;
<span class="keyword">if</span> (!q->head)
<span class="keyword">return</span> -<span class="number">1</span>;
req = q->head;
id = req->coder_id;
q->head = req->next;
<span class="keyword">if</span> (!q->head)
q->tail = <span class="macro">NULL</span>;
<span class="function">free</span>(req);
<span class="keyword">return</span> id;
}</code></pre>
<h3 data-i18n="ch6_4">EDF (Earliest Deadline First)</h3>
<p data-i18n="ch6_5">Each coder has a burnout deadline computed as <code>last_compile_start + time_to_burnout</code>. EDF serves the coder whose deadline is closest:</p>
<pre data-lang="c"><code><span class="comment">/* Priority = deadline (lower = higher priority) */</span>
<span class="keyword">typedef struct</span> <span class="type">s_request</span>
{
<span class="type">int</span> coder_id;
<span class="type">long</span> <span class="type">long</span> deadline; <span class="comment">/* burnout deadline in ms */</span>
} <span class="type">t_request</span>;
<span class="comment">/* Min-heap: parent has earlier deadline than children */</span>
<span class="keyword">typedef struct</span>
{
<span class="type">t_request</span> *arr;
<span class="type">int</span> size;
<span class="type">int</span> capacity;
} <span class="type">t_edf_queue</span>;</code></pre>
<h3 data-i18n="ch6_6">A Worked Example: Watch FIFO Fail Where EDF Succeeds</h3>
<p data-i18n="ch6_7">Suppose two coders both become ready to compile at the same instant (<code>t = 0</code>), but only one dongle pair is free at a time so they can't be served simultaneously. Say <code>time_to_burnout = 100ms</code> for both, but coder A last compiled at <code>t = -80</code> (so their deadline is <code>t = 20</code>) while coder B last compiled at <code>t = -10</code> (deadline <code>t = 90</code>). Coder A arrived in the queue microseconds after coder B purely by chance.</p>
<table data-i18n="ch6_8">
<tr><th>Scheduler</th><th>Serves first</th><th>Outcome</th></tr>
<tr><td>FIFO</td><td>Coder B (arrived first)</td><td>Coder A must wait; if serving B takes long enough, A crosses its deadline at <code>t = 20</code> and <strong>burns out</strong>, even though A was objectively more urgent.</td></tr>
<tr><td>EDF</td><td>Coder A (earlier deadline, <code>t = 20 < 90</code>)</td><td>A compiles first and resets its deadline; B still has until <code>t = 90</code> to be served, comfortably surviving.</td></tr>
</table>
<p data-i18n="ch6_9">This is the entire reason Codexion asks you to implement two schedulers: it's not a stylistic choice, it's a demonstration that <strong>arrival order and urgency are different things</strong>, and a scheduler that only knows about arrival order can let an urgent thread starve even while being perfectly "fair" by its own definition.</p>
<div class="info-box purple">
<div class="info-box-title" data-i18n="ch6_10">EDF Optimality</div>
<p data-i18n="ch6_11">EDF is <strong>optimal</strong> for uniprocessor scheduling: if any schedule can meet all deadlines, EDF will too. This makes it ideal for Codexion's burnout prevention. Note the precise meaning of "optimal" here: it means <em>if a feasible schedule exists at all</em>, EDF will find it. It does <em>not</em> mean EDF can rescue an overloaded system — if your parameters simply don't leave enough time for every coder to be served before their deadlines (e.g. <code>time_to_compile</code> too large relative to <code>time_to_burnout</code> and the number of coders sharing dongles), even EDF will eventually miss a deadline. That's expected, correct behaviour, not a bug.</p>
</div>
</section>
<section id="ch7">
<h2><span class="chapter-number">7</span><span data-i18n="h2_ch7">Priority Queue / Min-Heap in C</span></h2>
<p data-i18n="ch7_0">C89 has no standard priority queue. You must implement a <strong>binary min-heap</strong> yourself. A heap is a complete binary tree where each parent is smaller than its children.</p>
<h3 data-i18n="ch7_1">Why a Plain Array, Not a Pointer-Based Tree?</h3>
<p data-i18n="ch7_2">It looks strange the first time: a "binary tree" implemented as a flat <code>t_heap_node *nodes</code> array with no <code>left</code>/<code>right</code> pointers anywhere. This works because a <strong>complete</strong> binary tree (every level full except possibly the last, filled left-to-right — which a heap always is by construction) has a predictable, gap-free shape, so its nodes can be numbered 0, 1, 2, 3... in level order and stored at exactly those array indices. The formulas <code>parent = (i-1)/2</code>, <code>left = 2*i+1</code>, <code>right = 2*i+2</code> then compute tree relationships from pure arithmetic instead of following pointers. The payoff: no <code>malloc</code> per node, better CPU cache locality (neighbouring array elements are physically close in memory, unlike scattered heap-allocated nodes), and dramatically simpler code — at the cost of needing to know (or grow) a capacity up front.</p>
<h3 data-i18n="ch7_3">Trace: Inserting Deadlines 50, 20, 80, 10</h3>
<p data-i18n="ch7_4">Walking through <code>heap_insert</code> by hand builds real intuition. Starting from an empty heap, insert coders with deadlines 50, then 20, then 80, then 10 (lower deadline = higher priority = should end up closer to the root):</p>
<ul data-i18n="ch7_5">
<li>Insert 50 → array <code>[50]</code>. Only element, nothing to compare — it's the root.</li>
<li>Insert 20 → appended at index 1: <code>[50, 20]</code>. Its parent is index 0 (value 50). Since 20 < 50, swap → <code>[20, 50]</code>.</li>
<li>Insert 80 → appended at index 2: <code>[20, 50, 80]</code>. Its parent is index 0 (value 20). Since 80 > 20, no swap needed — heap property already holds.</li>
<li>Insert 10 → appended at index 3: <code>[20, 50, 80, 10]</code>. Its parent is index 1 (value 50, computed as <code>(3-1)/2 = 1</code>). Since 10 < 50, swap → <code>[20, 10, 80, 50]</code>. Continue up: 10's new parent is index 0 (value 20, <code>(1-1)/2 = 0</code>). Since 10 < 20, swap again → <code>[10, 20, 80, 50]</code>. Now at the root, stop.</li>
</ul>
<p data-i18n="ch7_6">Final array <code>[10, 20, 80, 50]</code> — extracting the min always returns 10 first, exactly the coder closest to burnout, in O(log n) work per operation rather than an O(n) linear scan through every waiting coder.</p>
<h3 data-i18n="ch7_7">Heap Structure</h3>
<pre data-lang="c"><code><span class="keyword">typedef struct</span> <span class="type">s_heap_node</span>
{
<span class="type">int</span> coder_id;
<span class="type">long</span> <span class="type">long</span> deadline; <span class="comment">/* priority: lower = higher */</span>
} <span class="type">t_heap_node</span>;
<span class="keyword">typedef struct</span>
{
<span class="type">t_heap_node</span> *nodes;
<span class="type">int</span> size;
<span class="type">int</span> capacity;
} <span class="type">t_heap</span>;
<span class="type">t_heap</span> *<span class="function">heap_create</span>(<span class="type">int</span> capacity)
{
<span class="type">t_heap</span> *h = <span class="function">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">t_heap</span>));
h->nodes = <span class="function">malloc</span>(<span class="keyword">sizeof</span>(<span class="type">t_heap_node</span>) * capacity);
h->size = <span class="number">0</span>;
h->capacity = capacity;
<span class="keyword">return</span> h;
}</code></pre>
<h3 data-i18n="ch7_8">Heapify Up (Insertion)</h3>
<p data-i18n="ch7_9">After adding a node at the end, swap it with its parent until the heap property is restored:</p>
<pre data-lang="c"><code><span class="type">void</span> <span class="function">heap_insert</span>(<span class="type">t_heap</span> *h, <span class="type">int</span> coder_id, <span class="type">long</span> <span class="type">long</span> deadline)
{
<span class="type">int</span> i;
<span class="type">t_heap_node</span> tmp;
<span class="comment">/* Add at the end */</span>
i = h->size;
h->nodes[i].coder_id = coder_id;
h->nodes[i].deadline = deadline;
h->size++;
<span class="comment">/* Heapify up: swap with parent while smaller */</span>
<span class="keyword">while</span> (i > <span class="number">0</span>)
{
<span class="type">int</span> parent = (i - <span class="number">1</span>) / <span class="number">2</span>;
<span class="keyword">if</span> (h->nodes[parent].deadline <= h->nodes[i].deadline)
<span class="keyword">break</span>;
<span class="comment">/* Swap */</span>
tmp = h->nodes[parent];
h->nodes[parent] = h->nodes[i];
h->nodes[i] = tmp;
i = parent;
}
}</code></pre>
<h3 data-i18n="ch7_10">Heapify Down (Extraction)</h3>
<p data-i18n="ch7_11">Replace the root with the last element, then swap with the smaller child until the heap property is restored:</p>
<pre data-lang="c"><code><span class="type">t_heap_node</span> <span class="function">heap_extract_min</span>(<span class="type">t_heap</span> *h)
{
<span class="type">t_heap_node</span> min;
<span class="type">t_heap_node</span> tmp;
<span class="type">int</span> i;
<span class="type">int</span> left;
<span class="type">int</span> right;
<span class="type">int</span> smallest;
min = h->nodes[<span class="number">0</span>];
h->size--;
h->nodes[<span class="number">0</span>] = h->nodes[h->size];
<span class="comment">/* Heapify down */</span>
i = <span class="number">0</span>;
<span class="keyword">while</span> (<span class="number">1</span>)
{
left = <span class="number">2</span> * i + <span class="number">1</span>;
right = <span class="number">2</span> * i + <span class="number">2</span>;
smallest = i;
<span class="keyword">if</span> (left < h->size && h->nodes[left].deadline < h->nodes[smallest].deadline)
smallest = left;
<span class="keyword">if</span> (right < h->size && h->nodes[right].deadline < h->nodes[smallest].deadline)
smallest = right;
<span class="keyword">if</span> (smallest == i)
<span class="keyword">break</span>;
tmp = h->nodes[i];
h->nodes[i] = h->nodes[smallest];
h->nodes[smallest] = tmp;
i = smallest;
}
<span class="keyword">return</span> min;
}</code></pre>
<h3 data-i18n="ch7_12">Complexity</h3>
<table data-i18n="ch7_13">
<tr><th>Operation</th><th>Time</th><th>Description</th></tr>
<tr><td>Insert</td><td>O(log n)</td><td>Heapify up from leaf to root</td></tr>
<tr><td>Extract Min</td><td>O(log n)</td><td>Heapify down from root to leaf</td></tr>
<tr><td>Peek Min</td><td>O(1)</td><td>Root is always the minimum</td></tr>
</table>
<div class="info-box tip">
<div class="info-box-title" data-i18n="ch7_14">Heap Index Formulas</div>
<p data-i18n="ch7_15">For a 0-indexed array:</p>
<ul data-i18n="ch7_16">
<li>Parent of i: <code>(i - 1) / 2</code></li>
<li>Left child of i: <code>2 * i + 1</code></li>
<li>Right child of i: <code>2 * i + 2</code></li>
</ul>
</div>
</section>
<section id="ch8">
<h2><span class="chapter-number">8</span><span data-i18n="h2_ch8">The Monitor Pattern</span></h2>
<p data-i18n="ch8_0">A <strong>monitor</strong> is a design pattern where a dedicated thread continuously checks a condition and takes action when it becomes true. In Codexion, the monitor detects burnout.</p>
<h3 data-i18n="ch8_1">Polling vs Event-Driven — A Deliberate Trade-off</h3>
<p data-i18n="ch8_2">Notice that the monitor thread below uses <code>usleep(1000)</code> in a loop — this is <strong>polling</strong>: repeatedly waking up and checking "has anything changed?" rather than being passively woken up only when something actually happens (which would be the <strong>event-driven</strong> style you saw with condition variables in Chapter 3). This might look like a step backwards after just learning to avoid busy-waiting, but it's the right tool here for a specific reason: burnout is defined by the <em>absence</em> of an event within a time window ("nobody compiled for this coder in the last <code>time_to_burnout</code> ms"), not by the occurrence of one. A condition variable can be signalled when something happens, but there's no clean way to signal "nothing happened for a while" — so the monitor has no choice but to periodically wake up and check the clock itself. The 1ms polling interval is a deliberate compromise: frequent enough to catch a burnout within the guide's 10ms precision requirement, infrequent enough to barely register on CPU usage (sleeping 999 microseconds out of every 1000).</p>
<img alt="Monitor Thread" class="concept-img" src="images/monitor_thread.png"/>
<p class="img-caption" data-i18n="ch8_3">Figure 6: The monitor thread continuously checks all coders' deadlines against the current time.</p>
<h3 data-i18n="ch8_4">Monitor Implementation</h3>
<pre data-lang="c"><code><span class="keyword">typedef struct</span> <span class="type">s_coder</span>
{
<span class="type">int</span> id;
<span class="type">long</span> <span class="type">long</span> last_compile_start;
<span class="type">int</span> compile_count;
<span class="type">int</span> burned_out;
} <span class="type">t_coder</span>;
<span class="keyword">typedef struct</span> <span class="type">s_sim</span>
{
<span class="type">t_coder</span> *coders;
<span class="type">int</span> num_coders;
<span class="type">long</span> <span class="type">long</span> time_to_burnout;
<span class="type">int</span> sim_over;
<span class="type">pthread_mutex_t</span> sim_lock;
} <span class="type">t_sim</span>;
<span class="type">void</span> *<span class="function">monitor_thread</span>(<span class="type">void</span> *arg)
{
<span class="type">t_sim</span> *sim = (<span class="type">t_sim</span> *)arg;
<span class="type">int</span> i;
<span class="type">long</span> <span class="type">long</span> now;
<span class="type">long</span> <span class="type">long</span> elapsed;
<span class="keyword">while</span> (<span class="number">1</span>)
{
<span class="function">usleep</span>(<span class="number">1000</span>); <span class="comment">/* Check every 1ms */</span>
<span class="function">pthread_mutex_lock</span>(&sim->sim_lock);
<span class="keyword">if</span> (sim->sim_over)
{
<span class="function">pthread_mutex_unlock</span>(&sim->sim_lock);
<span class="keyword">break</span>;
}
now = <span class="function">get_time_ms</span>();
<span class="keyword">for</span> (i = <span class="number">0</span>; i < sim->num_coders; i++)
{
<span class="keyword">if</span> (sim->coders[i].burned_out)
<span class="keyword">continue</span>;
elapsed = now - sim->coders[i].last_compile_start;
<span class="keyword">if</span> (elapsed > sim->time_to_burnout)
{
sim->coders[i].burned_out = <span class="number">1</span>;
sim->sim_over = <span class="number">1</span>;
<span class="function">printf</span>(<span class="string">"%lld %d burned out\n"</span>, now, sim->coders[i].id);
<span class="keyword">break</span>;
}
}
<span class="function">pthread_mutex_unlock</span>(&sim->sim_lock);
}
<span class="keyword">return</span> <span class="macro">NULL</span>;
}</code></pre>
<div class="info-box warning">
<div class="info-box-title" data-i18n="ch8_5">Precision Requirement</div>
<p data-i18n="ch8_6">The burnout log must be printed within 10ms of the actual burnout time. The monitor checks frequently (every 1ms) and uses the same mutex-protected state as the coders to ensure consistency.</p>
</div>
</section>
<section id="ch9">
<h2><span class="chapter-number">9</span><span data-i18n="h2_ch9">Codexion Architecture</span></h2>
<p data-i18n="ch9_0">Now we combine everything into the Codexion simulation. Here is the high-level architecture:</p>
<img alt="Coder Activity Cycle" class="concept-img" src="images/coder_cycle.png"/>
<p class="img-caption" data-i18n="ch9_1">Figure 7: Each coder cycles through Compile -> Debug -> Refactor repeatedly.</p>
<h3 data-i18n="ch9_2">Component Overview</h3>
<ol data-i18n="ch9_3">
<li><strong>Coder Threads</strong> — One per coder. Each loops: request dongles -> compile -> debug -> refactor.</li>
<li><strong>Dongles</strong> — N mutex-protected resources. Each has a state (available/held/cooldown) and a cooldown timer.</li>
<li><strong>Scheduler</strong> — Centralized queue (FIFO or EDF heap) that grants dongle access to waiting coders.</li>
<li><strong>Monitor Thread</strong> — Detects burnout by checking if any coder exceeded <code>time_to_burnout</code> since last compile.</li>
<li><strong>Logger</strong> — Mutex-protected output to prevent interleaved messages.</li>
</ol>
<h3 data-i18n="ch9_4">State Machine per Coder</h3>
<pre data-lang="c"><code><span class="keyword">typedef enum</span> <span class="type">e_state</span>
{
THINKING, <span class="comment">/* Waiting for dongles */</span>
COMPILING, <span class="comment">/* Has both dongles */</span>
DEBUGGING, <span class="comment">/* Just released dongles */</span>
REFACTORING <span class="comment">/* After debugging */</span>
} <span class="type">t_state</span>;
<span class="keyword">typedef struct</span> <span class="type">s_coder</span>
{
<span class="type">int</span> id;
<span class="type">pthread_t</span> thread;
<span class="type">t_state</span> state;
<span class="type">long</span> <span class="type">long</span> last_compile_start;
<span class="type">int</span> compile_count;
<span class="type">int</span> burned_out;
} <span class="type">t_coder</span>;</code></pre>
<h3 data-i18n="ch9_5">Dongle Structure</h3>
<pre data-lang="c"><code><span class="keyword">typedef struct</span> <span class="type">s_dongle</span>
{
<span class="type">pthread_mutex_t</span> mutex;
<span class="type">int</span> holder_id; <span class="comment">/* -1 if available */</span>
<span class="type">long</span> <span class="type">long</span> released_at; <span class="comment">/* timestamp for cooldown */</span>
<span class="type">int</span> in_cooldown;
} <span class="type">t_dongle</span>;</code></pre>
<h3 data-i18n="ch9_6">Simulation Parameters</h3>
<table data-i18n="ch9_7">
<tr><th>Parameter</th><th>Description</th></tr>
<tr><td><code>number_of_coders</code></td><td>How many coders (threads) in the simulation</td></tr>
<tr><td><code>time_to_burnout</code></td><td>Max ms between compiles before burnout</td></tr>
<tr><td><code>time_to_compile</code></td><td>Duration of compiling in ms</td></tr>
<tr><td><code>time_to_debug</code></td><td>Duration of debugging in ms</td></tr>
<tr><td><code>time_to_refactor</code></td><td>Duration of refactoring in ms</td></tr>
<tr><td><code>number_of_compiles_required</code></td><td>Stop when all coders compiled this many times</td></tr>
<tr><td><code>dongle_cooldown</code></td><td>Ms before a released dongle can be taken again</td></tr>
<tr><td><code>scheduler</code></td><td><code>"fifo"</code> or <code>"edf"</code></td></tr>
</table>
</section>
<section id="ch10">
<h2><span class="chapter-number">10</span><span data-i18n="h2_ch10">Data Structures</span></h2>
<h3 data-i18n="ch10_0">Complete Simulation Structure</h3>
<pre data-lang="c"><code><span class="macro">#define</span> <span class="macro">MAX_CODERS</span> <span class="number">200</span>
<span class="keyword">typedef struct</span> <span class="type">s_sim</span>
{
<span class="comment">/* Config */</span>
<span class="type">int</span> num_coders;
<span class="type">long</span> <span class="type">long</span> time_to_burnout;
<span class="type">long</span> <span class="type">long</span> time_to_compile;
<span class="type">long</span> <span class="type">long</span> time_to_debug;
<span class="type">long</span> <span class="type">long</span> time_to_refactor;
<span class="type">int</span> compiles_required;
<span class="type">long</span> <span class="type">long</span> dongle_cooldown;
<span class="type">int</span> use_edf; <span class="comment">/* 0 = fifo, 1 = edf */</span>
<span class="comment">/* State */</span>
<span class="type">t_coder</span> coders[<span class="macro">MAX_CODERS</span>];
<span class="type">t_dongle</span> dongles[<span class="macro">MAX_CODERS</span>];
<span class="type">int</span> sim_over;
<span class="type">int</span> all_done;
<span class="comment">/* Scheduler */</span>
<span class="type">pthread_mutex_t</span> sched_lock;
<span class="type">pthread_cond_t</span> sched_cond;
<span class="type">t_heap</span> *edf_queue; <span class="comment">/* NULL if FIFO */</span>
<span class="type">t_fifo_queue</span> fifo_queue;