*[תהליכים](../perlfunc-by-category.md)* # alarm תזמון `SIGALRM` שיישלח לתהליך הנוכחי לאחר מספר שלם של שניות שעון־קיר. `alarm` דורך טיימר ספירה לאחור יחיד לכל תהליך. כאשר הטיימר פוקע, הקרנל שולח `SIGALRM`; מה שקורה אחר כך תלוי כולו במטפל האות המותקן ב־[`%SIG`](../perlvar.md). הטיימר רץ בזמן שעון־קיר, לא בזמן CPU, כך שתהליך ישן או חסום עדיין מקבל את האות בזמן (בכפוף ל־jitter של התזמון). ## תקציר ```perl alarm SECONDS alarm ``` ללא ארגומנט, נעשה שימוש בערך שב־[`$_`](../perlvar.md). ## מה מוחזר מספר השניות שנותרו על הטיימר ה**קודם**, או `0` אם לא היה טיימר ממתין. זה מאפשר לשמור ולשחזר את ה־alarm של הקורא לאורך פעולה מתוזמנת מקוננת: ```perl my $prev = alarm 10; # ... do something that must finish in 10s ... alarm $prev; # restore caller's timer ``` `alarm 0` מבטל כל טיימר ממתין ומחזיר את השניות שנותרו עליו. ## מצב גלובלי שהפונקציה נוגעת בו - [`%SIG`](../perlvar.md) — תא `ALRM` נושא את שם המטפל שמוזעק כאשר הטיימר יורה. ללא מטפל, ברירת המחדל של `SIGALRM` היא לסיים את התהליך. - [`$_`](../perlvar.md) — נקרא כאשר `alarm` נקרא ללא ארגומנט. - [`$@`](../perlvar.md) — נושא את הודעת ה־`die` מתוך הניב הקנוני `eval { alarm ...; ...; alarm 0 }`. - [`$!`](../perlvar.md) — קריאת מערכת חוסמת שנקטעה על ידי `SIGALRM` קובעת את `$!` ל־`EINTR`, **אך** Perl מפעיל מחדש קריאות מערכת רבות אוטומטית במערכות מסוימות, ולכן אי אפשר להסתמך על ראיית `EINTR`. יש להשתמש ב־[`eval`](eval.md) / [`die`](die.md) במקום זאת (ראו להלן). ## הניב הקנוני ל־timeout שימוש ב־`alarm` יחד עם [`eval`](eval.md) ו־[`die`](die.md) להטלת תקרת זמן שעון־קיר על כל בלוק קוד, לרבות I/O חוסם: ```perl eval { local $SIG{ALRM} = sub { die "timeout\n" }; # \n matters alarm $timeout; my $nread = sysread $sock, $buf, 4096; alarm 0; # cancel on success }; if ($@) { die $@ unless $@ eq "timeout\n"; # rethrow others # ... handle timeout ... } ``` שלושה פרטים נושאים משקל: - [`local`](local.md) מתחם את המטפל לבלוק ה־`eval` כך שלא ידלוף לקורא. - ה־`\n` ב־`"timeout\n"` מדכא את סיומת הקובץ/שורה ש־[`die`](die.md) היה מוסיף אחרת, ההופך את בדיקת ההתאמה המדויקת ב־`$@` לאמינה. - `alarm 0` **בתוך** ה־`eval` מבטל את הטיימר במסלול ההצלחה. בלעדיו, טיימר שנותר דרוך עלול לירות במהלך קוד לא־קשור לאחר שה־`eval` חוזר. ## דוגמאות תאריך יעד פשוט בשעון־קיר: ```perl $SIG{ALRM} = sub { die "timeout\n" }; eval { alarm 5; my $line = <$slow_pipe>; # may block indefinitely alarm 0; }; warn "gave up waiting\n" if $@ eq "timeout\n"; ``` טיימרים מקוננים — שמירה ושחזור: ```perl my $saved = alarm 30; # remember caller's timer do_something(); alarm $saved; # put it back ``` ביטול טיימר ממתין: ```perl my $left = alarm 0; # 0 if none was pending ``` טיק מחזורי ללא `Time::HiRes`: ```perl $SIG{ALRM} = sub { print "tick\n"; alarm 1; # re-arm from inside the handler }; alarm 1; sleep 10; # do not mix sleep+alarm in real code ``` השהיה תת־שנייתית — יש להשתמש ב־`Time::HiRes`: ```perl use Time::HiRes qw(alarm); alarm 0.25; # 250 ms ``` ## מקרי קצה - **שניות שלמות בלבד.** ה־`alarm` שבליבה קוטם ארגומנטים שבריריים לשניות שלמות. לרזולוציה דקה יותר, יש לטעון את `Time::HiRes`, אשר מייצא `alarm` חליפי המקבל מספרים בנקודה צפה. - **jitter של תזמון.** האות עשוי להגיע מוקדם או מאוחר עד כשנייה אחת בגלל אופן ספירת השניות בקרנל ותזמון התהליך. יש להתייחס ל־`alarm N` כאל גבול *לא־לפני־N־פחות־1*, לא כאל תאריך יעד מדויק. - **טיימר אחד לכל תהליך.** כל קריאת `alarm` מחליפה את הטיימר הקודם; אין תור. הערך המוחזר הוא הרישום היחיד של מה שנדרס. - **ברירת המחדל קטלנית.** ללא מטפל מותקן עבור `ALRM`, פקיעת הטיימר מסיימת את התהליך עם `Alarm clock`. יש להתקין מטפל *לפני* דריכת הטיימר. - **אין לערבב עם [`sleep`](sleep.md).** במערכות רבות [`sleep`](sleep.md) ממומש על גבי `alarm`, ולכן דריכת אחד מהם דורסת את השני. יש לבחור אחד לכל בלוק. - **`EINTR` אינו אמין.** Perl מתקין מטפלי אותות עם `SA_RESTART` בפלטפורמות רבות, ולכן `SIGALRM` הקוטע קריאת מערכת חוסמת מפעיל מחדש את הקריאה בשקיפות במקום לחזור כאשר [`$!`](../perlvar.md) מוגדר כ־`EINTR`. הדרך הניידת לאכוף תאריך יעד היא [`eval`](eval.md) / [`die`](die.md) מתוך המטפל, כפי שמודגם לעיל. - **אותות נשלחים בין אופים של Perl.** בתוך לולאה צפופה ב־Perl טהור המטפל רץ בנקודה הבטוחה הבאה, לא באופן מיידי; קריאת XS עתירת CPU יכולה לדחות את המסירה עוד יותר. - **[`fork`](fork.md) מנקה את הטיימר.** תהליכי בן מתחילים ללא alarm ממתין ללא קשר למה שההורה דרך. - **`exec` מנקה את הטיימר** ב־Linux; דמות התהליך החליפית מתחילה ללא `SIGALRM` ממתין. ## הבדלים מן ה-upstream תואם מלא ל־Perl 5.42 upstream. ## ראו גם - [`sleep`](sleep.md) — השהיית שעון־קיר; לעתים קרובות ממומש על גבי `alarm`, ולכן השניים אינם משתלבים - [`die`](die.md) — מעלה את החריגה ממטפל `ALRM` ש־[`eval`](eval.md) תופס כדי לממש timeout - [`eval`](eval.md) — תופס את ה־`die` מהמטפל ומאפשר לתוכנית להתאושש מ־timeout - [`%SIG`](../perlvar.md) — המקום שבו מטפל `ALRM` מותקן; המטפל מחליט מה הטיימר *משמעותו* - [`select`](select.md) — הצורה בעלת ארבעת הארגומנטים מקבלת timeout בנקודה צפה ומהווה חלופה ל־`alarm` עבור תאריכי יעד של I/O - `Time::HiRes` — חליף `alarm` ישיר המקבל מספרים בנקודה צפה תת־שנייתיים באמצעות `setitimer(2)`