245 lines
9.3 KiB
TeX
245 lines
9.3 KiB
TeX
\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}
|
|
\usepackage{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}{07}
|
|
\newcommand{\CardCount}{16}
|
|
\newcommand{\CardSlug}{task-wrapper}
|
|
\newcommand{\CardVersion}{v00.01}
|
|
\newcommand{\DocumentUUID}{9bffead0-4341-4350-806d-e975b1a3a08f}
|
|
\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{K07 · FreeRTOS C++ · Task}}\rhead{\small L06 · start i trampoline}
|
|
\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 \texttt{TaskHandle\_t}, jawny \texttt{start()} i trampoline}\par
|
|
\vspace{.25em}{\large jeden stabilny obiekt przez granicę C $\rightarrow$ C++}\par
|
|
\end{center}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1.65cm}Xp{1.55cm}X@{}}
|
|
\toprule
|
|
Karta & K07 / \CardCount & Czas & 30 minut \\
|
|
Platforma & Hazard3 / RV32I & Język & freestanding C++17 \\
|
|
Storage & dynamiczny TCB + stack & Dispatch & static CRTP trampoline \\
|
|
Wersja & \CardVersion & UUID karty & \texttt{9bffead0-...} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Trzy adresy, trzy role}
|
|
\begin{center}
|
|
\texttt{C++ object} $\ne$ \texttt{TaskHandle\_t/TCB} $\ne$
|
|
\texttt{task stack storage}
|
|
\end{center}
|
|
|
|
Konstruktor zapisuje tylko konfigurację. Dopiero \texttt{start()} woła
|
|
\texttt{xTaskCreate}, a kernel zapamiętuje adres kompletnego obiektu jako
|
|
\texttt{void*}. Statyczny trampoline odzyskuje typ i uruchamia prywatne
|
|
\texttt{run()} bez vtable.
|
|
|
|
\noindent\fcolorbox{accent}{accentlight}{\begin{minipage}{.94\textwidth}
|
|
\textbf{Inwariant K07.} Adres obiektu od startu do końca taska jest stały.
|
|
Wrapper nie jest kopiowalny ani przenoszalny. Handle identyfikuje TCB, a nie
|
|
obiekt aplikacyjny. Destruktor nie wykonuje wymuszonego delete taska.
|
|
\end{minipage}}
|
|
|
|
\section*{Plan 30 minut}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1.35cm}p{3cm}X@{}}
|
|
\toprule
|
|
Czas & Tryb & Dowód \\
|
|
\midrule
|
|
0--5 & adresy & object, TCB/handle i stack to różne rzeczy \\
|
|
5--10 & explicit start & konstruktor heap delta 0; start tworzy kernel storage \\
|
|
10--16 & C API & sześć argumentów xTaskCreate \\
|
|
16--21 & trampoline & raw context = object = this in run \\
|
|
21--26 & lifecycle & ready, running, completed, null handle, self-delete \\
|
|
26--30 & GDB/exit & local na task stack; bez move/vtable/destructor-delete \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Predykcja}
|
|
Który adres znajdzie się w \texttt{TaskHandle\_t}: obiekt czy TCB?
|
|
\blank{4cm}\quad Czy konstruktor może uruchomić task? \blank{4cm}
|
|
|
|
\newpage
|
|
\section{Dlaczego start nie należy do konstruktora}
|
|
|
|
W czasie konstruktora bazowego część derived nie jest jeszcze kompletna.
|
|
Scheduler mógłby wywołać \texttt{run()} natychmiast po create. Dlatego:
|
|
|
|
\begin{lstlisting}
|
|
CounterTask worker; // tylko konfiguracja, heap bez zmian
|
|
bool ok = worker.start(); // dopiero teraz xTaskCreate
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{4.4cm}X@{}}
|
|
\toprule
|
|
Stan wrappera & Znaczenie \\
|
|
\midrule
|
|
\texttt{constructed} & kompletny obiekt, brak taska kernela \\
|
|
\texttt{starting} & wejście do jawnej operacji start \\
|
|
\texttt{ready} & context i konfiguracja przekazywane do kernela \\
|
|
\texttt{running} & trampoline odzyskał typed object \\
|
|
\texttt{completed} & member body wrócił, handle wyzerowany \\
|
|
\texttt{start\_failed} & create nie utworzył taska; handle null \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
Drugie \texttt{start()} musi zwrócić \blank{2cm}, wykonać
|
|
\blank{2cm} nowych \texttt{xTaskCreate} i pozostawić handle
|
|
\blank{5cm}.
|
|
|
|
\section{Rozpisz sześć argumentów \texttt{xTaskCreate}}
|
|
|
|
\begin{lstlisting}
|
|
xTaskCreate(&Task::trampoline,
|
|
name_, stack_words_,
|
|
static_cast<Derived*>(this),
|
|
priority_, &handle_);
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{5.5cm}X@{}}
|
|
\toprule
|
|
Argument & Co niesie / jednostka \\
|
|
\midrule
|
|
\texttt{\&Task::trampoline} & \blank{10cm} \\
|
|
\texttt{name\_} & \blank{10cm} \\
|
|
\texttt{stack\_words\_} & \blank{10cm} \\
|
|
typed context: \texttt{Derived*} z \texttt{this} & \blank{9cm} \\
|
|
\texttt{priority\_} & \blank{10cm} \\
|
|
\texttt{\&handle\_} & \blank{10cm} \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\textbf{Pułapka jednostki:} stack depth jest liczbą \texttt{StackType\_t}, nie
|
|
domyślnie bajtów. Dla RV32I 256 słów to \blank{2cm} B.
|
|
|
|
\newpage
|
|
\section{Trampoline odzyskuje typ, nie tworzy obiektu}
|
|
|
|
\begin{lstlisting}
|
|
static void trampoline(void* context) {
|
|
Derived* derived = static_cast<Derived*>(context);
|
|
Task& base = *static_cast<Task*>(derived);
|
|
base.state_ = TaskState::running;
|
|
derived->run();
|
|
base.state_ = TaskState::completed;
|
|
base.handle_ = nullptr;
|
|
vTaskDelete(nullptr);
|
|
}
|
|
\end{lstlisting}
|
|
|
|
Nie ma \texttt{new}, lookupu ani vtable. Context jest adresem istniejącego,
|
|
kompletnego obiektu. Prywatny \texttt{run()} jest dostępny dzięki jawnej relacji
|
|
\texttt{friend} z konkretną instancją CRTP.
|
|
|
|
\section*{Trzy domeny adresów}
|
|
\noindent\begin{tabularx}{\textwidth}{@{}XXX@{}}
|
|
\toprule
|
|
Tożsamość obiektu C++ & Tożsamość TCB & Task stack \\
|
|
\midrule
|
|
before start: \blank{3.1cm} & handle after start: \blank{3cm} & low: \blank{3cm} \\
|
|
after start: \blank{3.1cm} & current handle: \blank{3cm} & high: \blank{3cm} \\
|
|
trampoline context: \blank{3.1cm} & status TCB: \blank{3cm} & local: \blank{3cm} \\
|
|
\texttt{this} in run: \blank{3.1cm} & & watermark: \blank{3cm} \\
|
|
completed object: \blank{3.1cm} & & \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\begin{center}
|
|
\texttt{object identities equal}\qquad
|
|
\texttt{handle == current == TCB}\qquad
|
|
\texttt{low <= local <= high}
|
|
\end{center}
|
|
|
|
\section*{Maszyna stanów wrappera}
|
|
\begin{center}
|
|
\texttt{constructed -> starting -> ready -> running -> completed}\\[.5em]
|
|
\texttt{constructed -> starting -> ready -> start\_failed} (gdy create fail)
|
|
\end{center}
|
|
|
|
Dlaczego stan \texttt{ready} jest ustawiany przed wejściem do
|
|
\texttt{xTaskCreate}? Przy aktywnym schedulerze nowy task może wykonać się, a
|
|
nawet zakończyć, zanim wywołanie create zwróci do startera. Kod po powrocie nie
|
|
może nadpisać \texttt{completed} ponownie stanem \texttt{ready}.
|
|
|
|
\section*{Predykcja duplikatu}
|
|
Drugie \texttt{start()} na tym samym obiekcie powinno:
|
|
\begin{itemize}
|
|
\item zwrócić \blank{2cm};
|
|
\item wywołać \texttt{xTaskCreate} jeszcze raz? \blank{2cm};
|
|
\item zmienić handle? \blank{2cm};
|
|
\item pozostawić stan: \blank{3cm}.
|
|
\end{itemize}
|
|
|
|
\section*{Granica destruktora}
|
|
Destruktor obiektu na innym stosie nie może bezpiecznie „sprzątnąć” dowolnego
|
|
żywego taska: wymuszone \texttt{vTaskDelete(handle)} nie odwija jego ramek C++.
|
|
K07 kończy body normalnie i dopiero trampoline usuwa bieżący task.
|
|
|
|
\newpage
|
|
\section{Hazard3/GDB i kontrakt zaliczenia}
|
|
\begin{lstlisting}[language=bash]
|
|
make check
|
|
riscv64-unknown-elf-gdb build/task01_task_wrapper/prog.elf
|
|
b explicit_task_debug_checkpoint
|
|
\end{lstlisting}
|
|
\begin{lstlisting}
|
|
p g_checkpoint_trace
|
|
p/x g_object_before_start
|
|
p/x g_trampoline_context
|
|
p/x g_run_this
|
|
p/x g_handle_after_start
|
|
p/x g_tcb_address
|
|
p/x g_stack_low
|
|
p/x g_stack_high
|
|
p/x g_stack_local
|
|
p g_counter_result
|
|
p g_task_wrapper_pass
|
|
\end{lstlisting}
|
|
|
|
\noindent\begin{tabularx}{\textwidth}{@{}p{1cm}p{3.8cm}X@{}}
|
|
\toprule
|
|
STOP & Stan & Obowiązkowy dowód \\
|
|
\midrule
|
|
1 & constructed & heap konstrukcji bez zmian, handle null \\
|
|
2--3 & start & starting/ready, context wskazuje pełny obiekt \\
|
|
4 & create returned & handle wskazuje TCB w ucHeap \\
|
|
5 & before scheduler & object address nadal ten sam \\
|
|
6--7 & trampoline & context odzyskany, state running \\
|
|
8 & private run & handle/current/TCB równe; local w stack; wynik 500500 \\
|
|
10 & completion & state completed; handle null; self-delete \\
|
|
11--12 & verifier & wszystkie relacje i pass=1 \\
|
|
\bottomrule
|
|
\end{tabularx}
|
|
|
|
\section*{Zaliczenie}
|
|
\begin{itemize}
|
|
\item $\square$ odróżniam object, handle/TCB i task stack;
|
|
\item $\square$ pokazuję zerowy heap delta konstruktora i jawny create;
|
|
\item $\square$ opisuję sześć argumentów \texttt{xTaskCreate};
|
|
\item $\square$ śledzę ten sam context przez trampoline do \texttt{run()};
|
|
\item $\square$ pokazuję local w stack bounds oraz wynik 500500;
|
|
\item $\square$ uzasadniam brak copy/move, vtable i delete w destruktorze.
|
|
\end{itemize}
|
|
|
|
\section*{Wyjście}
|
|
Dlaczego handle nie może być użyty jako adres obiektu aplikacyjnego?\\[.4em]
|
|
\blank{12cm}\\[1em]
|
|
Co chroni jawny \texttt{start()}?\\[.4em]\blank{11cm}
|
|
|
|
\vfill
|
|
\noindent\textbf{Następna karta K08:} dynamiczny i statyczny TCB/stack,
|
|
słowa kontra bajty, jawne zakończenie i czas odzyskania pamięci.
|
|
\end{document}
|