Thread Sanitizer został dodany w pakiecie z Xcode 8. Jest to narzędzie, za pomocą którego możecie sprawdzić, czy poszczególne wątki nie walczą między sobą o pierwszeństwo dostępu do określonych danych w pamięci. Zjawisko takie określane jest mianem Data Race, a do jego wystąpienia konieczne jest spełnienie trzech warunków (posiłkowałem się wyjaśnieniem ze strony Oracle):

  1. Conajmniej dwa wątki w ramach jednego procesu próbują równocześnie uzyskać dostęp do tego samego adresu w pamięci.
  2. Przynajmniej jeden z wątków próbuje dokonać pod danym adresem zapisu.
  3. Wątki nie wykorzystują żadnych blokad, aby kontrolować dostęp do zasobów pamięci.

Spełnienie wszystkich warunków oznacza, że kolejność dostępu dla poszczególnych wątków nie jest określona, a wyniki obliczeń mogą różnić się przy każdym uruchomieniu programu. Takie zachowanie aplikacji może prowadzić do powstawania trudnych w identyfikacji bugów.

 

Thread Sanitizer w praktyce

 

Thread Sanitizer nie jest uruchamiany domyślnie i musicie najpierw edytować ustawienia Waszego projektu. Wejdźcie w zakładkę Product > Scheme > Edit scheme i zaznaczcie odpowiednią opcję, tak jak na poniższym obrazku:

 

 

Do przetestowania Thread Sanitizer w praktyce skorzystamy z prostego przykładu. Możecie utworzyć sobie do tego projekt w Xcode.

Utwórzcie w projekcie klasę Person, która będzie zawierała tylko jedno, z góry zdefiniowane pole. Dla ułatwienia możecie dodać ten kod na samym dole domyślnego pliku ViewController.swift:

W klasie ViewController dodajcie natomiast następujący kod:

Powyższy kod jest przyczyną powstania zjawiska Data Race, które opisałem na samym początku. Poszczególne bloki DispatchQueue.global.async {} uruchamiają zadany im kod asynchronicznie na wątku działającym w tle (background thread). Wykonanie asynchronicznie oznacza, że nie jesteśmy w stanie przewidzieć, która operacja zostanie zakończona jako pierwsza, a przez to nie wiemy jak będzie wyglądała wartość pola personAge w danym momencie. Oczywiście w normalnych zastosowaniach kod nie będzie tak banalny, ale wyobraźcie sobie sytuację, w której w poszczególnych blokach umieszczacie kod odpowiedzialny za współpracę z serwerem lub z bazą danych.

Sprawdźmy teraz jak na nasze działania zareaguje Thread Sanitizer (w skrócie TSan). Skompilujcie projekt i uruchomcie go na emulatorze (TSan nie działa na fizycznych urządzeniach). Waszym oczom powinien ukazać się widok podobny do poniższego obrazka:

 

 

Dodatkowo w konsoli Xcode zostanie wyświetlony szereg trudnych do odczytania informacji, z których na samy dole można wyłuskać poniższe linijki:

TSan nie zatrzyma wykonania aplikacji, a zamiast tego spróbuje wskazać nam miejsce wykrycia potencjalnych problemów. W naszym przypadku podświetlił on na fioletowo linię numer 15, natomiast w oknie Issue Navigator (okienko po lewej stronie) oraz konsoli umieścił szereg przydatnych informacji.

Za pomocą Issue Navigator możemy dowiedzieć się w jakiej klasie, pod jakim adresem oraz na jakich wątkach występują problemy. W konsoli możemy nawet sprawdzić jakie id mają poszczególne wątki oraz jakiego są one rodzaju. Większość z tych informacji nie będzie jednak w codziennej pracy zbyt przydatna, bo do naprawienia błędu powinno wystarczyć nam wskazanie miejsca jego występowania. Oczywiście dane generowane przez TSan nie zawsze będą tak czytelne i przejrzyste ☹️.

 

Słowo na drogę

 

Z TSan warto korzystać w codziennej pracy, aby już na wczesnym etapie wyłapywać potencjalne problemy. Wymaga on odrobiny przyzwyczajenia, ale zainwestowany czas zwróci się w dłuższej perspektywie. Kilka dodatkowych informacji możecie znaleźć w oficjalnej dokumentacji. Obok Thread Sanitizer Xcode został wyposażony również inne przydatne narzędzia, takie jak Address Sanitizer oraz Memory Grapher. W kolejny wpisach postaram się przybliżyć Wam każde z nich. Do następnego 🤓.


 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *