8.3 - יקיצה ומימוש executor בסיסי - פתרון
פתרון חיי היקיצה¶
פתרון ראשון - כמה הודעות עבור עבודה אחת¶
הטיפוס הבא מצטרף לקובץ המנוע מההרצאה. הוא מחזיק מונה אטומי רק כדי שהבדיקה הראשית תוכל לבדוק כמה פעמים נקרא.
use std::sync::atomic::{AtomicUsize, Ordering};
struct Noisy {
pending_returned: bool,
polls: Arc<AtomicUsize>,
}
impl Future for Noisy {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
self.polls.fetch_add(1, Ordering::Relaxed);
if self.pending_returned {
Poll::Ready(())
} else {
self.pending_returned = true;
for _ in 0..3 {
cx.waker().wake_by_ref();
}
Poll::Pending
}
}
}
fn main() {
let polls = Arc::new(AtomicUsize::new(0));
let mut executor = Executor::new();
executor.spawn(Noisy {
pending_returned: false,
polls: polls.clone(),
});
executor.spawn(async {
Delay::new(Duration::from_millis(20)).await;
});
executor.run();
assert_eq!(polls.load(Ordering::Relaxed), 2);
println!("two polls despite three wakes");
}
היקיצות הנוספות יכולות להישאר בתור גם אחרי ההשלמה. המשבצת הריקה מונעת בדיקה חוזרת של העתיד. בתוכנית הזאת המשימה הנוספת גם גורמת למנוע להמשיך לעבוד אחרי השלמת העתיד הרועש, כך שהבדיקה אינה מסתמכת רק על יציאה מיידית מהמנוע.
פתרון שני - סדר האירועים קובע¶
ברצף הראשון העובד נועל, מסמן השלמה ואינו מוצא יקיצה. לאחר מכן הבדיקה נועלת ורואה השלמה. ברצף השני הבדיקה נועלת, רואה שהפעולה טרם הושלמה ושומרת יקיצה; העובד נועל אחריה, מסמן השלמה ומוציא את היקיצה. בשני הרצפים אין צורך בבדיקה תקופתית.
ברצף השגוי הבדיקה קוראת תנאי שקרי ומשחררת את הנעילה. העובד מסמן השלמה ורואה שאין יקיצה. לאחר יציאת העובד הבדיקה רושמת יקיצה ומחזירה המתנה. הרגע שבו העובד יוצא בלי יקיצה הוא הרגע שבו אובדת הדרך המתוכננת להמשך. התיקון הוא פרוטוקול רישום ובדיקה אטומי מבחינת המתחרים, כפי שמומש במנעול המשותף, ולא רק שינוי סדר שתי הוראות שאינן מוגנות יחד.
פתרון שלישי - משימה ריקה עדיין יכולה להיות חיה¶
העתיד והמשימה הם שני אובייקטים שונים. אפשר להשמיד את העתיד אחרי השלמה, להשאיר משבצת ריקה, ולהחזיק את מעטפת המשימה כל עוד יש עותקי יקיצה. היקיצה המאוחרת יכולה להוסיף התראה ישנה לתור; היא אינה רשאית להפעיל שוב את העתיד שכבר הושמד. הוספת Drop ל־Task והדפסת אירוע ההשמדה תראה שהמעטפת מושמדת רק לאחר שחרור כל יחידות הבעלות.
החלפת התור בתור חסום עלולה ליצור קיפאון אם העתיד מבקש יקיצה מתוך הבדיקה שלו, והתור כבר מלא. התהליכון שמנסה להכניס לתור הוא גם התהליכון שאמור להוציא ממנו את הפריט הבא. תכנון תקין צריך לאחד יקיצות ולספק נתיב שאינו חוסם את המפעיל, או להשתמש במבנה תור ובפרוטוקול אחר. החלפת שם טיפוס הערוץ אינה פותרת את בעיית ההתקדמות.