שרשור מודלים

שיטות שמסומנות כoneway לא חוסמות. עבור שיטות שלא מסומנות כ oneway, קריאת ה-method של הלקוח תיחסם עד שהשרת או קריאה חוזרת (callback) סינכרונית (המוקדם מביניהם). הטמעות של שיטות שרת יכולות להפעיל קריאה חוזרת סינכרונית אחת לכל היותר; תוספת שיחות קריאה חוזרת נמחקות ומתועדות כשגיאות. אם שיטה אמורה מחזירה ערכים באמצעות קריאה חוזרת ולא קוראת את הקריאה החוזרת שלו, הפעולה מתועדת שגיאה ודווחה ללקוח כשגיאה בהעברה.

שרשורים במצב מעבר

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

שרשורים ברכיבי HAL מקושרים

כדי לשרת קריאות RPC נכנסות (כולל קריאות חוזרות אסינכרוניות מ-HALs אל משתמשי HAL) והתראות על מוות, מאגר שרשורים משויך לכל תהליך שמשתמש ב-HIDL. אם בתהליך יחיד מיושמים מספר ממשקי HIDL ו/או מטפלים בהתראות על מוות, מאגר השרשורים שלו משותף לכולם. מתי תהליך מקבל שיחת שיטה נכנסת מלקוח, הוא בוחר שרשור חינמי ממאגר ה-thread ומבצע את הקריאה בשרשור הזה. אם אין שרשור פנוי היא תיחסם עד שתהיה זמינה.

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

קריאות מקננות מרובות נשלחות באותו שרשור hwbinder. לדוגמה, אם תהליך (A) מבצע קריאה סנכרונית משרשור hwbinder לתהליך (B), ואז עיבוד (B) מבצע קריאה סינכרונית בחזרה לתהליך (A), הקריאה הופעלה ב-thread המקורי של hwbinder ב-(A) שנחסם במקור שיחה. האופטימיזציה הזו מאפשרת לנהל שרת אחד עם שרשורים, טיפול בשיחות מקוננות, אבל לא מדובר במקרים שבהם השיחות עוברות רצף נוסף של קריאות IPC. לדוגמה, אם תהליך (ב) גרם קריאה ל-binder/vndbinder שקיבלה קריאה לתהליך (C) ולאחר מכן מעבדת קריאות (C) בחזרה ל-(A), לא ניתן להגיש אותו בשרשור המקורי שב-(A).

מודל שרשור של שרת (threading)

מלבד מצב העברה, הטמעות של שרתי HIDL פעילים בתהליך שונה מהלקוח, וצריכים שרשור אחד או יותר שממתינים שיחות נכנסות דרך ה-method. השרשורים האלה הם מאגר ה-threads של השרת; השרת יכול להחליט כמה שרשורים הוא רוצה להפעיל במאגר השרשורים שלו, ויכול להשתמש גודל מאגר של שרשורים אחד כדי לבצע סריאליזציה לכל הקריאות בממשקים שלו. אם השרת יש יותר משרשור אחד במאגר השרשורים, והוא יכול לקבל בו-זמנית בכל אחד מהממשקים שלו (ב-C++ פירוש הדבר שהנתונים המשותפים חייבים להיות נעולות בזהירות).

קריאות חד-כיווניות לאותו ממשק עוברות סריאליות. אם לקוח קורא לממשק של method1 ו-method2 IFoo ו-method3 בממשק IBar, method1 ו-method2 תמיד עוברים סדרה, אבל method3 יכול לפעול במקביל ל-method1 וגם method2.

שרשור הפעלה יחיד של לקוח יכול לגרום לביצוע בו-זמנית שרת עם כמה תהליכונים במקביל, בשתי דרכים:

  • oneway שיחות לא נחסמות. אם שיחת oneway היא ולאחר מכן מתבצעת קריאה לפעולה שאינה oneway, השרת יכול לבצע השיחה עם oneway והשיחה מחוץ ל-oneway בו-זמנית.
  • שיטות שרת המחזירות נתונים עם קריאות חוזרות סינכרוניות יכולות לבטל את החסימה הלקוח, ברגע שהקריאה החוזרת מופעלת מהשרת.

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

