\documentclass[10pt]{article} \usepackage[T1]{fontenc}\usepackage[utf8]{inputenc}\usepackage[polish]{babel} \usepackage[a4paper,margin=1.55cm]{geometry} \usepackage{array,tabularx,booktabs,amsmath,amssymb,xcolor,listings,hyperref,fancyhdr,lastpage,enumitem} \definecolor{accent}{HTML}{16324A}\definecolor{accentlight}{HTML}{EEF3F7}\definecolor{rulegray}{HTML}{D7DEE5} \hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue} \IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}} \newcommand{\PublisherDomain}{mpabi} \newcommand{\CardArea}{inf} \newcommand{\CardSeries}{freertos-cpp} \newcommand{\CardNumber}{09} \newcommand{\CardCount}{16} \newcommand{\CardSlug}{kernel-facade} \newcommand{\CardVersion}{v00.01} \newcommand{\DocumentUUID}{6a2ab068-17e7-4bfa-b726-2df37787ed69} \newcommand{\blank}[1]{\rule{#1}{.2pt}} \lstset{language=C++,basicstyle=\ttfamily\scriptsize,columns=fullflexible,keepspaces=true,frame=single,breaklines=true,showstringspaces=false,numbers=none,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray}} \pagestyle{fancy}\fancyhf{} \lhead{\textbf{K09 · FreeRTOS C++ · kernel facade}}\rhead{\small L08 · paired kernel state} \lfoot{\scriptsize commit \BuildCommit}\cfoot{\scriptsize \thepage/\pageref{LastPage}}\rfoot{\scriptsize V11.3.0 / \CardVersion} \setlength{\headheight}{14pt}\setlength{\footskip}{19pt} \setlist[itemize]{nosep,leftmargin=1.45em}\setlist[enumerate]{nosep,leftmargin=1.65em} \begin{document}\sloppy \begin{center} {\LARGE\bfseries Scheduler facade i scoped guards}\par \vspace{.25em}{\large statyczna usługa; RAII tylko dla prawdziwych par}\par \end{center} \noindent\begin{tabularx}{\textwidth}{@{}p{1.65cm}Xp{1.55cm}X@{}} \toprule Karta & K09 / \CardCount & Czas & 30 minut \\ Platforma & Hazard3 / RV32I & Język & freestanding C++17 \\ Dowód & nesting + \texttt{mstatus.MIE} & API & task context \\ Wersja & \CardVersion & UUID karty & \texttt{6a2ab068-...} \\ \bottomrule \end{tabularx} \section*{Najpierw klasyfikacja lifetime} \begin{center} \texttt{Scheduler::start/state} $\rightarrow$ usługa procesu, brak scoped lifetime\\[.35em] \texttt{enter/exit}, \texttt{suspend/resume} $\rightarrow$ pary, więc guard RAII \end{center} \noindent\fcolorbox{accent}{accentlight}{\begin{minipage}{.94\textwidth} \textbf{Kontrakt K09.} Destruktor guarda ma odtworzyć stan również przy każdym \texttt{return}. Nie wolno jednak blokować przy zawieszonym schedulerze, a \texttt{CriticalSection} nie jest mutexem ani API dla ISR. \end{minipage}} \section*{Plan 30 minut} \noindent\begin{tabularx}{\textwidth}{@{}p{1.35cm}p{3.15cm}X@{}} \toprule Czas & Tryb & Dowód \\ \midrule 0--5 & raw API & rozdziel usługi od par \\ 5--10 & critical & nesting oraz MIE przed/wewnątrz/po \\ 10--15 & guard & trzy wczesne wyjścia wracają do stanu bazowego \\ 15--20 & suspend & scheduler suspended, lecz MIE pozostaje 1 \\ 20--25 & nesting & inner exit nie wznawia jeszcze outer suspension \\ 25--30 & ABI & inline do raw API; brak vtable/runtime \\ \bottomrule \end{tabularx} \section*{Dwie różne blokady} \noindent\begin{tabularx}{\textwidth}{@{}p{4.1cm}p{3.3cm}p{3.2cm}X@{}} \toprule Operacja & Context switch & Przerwania & Para \\ \midrule \texttt{vTaskSuspendAll} & odroczony & działają & \texttt{xTaskResumeAll} \\ \texttt{taskENTER\_CRITICAL} & niemożliwy & MIE=0 & \texttt{taskEXIT\_CRITICAL} \\ \bottomrule \end{tabularx} Predykcja MIE podczas suspension: \blank{2cm}. Predykcja critical depth po dwóch enter i jednym exit: \blank{2cm}. \newpage \section{Raw critical: zmierz dokładną sekwencję} \begin{lstlisting} taskENTER_CRITICAL(); // depth + 1, MIE=0 taskENTER_CRITICAL(); // depth + 1, MIE=0 taskEXIT_CRITICAL(); // depth - 1, nadal MIE=0 taskEXIT_CRITICAL(); // depth == 0, MIE=1 \end{lstlisting} \noindent\begin{tabularx}{\textwidth}{@{}p{2.0cm}p{3.0cm}p{2.5cm}X@{}} \toprule Checkpoint & Operacja & Predykcja depth/MIE & Odczyt depth/MIE \\ \midrule C0 & przed & 0 / 1 & \blank{2.5cm} \\ C1 & enter & 1 / 0 & \blank{2.5cm} \\ C2 & drugi enter & 2 / 0 & \blank{2.5cm} \\ C3 & pierwszy exit & 1 / 0 & \blank{2.5cm} \\ C4 & drugi exit & 0 / 1 & \blank{2.5cm} \\ \bottomrule \end{tabularx} \section{RAII zamienia trzy krawędzie w jedną parę} \begin{lstlisting} uint32_t repair(uint32_t path) { freertos::CriticalSection guard; if (path == 0) return 0x10; if (path == 1) return 0x20; return 0x30; } // destructor runs on every return \end{lstlisting} \noindent\begin{tabularx}{\textwidth}{@{}p{1.5cm}p{2.6cm}p{2.7cm}p{2.5cm}X@{}} \toprule Path & Wynik & inside depth/MIE & after depth/MIE & PASS \\ \midrule 0 & \texttt{0x10} & \blank{2cm} & \blank{2cm} & $\square$ \\ 1 & \texttt{0x20} & \blank{2cm} & \blank{2cm} & $\square$ \\ 2 & \texttt{0x30} & \blank{2cm} & \blank{2cm} & $\square$ \\ \bottomrule \end{tabularx} \section*{Co dokładnie gwarantuje guard?} \begin{itemize} \item paruje wywołania w normalnym przepływie i przy wcześniejszym return; \item nie jest kopiowalny ani przenoszalny, więc nie powiela exit; \item zachowuje zagnieżdżenie: inner exit nie kończy outer critical section. \end{itemize} Czy RAII pozwala wywołać blokujące API wewnątrz critical section? \blank{2cm}. Uzasadnienie:\\[.5em]\blank{16cm} \newpage \section{SchedulerSuspendGuard: context switch bez maskowania IRQ} \begin{lstlisting} { freertos::SchedulerSuspendGuard guard; // scheduler state == suspended; mstatus.MIE == 1 // no delay, queue wait, semaphore wait or other blocking API } // xTaskResumeAll exactly once \end{lstlisting} \noindent\begin{tabularx}{\textwidth}{@{}p{2cm}p{3.3cm}p{3.1cm}X@{}} \toprule Path & Inside state/MIE & After state/MIE & Wynik \\ \midrule 0 & suspended / \blank{1cm} & running / \blank{1cm} & \texttt{0x40} \\ 1 & suspended / \blank{1cm} & running / \blank{1cm} & \texttt{0x50} \\ 2 & suspended / \blank{1cm} & running / \blank{1cm} & \texttt{0x60} \\ \bottomrule \end{tabularx} \section*{Zagnieżdżenie} \begin{center} \texttt{running}\\ $\downarrow$ outer ctor: \texttt{suspended}\\ $\downarrow$ inner ctor: \texttt{suspended}\\ $\downarrow$ inner dtor: \texttt{suspended}\\ $\downarrow$ outer dtor: \texttt{running} \end{center} Dlaczego inner destructor nie może jeszcze zwrócić stanu running?\\[.5em] \blank{16cm} \section{Statyczna fasada i bieżący task} \begin{lstlisting} freertos::Scheduler::start(); auto state = freertos::Scheduler::state(); auto handle = freertos::this_task::current(); auto tick = freertos::this_task::tick_count(); freertos::this_task::yield(); \end{lstlisting} \noindent\begin{tabularx}{\textwidth}{@{}p{5cm}p{4cm}X@{}} \toprule Relacja & Predykcja & Odczyt \\ \midrule state przed start & not started & \blank{2.5cm} \\ state w workerze & running & \blank{2.5cm} \\ this task handle & worker handle & \blank{2.5cm} \\ tick after yield & $\geq$ tick before & \blank{2.5cm} \\ \bottomrule \end{tabularx} \section*{Granice} Suspension nie wyłącza ISR; tylko odracza przełączenie taska. Critical section maskuje przerwania i musi być krótka. Żaden z guardów nie jest zamiennikiem mutexa chroniącego zasób przez długą operację. \newpage \section{Hazard3/GDB i ABI} \begin{lstlisting}[language=bash] make check riscv64-unknown-elf-gdb build/task01_kernel_facade/prog.elf b kernel_facade_debug_checkpoint \end{lstlisting} \begin{lstlisting} p g_raw_critical_depth p g_raw_critical_mie p g_guard_critical_after_depth p g_guard_critical_after_mie p g_raw_scheduler_state p g_guard_scheduler_after_state p g_nested_scheduler_state p g_this_task_handle p g_worker_handle p g_kernel_facade_pass \end{lstlisting} \section*{Co ma zostać w assembly?} \noindent\begin{tabularx}{\textwidth}{@{}p{5.3cm}p{3.3cm}X@{}} \toprule Dowód & Oczekiwane & Odczyt \\ \midrule disable IRQ & \texttt{csrci mstatus,8} & \blank{2.5cm} \\ enable IRQ & \texttt{csrsi mstatus,8} & \blank{2.5cm} \\ symbole wrappera & brak & \blank{2.5cm} \\ vtable/RTTI/exception runtime & brak & \blank{2.5cm} \\ \bottomrule \end{tabularx} \section*{Zaliczenie} \begin{itemize} \item $\square$ rozdzielam statyczną fasadę od scoped ownera; \item $\square$ odtwarzam critical depth 0--1--2--1--0 i MIE 1--0--0--0--1; \item $\square$ pokazuję restore po trzech wczesnych return; \item $\square$ pokazuję, że suspension zachowuje MIE=1; \item $\square$ wyjaśniam nesting outer/inner i zakaz blokowania; \item $\square$ dowodzę inline facade/guards oraz końcowy PASS. \end{itemize} \section*{Wyjście} Kiedy wybrać \texttt{CriticalSection}, a kiedy \texttt{SchedulerSuspendGuard}?\\[.5em] \blank{16cm}\\[1em] Dlaczego \texttt{Scheduler} nie jest obiektem RAII?\\[.5em] \blank{16cm} \vfill \noindent\textbf{Następna karta K10:} typed \texttt{Queue} i \texttt{StaticQueue} z jawnym kontraktem copyable message type. \end{document}