- الرئيسية
- Documentation
- الأصلية
- تنفيذ مهام Rust الأصلية وإلغاؤها (`pi-natives`)
تنفيذ مهام Rust الأصلية وإلغاؤها (`pi-natives`)
يصف هذا المستند كيفية جدولة crates/pi-natives للعمل الأصلي، وكيفية تدفق الإلغاء من خيارات JS (timeoutMs، AbortSignal) إلى تنفيذ Rust.
ملفات التنفيذ
Section titled “ملفات التنفيذ”crates/pi-natives/src/task.rscrates/pi-natives/src/grep.rscrates/pi-natives/src/glob.rscrates/pi-natives/src/fd.rscrates/pi-natives/src/shell.rscrates/pi-natives/src/pty.rscrates/pi-natives/src/html.rscrates/pi-natives/src/image.rscrates/pi-natives/src/clipboard.rscrates/pi-natives/src/text.rscrates/pi-natives/src/ps.rs
العناصر الأولية الأساسية (task.rs)
Section titled “العناصر الأولية الأساسية (task.rs)”يعرّف task.rs ثلاثة عناصر أساسية:
-
task::blocking(tag, cancel_token, work)- يُغلّف
napi::AsyncTask/Task. - تعمل
compute()على خيوط عمل libuv (للعمليات المكثفة بحوسبيًا أو استدعاءات النظام المتزامنة/الحاجبة). - تُرجع
Promise<T>في JS.
- يُغلّف
-
task::future(env, tag, work)- يُغلّف
env.spawn_future(...). - يُشغّل العمل غير المتزامن على وقت تشغيل Tokio.
- تُرجع
PromiseRaw<'env, T>.
- يُغلّف
-
CancelToken/AbortToken/AbortReason- تجمع
CancelToken::new(timeout_ms, signal)بين الموعد النهائي والـAbortSignalالاختياري. CancelToken::heartbeat()هو إلغاء تعاوني لحلقات الحجب.CancelToken::wait()هو انتظار إلغاء غير متزامن (Signal/Timeout/UserCtrl-C).- يتيح
AbortTokenللكود الخارجي طلب الإلغاء (abort(reason)).
- تجمع
blocking مقابل future: نموذج التنفيذ والاختيار
Section titled “blocking مقابل future: نموذج التنفيذ والاختيار”استخدام task::blocking
Section titled “استخدام task::blocking”استخدمه عندما يكون العمل مكثفًا حوسبيًا أو متزامنًا/حاجبًا بطبيعته:
- المسح بالتعبيرات النمطية/الملفات (
grep،glob،fuzzy_find) - عمليات حلقة PTY المتزامنة الداخلية (
run_pty_syncعبرspawn_blocking) - تحويلات الحافظة/الصور/HTML
السلوك:
- تستقبل إغلاق العمل نسخة مستنسخة من
CancelToken. - يُلاحَظ الإلغاء فقط عند تحقق الكود من
ct.heartbeat()?. - يرفض
Err(...)من الإغلاق وعد JS.
استخدام task::future
Section titled “استخدام task::future”استخدمه عندما يجب أن يعمل العمل بـ await على عمليات غير متزامنة:
- تنسيق جلسات shell (
shell.run،executeShell) - المسابقة بين المهام (
tokio::select!) بين الإتمام والإلغاء
السلوك:
- يمكن للمستقبل أن يتسابق بين الإتمام الطبيعي و
ct.wait(). - في مسار الإلغاء، عادةً ما تنشر التنفيذات غير المتزامنة الإلغاء إلى الأنظمة الفرعية الداخلية (مثل
tokio_util::CancellationToken) وتفرض الإلغاء القسري عند انتهاء مهلة السماحة اختياريًا.
تعيين واجهة برمجة JS ↔ صادرات Rust (المتعلقة بالمهام/الإلغاء)
Section titled “تعيين واجهة برمجة JS ↔ صادرات Rust (المتعلقة بالمهام/الإلغاء)”| واجهة JS | صادرات Rust (#[napi]) | المجدول | ربط الإلغاء |
|---|---|---|---|
grep(options, onMatch?) | grep | task::blocking("grep", ct, ...) | CancelToken::new(options.timeoutMs, options.signal) + ct.heartbeat() |
glob(options, onMatch?) | glob | task::blocking("glob", ct, ...) | CancelToken::new(...) + ct.heartbeat() في حلقة التصفية |
fuzzyFind(options) | fuzzy_find | task::blocking("fuzzy_find", ct, ...) | CancelToken::new(...) + ct.heartbeat() في حلقة التسجيل |
shell.run(options, onChunk?) | Shell::run | task::future(env, "shell.run", ...) | ct.wait() في مسابقة مع مهمة التشغيل؛ يتصل بـ CancellationToken في Tokio |
executeShell(options, onChunk?) | execute_shell | task::future(env, "shell.execute", ...) | مثل السابق |
pty.start(options, onChunk?) | PtySession::start | task::future(env, "pty.start", ...) + spawn_blocking داخلي | يُتحقق من CancelToken في حلقة PTY المتزامنة عبر heartbeat() |
htmlToMarkdown(html, options?) | html_to_markdown | task::blocking("html_to_markdown", (), ...) | لا يوجد (رمز ()) |
PhotonImage.parse/encode/resize | PhotonImage::{parse,encode,resize} | task::blocking(...) | لا يوجد (رمز ()) |
copyToClipboard/readImageFromClipboard | copy_to_clipboard / read_image_from_clipboard | task::blocking(...) | لا يوجد (رمز ()) |
لا يستخدم كل من text.rs وps.rs حاليًا task::blocking/task::future، وبالتالي لا يشاركان في مسار الإلغاء هذا.
دورة حياة الإلغاء وانتقالات الحالة
Section titled “دورة حياة الإلغاء وانتقالات الحالة”دورة حياة CancelToken
Section titled “دورة حياة CancelToken”CancelToken تعاونية وذات حالة:
Created ├─ no signal + no timeout -> passive token (never aborts unless externally emplaced) ├─ signal registered -> waits for AbortSignal callback └─ deadline set -> timeout check becomes active
Running ├─ heartbeat()/wait() sees signal -> AbortReason::Signal ├─ heartbeat()/wait() sees deadline -> AbortReason::Timeout ├─ wait() sees Ctrl-C -> AbortReason::User └─ no abort -> continue
Aborted (terminal) └─ first abort reason wins (atomic flag + notifier)الإلغاء قبل البدء مقابل الإلغاء أثناء التنفيذ
Section titled “الإلغاء قبل البدء مقابل الإلغاء أثناء التنفيذ”-
قبل البدء / قبل أول فحص للإلغاء:
- يمكن لمستخدمي
task::futureالذين يتسابقون علىct.wait()حل الإلغاء فورًا بمجرد دخولهمselect!. - يلاحظ مستخدمو
task::blockingالإلغاء فقط عند وصول كود الإغلاق إلىheartbeat(). إذا لم ينفّذ الإغلاق heartbeat مبكرًا، يتأخر الإلغاء.
- يمكن لمستخدمي
-
أثناء التنفيذ:
blocking: تُرجعheartbeat()التاليةErr("Aborted: ...").future: يفوز فرعct.wait()فيselect!، ثم يُلغي الكود الآلية غير المتزامنة التابعة (بالنسبة لـ shell: يُلغي رمز Tokio، وينتظر حتى 2 ثانية، ثم يُجبر على إلغاء المهمة).
توقعات النبضات لحلقات التشغيل الطويل
Section titled “توقعات النبضات لحلقات التشغيل الطويل”يجب تشغيل heartbeat() بإيقاع منتظم في الحلقات ذات مجموعات العمل غير المحدودة أو الكبيرة.
الأنماط الملاحظة:
glob::filter_entries: التحقق من كل إدخال قبل التصفية/المطابقة.fd::score_entries: التحقق من كل مرشح تم مسحه.grep_sync: فحص إلغاء صريح قبل مرحلة البحث المكثفة، بالإضافة إلى استدعاءات fs-cache التي تستقبل الرمز أيضًا.run_pty_sync: التحقق في كل دورة حلقة (بإيقاع نوم ~16ms) وإيقاف العملية الابن عند الإلغاء.
القاعدة العملية: لا ينبغي أن تتجاوز أي حلقة على مدخلات خارجية الحجم فترة محدودة قصيرة دون نبضة.
سلوك الفشل ونشر الأخطاء إلى JS
Section titled “سلوك الفشل ونشر الأخطاء إلى JS”المهام الحاجبة
Section titled “المهام الحاجبة”مسار الخطأ:
- يُرجع الإغلاق
Err(napi::Error)(بما في ذلك إلغاءheartbeat()). - تُرجع
Task::compute()قيمةErr. - يرفض
AsyncTaskوعد JS.
سلاسل الأخطاء النموذجية:
Aborted: TimeoutAborted: Signal- أخطاء النطاق (
Failed to decode image: ...،Conversion error: ...، إلخ)
المهام المستقبلية
Section titled “المهام المستقبلية”مسار الخطأ:
- يُرجع جسم غير المتزامن
Err(napi::Error)أو يُعيَّن فشل الانضمام (... task failed: {err}). - يرفض الوعد المُولَّد بـ
task::future. - تُرجع بعض واجهات برمجة التطبيقات عن قصد نتائج إلغاء منظمة بدلًا من الرفض (
ShellRunResult/ShellExecuteResultمع أعلامcancelled/timed_outوexit_code: None).
تقسيم تقارير الإلغاء
Section titled “تقسيم تقارير الإلغاء”- الإلغاء كخطأ: معظم الصادرات الحاجبة التي تستخدم
heartbeat()?. - الإلغاء كنتيجة مكتوبة: واجهات برمجة تطبيقات الأوامر من نمط shell/pty التي تُنمذج الإلغاء في هياكل النتائج.
اختر نموذجًا واحدًا لكل واجهة برمجة ووثّقه صراحةً.
المشكلات الشائعة
Section titled “المشكلات الشائعة”-
نبضة مفقودة في حلقات الحجب
- الأعراض: يبدو أن المهلة/الإشارة مُتجاهلة حتى نهاية الحلقة.
- الحل: أضف
ct.heartbeat()?في بداية الحلقة وقبل الخطوات المكثفة لكل عنصر.
-
أقسام غير قابلة للإلغاء لفترات طويلة
- الأعراض: ارتفاع زمن الاستجابة للإلغاء خلال استدعاء واحد كبير (فك الترميز، الفرز، الضغط، إلخ).
- الحل: قسّم العمل إلى قطع مع حدود نبضات؛ إذا كان ذلك مستحيلًا، وثّق زمن الاستجابة.
-
حجب منفّذ غير المتزامن
- الأعراض: تتوقف واجهة برمجة التطبيقات غير المتزامنة عند تشغيل كود مكثف حوسبيًا مباشرةً في المستقبل.
- الحل: انقل كتل CPU/المتزامن إلى
task::blockingأوtokio::task::spawn_blocking.
-
دلالات إلغاء غير متسقة
- الأعراض: ترفض إحدى واجهات برمجة التطبيقات عند الإلغاء، بينما تحل واجهة أخرى مع أعلام، مما يُربك المستدعين.
- الحل: وحّد المعالجة لكل نطاق واحتفظ بتوثيق المُغلّف متوافقًا.
-
نسيان جسر الإلغاء في المهام غير المتزامنة المتداخلة
- الأعراض: يُلغى الرمز الخارجي لكن قراء الداخل/مهام العمليات الفرعية تستمر في العمل.
- الحل: اجسر الإلغاء إلى الرمز/الإشارة الداخلية وفرض مهلة السماحة والإلغاء القسري احتياطيًا.
قائمة مراجعة الصادرات القابلة للإلغاء الجديدة
Section titled “قائمة مراجعة الصادرات القابلة للإلغاء الجديدة”-
صنّف العمل بشكل صحيح:
- مكثف حوسبيًا أو حجب متزامن ->
task::blocking - إدخال/إخراج غير متزامن / تنسيق
await->task::future
- مكثف حوسبيًا أو حجب متزامن ->
-
اكشف مدخلات الإلغاء عند الحاجة:
- أدرج
timeoutMsوsignalفي خيارات#[napi(object)] - أنشئ
let ct = task::CancelToken::new(timeout_ms, signal);
- أدرج
-
اربط الإلغاء عبر جميع الطبقات:
- حلقات الحجب:
ct.heartbeat()?على فترات منتظمة - تنسيق غير متزامن: تسابق مع
ct.wait()وإلغاء المهام الفرعية/الرموز
- حلقات الحجب:
-
حدد عقد الإلغاء:
- رفض الوعد مع خطأ إلغاء، أو
- حل نوع مكتوب
{ cancelled, timedOut, ... } - احتفظ بهذا العقد متسقًا لعائلة واجهة برمجة التطبيقات
-
انشر الأخطاء مع السياق:
- عيّن الأخطاء عبر
Error::from_reason(format!("...: {err}")) - أدرج بادئات خاصة بالمرحلة (
spawn،decode،wait، إلخ)
- عيّن الأخطاء عبر
-
تعامل مع الإلغاء قبل البدء وأثناء التنفيذ:
- يجب أن يحدث فحص/انتظار الإلغاء قبل الجسم المكلف وأثناء التنفيذ الطويل
-
تحقق من عدم إساءة استخدام المنفّذ:
- لا عمل متزامن طويل مباشرةً داخل المستقبلات غير المتزامنة دون
spawn_blocking/مُغلّف مهمة حاجبة
- لا عمل متزامن طويل مباشرةً داخل المستقبلات غير المتزامنة دون