פתרון מנוע השלמות עם IOCP¶
ה־dispatcher צריך להפריד תחילה בין קיום חבילה לבין תוצאת הפעולה. הקטע הבא מחליף את ראש לולאת האיסוף בדוגמה, כאשר שאר המבנים נשארים כפי שהוגדרו:
DWORD bytes = 0;
ULONG_PTR received_key = 0;
OVERLAPPED* operation = nullptr;
const BOOL ok = GetQueuedCompletionStatus(
port.value, &bytes, &received_key, &operation, 50);
const DWORD error = ok ? ERROR_SUCCESS : GetLastError();
if (!operation) {
if (!ok && error == WAIT_TIMEOUT) {
++timeouts;
continue;
}
std::terminate();
}
המשתנה timeouts הוא מונה מקומי שהוגדר לפני הלולאה. אין להשתמש ב־bytes או ב־received_key כדי לזהות בקשה במסלול שאין בו operation. כאשר operation קיים, מחפשים את הבקשה, מאמתים שהיא פעילה ושומרים את error גם אם ערכו אינו אפס.
עצירה שמרוקנת את תור ההשלמות¶
בקשת עצירה משנה מצב של המנוע כך שלא יוגשו פעולות נוספות. לאחר מכן היא עוברת על הבקשות הפעילות וקוראת CancelIoEx עם כתובת המבנה של כל אחת. הצלחה או ERROR_NOT_FOUND אינם מורידים את מונה active. רק צריכת ההשלמה של פעולה שהתקבלה מורידה אותו.
כאשר active מגיע לאפס, אפשר להרוס את buffers הבקשות ולסגור את handle הקובץ וה־port. אם ה־driver אינו משלים ביטול בזמן סביר, אפשר לדווח שהמערכת עדיין מתנקזת. אין לשחרר את הזיכרון כדי ליצור מראית עין של סיום מוצלח.
מסלול השלמה מיידית¶
בתצורת ההרצאה כל בקשה שהתקבלה, כולל זו שהושלמה מיד, מטופלת דרך חבילת completion. לכן active גדל לפני ReadFile וקטן מיד רק בשגיאת הגשה שאינה ERROR_IO_PENDING. הקוד אינו קורא למסיים ידני כאשר ReadFile מחזירה true.
אם מפעילים FILE_SKIP_COMPLETION_PORT_ON_SUCCESS, הצלחה מיידית דורשת טיפול מקומי כי לא תגיע עבורה חבילה. צריך לעדכן את אותה מכונת מצבים, לשמור תוצאה ולהפחית active בדיוק פעם אחת. השינוי חייב להיות חלק מן החוזה של handle מסוים; אסור להחליט לפי ניחוש בכל בקשה.
בדיקות של ספירה¶
קובץ ריק יכול להפיק שגיאות סוף קובץ כבר בהגשה, ולכן ייתכן שהלולאה לא תאסוף אף חבילה. קובץ קצר יכול להשלים חלק מן הבקשות עם פחות בתים. הבדיקה צריכה לספור הגשות שהתקבלו, שגיאות הגשה והשלמות שנצרכו בנפרד.
ה־invariant המרכזי הוא שמספר ההגשות שהתקבלו פחות מספר ההשלמות שנצרכו שווה למספר הבקשות הפעילות. עבור כל מזהה לוגי צריך להיות מסלול סיום יחיד. סכום כולל נכון אינו מספיק אם בקשה אחת הסתיימה פעמיים ואחרת לא הסתיימה כלל.