9.5 - סגירה ולחץ חוזר בחיבור רשת - פתרון
פתרון התקציב והסגירה¶
פתרון ראשון - תקציב נמדד בהתקדמות¶
הפונקציה הראשית הבאה משתמשת בטיפוסים מההרצאה. היא מכריחה קריאות קטנות כדי לבדוק את ההיסט ואת זמן שחרור התקציב.
fn main() -> io::Result<()> {
let mut outbox = Outbox::new(4);
outbox.enqueue(b"abcd".to_vec().into_boxed_slice()).unwrap();
let mut writer = ScriptedWriter {
steps: VecDeque::from([Some(1), Some(1), Some(1), Some(1)]),
output: Vec::new(),
};
outbox.flush(&mut writer, 3)?;
assert_eq!(&writer.output, b"abc");
assert_eq!((outbox.pending, outbox.reserved), (1, 4));
outbox.flush(&mut writer, 3)?;
assert_eq!(&writer.output, b"abcd");
assert_eq!((outbox.pending, outbox.reserved), (0, 0));
Ok(())
}
הסבב הראשון מסתיים בגלל התקציב, גם אם הכותב היה מוכן לקבל עוד. המנוע צריך לזכור שיש עבודה מקומית שנותרה ולתזמן סבב נוסף. אם משתמשים במוכנות מבוססת קצה, אסור לשכוח את העבודה ולחכות בהכרח לקצה חדש.
פתרון שני - כשל אינו הצלחה חלקית שקופה¶
אפשר לבדוק כתיבה אפס באמצעות צעד Some(0) בכותב המדומה. התוצאה צריכה להיות WriteZero, וההיסט אינו מתקדם. כדי לבדוק שגיאת חיבור נגדיר כותב שמקבל שני בתים ואז נכשל.
struct BreaksAfterTwo { written: bool }
impl Write for BreaksAfterTwo {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
if self.written { return Err(io::ErrorKind::BrokenPipe.into()); }
self.written = true;
Ok(bytes.len().min(2))
}
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
fn main() {
let mut outbox = Outbox::new(4);
outbox.enqueue(b"abcd".to_vec().into_boxed_slice()).unwrap();
let mut writer = BreaksAfterTwo { written: false };
assert_eq!(outbox.flush(&mut writer, 4).unwrap_err().kind(), io::ErrorKind::BrokenPipe);
assert_eq!((outbox.pending, outbox.reserved), (2, 4));
}
התור יודע ששני בתים התקבלו על ידי ממשק הכתיבה ושניים עדיין ממתינים. הוא אינו יודע אם היישום המרוחק קיבל או עיבד את השניים הראשונים. לכן אי אפשר להחזיר הצלחה מלאה או לבצע אוטומטית ניסיון חדש בלי חוזה שמטפל בכפילות ובתוצאה לא ידועה.
פתרון שלישי - סדר אחריות¶
המעבר למסיים עבודה מפסיק קבלת בקשות חדשות, אך שומר את הבקשות שכבר התקבלו ואת הידיות שלהן. לאחר סיום החישובים עוברים למרוקן תשובות וממשיכים לטפל במוכנות לכתיבה. רק אחרי שהתור ריק אפשר לסיים את כיוון הכתיבה לפי הפרוטוקול ולעבור לסגור, או לחכות לאישור נוסף אם הוא נדרש.
מועד הסיום הכולל נשמר לאורך כל המעברים ואינו מתחיל מחדש בכל מצב. אם הוא פג, מסמנים את הבקשות שנותרו לפי חוזה הכשל וסוגרים את המשאבים לאחר סיום הגישות הפעילות. סוף קלט מהלקוח אינו מחייב מחיקת התשובה שלו; הוא יכול להיות בדיוק האופן שבו הלקוח מודיע שסיים לשלוח בקשה.