ברגע שהשרת קורא לקריאה החוזרת שסופקה, התעבורה יכולה לבצע קריאה הטמעה של קריאה חוזרת אצל הלקוח וביטול החסימה של הלקוח. הלקוח ממשיך במקביל למה שיישום השרת עושה אחרי שהוא קורא קריאה חוזרת (callback) (שעשויה לכלול פיצ'רים רציפים). קוד בפונקציית השרת שהקריאה החוזרת (callback) לא חוסמת יותר את הלקוח (כל עוד השרת לשרשור יש מספיק שרשורים לטיפול בשיחות נכנסות), אבל יכול להיות שהוא יבצע בו-זמנית עם קריאות עתידיות מהלקוח (אלא אם ה-thread{/4} של השרת שרשור אחד בלבד).

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

הערה: מומלץ מאוד להפעיל פונקציות שרת יחזרו בדיוק אחרי שהם קראו את פונקציית הקריאה החוזרת.

לדוגמה (ב-C++ ):

Return<void> someMethod(someMethod_cb _cb) {
    // Do some processing, then call callback with return data
    hidl_vec<uint32_t> vec = ...
    _cb(vec);
    // At this point, the client's callback is called,
    // and the client resumes execution.
    ...
    return Void(); // is basically a no-op
};

מודל שרשור של לקוחות

מודל השרשורים אצל הלקוח שונה בין קריאות שלא חוסמות (פונקציות שמסומנות באמצעות מילת המפתח oneway) וחסימה שיחות (פונקציות שלא צוינה בהן מילת המפתח oneway).

חסימת שיחות

כדי לחסום שיחות, הלקוח חוסם את השיחות עד שמתקיים אחד מהתנאים הבאים:

  • מתרחשת שגיאה בהעברה; האובייקט Return מכיל שגיאה את המצב הזה שאפשר לאחזר באמצעות Return::isOk().
  • הטמעת השרת מפעילה את הקריאה החוזרת (אם הייתה כזו).
  • הטמעת השרת מחזירה ערך (אם לא היה פרמטר קריאה חוזרת).

במקרה של הצלחה, פונקציית הקריאה החוזרת שהלקוח מעביר כארגומנט היא השרת תמיד נקרא לפני שהפונקציה עצמה מחזירה. הקריאה החוזרת היא מופעלות באותו שרשור שבו מתבצעת הקריאה לפונקציה, כך שהמטמיעים חייב להיות זהירים לגבי נעילות במהלך הפעלות של פונקציות (ולהימנע מהן כשהדבר אפשרי). פונקציה ללא הצהרת generates או שמילת מפתח מסוג oneway עדיין חוסמת. הלקוח חוסם עד השרת מחזיר אובייקט Return<void>.

שיחות בכיוון אחד

כשפונקציה מסומנת oneway, הלקוח חוזר באופן מיידי ולא ממתין שהשרת ישלים את הפעלת הקריאה לפונקציה. ב כלומר, במצטבר, פירוש הדבר הוא שהקריאה לפונקציה לוקחת חצי מפני שהוא מריצים חצי מהקוד, אבל בזמן כתיבת הטמעות הם רגישים לביצועים, ויש לכך השלכות מסוימות על התזמון. בדרך כלל, שימוש בשיחה חד-כיוונית גורם לכך שהמתקשר ימשיך להיות מתוזמן שימוש בהפעלה סינכרונית רגילה גורם למתזמנים להעברה מיידית מהמתקשר לתהליך מקבל הקריאה החוזרת. זוהי אופטימיזציה של ביצועים ב-Binder. לשירותים שבהם צריך לבצע את הקריאה החד-כיוונית בתהליך היעד בעדיפות גבוהה, מדיניות התזמון של השירות המקבל יכולה להיות השתנה. ב-C++, באמצעות השיטה של libhidltransport setMinSchedulerPolicy במדיניות ובעדיפות של מתזמן המשימות שמוגדר ב-sched.h מבטיח שכל הקריאות לשירות יפעלו לפחות במדיניות התזמון ובעדיפות שנקבעו.