פתרון דירות תהליכונים וכניסה חוזרת¶
פתרון התרגיל הראשון¶
אפשר להשתמש בguard הבא במקום זוג ידני. זהו קטע להוספה לתוכנית ההרצאה, עם אותם headers. הוא אינו ניתן להעתקה או להזזה, וחייב להיווצר ולהיהרס על אותו thread. בדיקת הthread בזמן הריסה מגלה הפרת חוזה במקום לסגור את האתחול של thread אחר.
class ComScope {
DWORD owner_ = GetCurrentThreadId();
HRESULT status_;
public:
explicit ComScope(DWORD flags) : status_(CoInitializeEx(nullptr, flags)) {}
ComScope(const ComScope&) = delete;
ComScope& operator=(const ComScope&) = delete;
~ComScope() {
if (GetCurrentThreadId() != owner_) std::terminate();
if (SUCCEEDED(status_)) CoUninitialize();
}
HRESULT status() const { return status_; }
};
לשימוש בstd::terminate נוסיף גם #include <exception>. האובייקטים והproxyים שנוצרו בscope צריכים להשתחרר לפני הריסת הguard. סדר ההצהרות המקומיות יכול לעזור, אבל הוא אינו מחליף בדיקה של references שנשמרו במקום אחר.
פתרון התרגיל השני¶
אפשר לבחור מדיניות שמאפשרת קריאה חוזרת לקריאה בלבד, ודוחה mutation בזמן notification עם קוד busy. אפשר גם להוסיף בקשות חדשות לתור ולהריץ אותן אחרי סיום notification. בשני המקרים לא נחשוף מצב שבוצע בו רק החצי הראשון של ההעברה.
במימוש עם תור נגדיר capacity ונמנע מצב שבו כל notification מוסיף עוד עבודה ללא גבול. כל אירוע יקבל snapshot ומספר רציף של commit. הלקוח יוכל לראות שאירוע שני נוצר בעקבות הראשון, בלי שהמתודה הראשונה תשתמש בstate ישן אחרי callback.
בקשת סגירה תשנה state לclosing ותמנע קבלת mutations חדשות. הפעולה שכבר ביצעה commit תחזיר תוצאה המתארת את הcommit הזה. שחרור פיזי יקרה אחרי שהקריאה והreferences הפעילים הסתיימו. אם רוצים שהסגירה תבטל גם פעולות שלא ביצעו commit, צריך נקודת הכרעה מפורשת לפניו.
פתרון התרגיל השלישי¶
במעגל יש שלוש קשתות: הSTA ממתין לתשובת הMTA, הMTA ממתין לcallback, והcallback זקוק לlock או לthread שאינו מתקדם. תיקון אחד הוא לבנות snapshot תחת הנעילה, לשחרר אותה ולבצע את הקריאה כשהמצב יציב. נצטרך לבדוק מחדש version או generation אם התוצאה אמורה לעדכן מצב שיכול להשתנות בינתיים.
תיקון שני הוא להפוך את הפרוטוקול לאסינכרוני: בקשה נכנסת לתור, הSTA חוזר ללולאה, והשלמה מגיעה כהודעה נפרדת. נצטרך לנהל בעלות על הבקשה, ביטול וסגירה בין שני האירועים. אין stack מקומי שאפשר להניח שיישאר חי עד שההשלמה תגיע.
הוספת pumping יכולה להסיר את החסימה של הthread, אבל היא לא מתקנת אוטומטית את הlock המוחזק או מצב חלקי. זאת הסיבה שwait graph וstate diagram משלימים זה את זה.