Chromium Chronicle מס' 25: הערות בנושא בטיחות בשרשורים

פרק 25: מאת Victor Costan ב-SFO (אוקטובר 2021)
הפרקים הקודמים

ב-C++, שלילת האפשרות של מרוצי נתונים מסתמכת על הוכחה קטנה לתקינות השרשורים בכל גישה של חבר לנתונים. ההוכחות האלה עמוסות בעבודה נפשית, במיוחד כשבודקים את הקוד או מארגנים אותו מחדש. מסגרת הניתוח הסטטי של Clang גורמת לעומס של הוכחות להגנה על שרשורים.

הוספת GUARDED_BY_CONTEXT() לחברים בנתונים במחלקות שלא בטוחות לשרשורים

רוב המחלקות של Chrome לא בטוחות לשרשורים, וצריך להשתמש בהן ברצף יחיד. אפשר להוסיף הערות לכל החברים בנתונים שאינם בטוחים בשרשור. הערות מיותרות הן בטוחות, אבל אם חסרות הערות, הן עלולות לגרום למרוץ נתונים מסוים.

#include "base/sequence_checker.h"  // for SEQUENCE_CHECKER()
#include "base/thread_annotations.h"  // for GUARDED_BY_CONTEXT()

class Cache {
  // Methods here.
 private:
  SEQUENCE_CHECKER(sequence_checker_);
  base::flat_map<std::string, std::string> data_ GUARDED_BY_CONTEXT(sequence_checker_);
};

Clang אוכף בדיקות רצף

בתמורה להוספת הערות לחברים בנתונים, Clang מבטיח שכל שיטה ניגשת לנתונים מבצעת בדיקת אבטחה של רצף לפני שהיא עושה זאת. הקוד עובר בתהליכי ארגון מחדש (Refactoring) Clang ממשיך לאכוף את ההערה GUARDED_BY_CONTEXT().

void Cache::Set(base::StringPiece key, base::StringPiece value) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);  // Clang warns without this.
  data_.emplace(key, value);
}

הוספת GUARDED_BY() לחברים בנתונים בכיתות בטוחות לשרשורים שמשתמשים ב-mutexs

בכיתות מסוימות ב-Chrome חובה להשתמש במנעולים כדי לשמור על בטיחות השרשור. במקרים כאלה, כדאי להוסיף הערות לכל החברים בנתונים שאינם בטוחים בשרשור. כל הערה מפנה לסימן מניעה (mutex) שאותו צריך להחזיק במהלך הגישה לחבר הנתונים.

#include "base/thread_annotations.h"  // for GUARDED_BY()

class ThreadSafeCache {
  // Methods here.
  private:
    base::Lock lock_;
    base::flat_map<std::string, std::string> data_ GUARDED_BY(lock_);
};

Clang אוכף מנעולנים

צריך לחכות קצת ולתת למהדר לוודא שההיקף של כל base::AutoLock מוגדר כמו שצריך, וששיחות Acquire() ו-Release() נעולות מותאמות בצורה נכונה.

void ThreadSafeCache::Set(base::StringPiece key, base::StringPiece value) {
  base::AutoLock auto_lock(lock_);  // Clang warns without this.
  data_.emplace(key, value);
}