Safari کے JavaScript انجن میں ایک پوشیدہ نقص ہے: جب کسی module-style Web Worker کا entry script bundle میں کہیں اور import کیا جاتا ہے، تو Safari اس entry script کو دوسری بار چلا دیتا ہے۔ یہ دوہری ایگزیکیوشن singleton state کو توڑ دیتی ہے، جس سے وہ workers خاموشی سے متاثر ہوتے ہیں جو shared caches یا single-instance objects پر انحصار کرتے ہیں۔
یہ مسئلہ ایک براؤزر پر مبنی ویڈیو پروسیسنگ ایپ تیار کرتے وقت سامنے آیا جو ProRes فائلز کو ڈیکوڈ کرنے کے لیے Web Workers کا استعمال کرتی ہے۔ Chrome اور Firefox نے بغیر کسی مسئلے کے کوڈ کو سنبھال لیا، لیکن Safari ویڈیو لوڈ کرنے میں مسلسل ناکام رہا۔ کنسول نے صرف ایک عام “cannot read video” ایرر رپورٹ کیا، جبکہ اصل وجہ worker کا initialization code دو بار چلنا اور میموری میں اسی module کی دو آزاد کاپیاں چھوڑنا تھا۔
یہ بگ کس طرح ظاہر ہوتا ہے
جدید bundlers (Vite, Rollup، وغیرہ) اکثر shared utilities کو worker کی entry file میں شامل کر لیتے ہیں تاکہ lazy-loaded chunks اس کوڈ کو entry point سے دوبارہ import کر سکیں۔ ان براؤزرز میں جو standard module loader کے طرزِ عمل پر عمل کرتے ہیں، ایک بار جب entry module instantiate ہو جاتا ہے تو loader کسی بھی بعد کے import کے لیے وہی module object واپس کر دیتا ہے، جس سے دوسری بار ایگزیکیوشن نہیں ہوتی۔
Safari اس توقع سے ہٹ کر کام کرتا ہے۔ جب کوئی lazy-loaded chunk worker کی entry file کو import کرتا ہے، تو Safari اس import کو ایک نئی module request کے طور پر لیتا ہے اور entry script کو دوبارہ چلا دیتا ہے۔ اس کا نتیجہ یہ ہوتا ہے کہ وہاں موجود ہر variable، class، یا singleton کے دو الگ الگ instances بن جاتے ہیں۔
جب entry دو بار چلے تو کیا ٹوٹتا ہے
- Singletons اور caches اب ڈیٹا شیئر نہیں کرتے؛ ایک کاپی خالی cache دیکھتی ہے جبکہ دوسری اسے پُر کرتی ہے۔
- Registries (مثال کے طور پر، message handlers کی فہرست) دونوں instances کے درمیان تقسیم ہو جاتی ہے، جس سے ایک طرف سے وہ مؤثر طور پر خالی رہ جاتی ہے۔
- Event listeners دو بار منسلک ہو جاتے ہیں، جس سے ممکنہ طور پر duplicate handling یا memory bloat ہو سکتا ہے۔
- WebAssembly (WASM) modules دو بار لوڈ ہوتے ہیں، جس سے bandwidth اور initialization کا وقت ضائع ہوتا ہے۔
- یہ ناکامی خاموش ہوتی ہے: کوئی uncaught exception نہیں پھینکا جاتا، صرف وہ downstream logic جو مفقود state پر انحصار کرتا ہے، غلط کام کرنے لگتا ہے۔
پروجیکٹ میں مسئلے کی نشاندہی کرنا
Built assets کے خلاف ایک فوری grep یہ ظاہر کر سکتا ہے کہ آیا کوئی chunk worker کی entry file کو import کرتا ہے:
grep -l 'from"./your.worker-' dist/assets/*.js
اگر کمانڈ میں کوئی فائلیں نظر آتی ہیں، تو امکان ہے کہ وہ imports Safari میں double-run bug کو ٹرگر کر رہے ہیں۔
عملی حل (Practical workarounds)
Worker entry سے shared code کو الگ کریں۔
Bundler کو اس طرح کنفیگر کریں کہ وہ common libraries کو اپنے الگ chunk میں رکھے (مثلاً Rollup کےmanualChunksکا استعمال کرتے ہوئے)۔ اس کے بعد worker اور تمام lazy-loaded modules اس تیسری فائل سے library کو import کریں گے، جس سے worker کے entry point کو import کرنے کی ضرورت ختم ہو جائے گی۔ایک پتلی (thin) entry file استعمال کریں۔
Worker کے entry script کو صرف ایک لائن تک محدود کر دیں جو اصل implementation کو re-export کرے:// worker-entry.js import("./main.js");جب تک کوئی دوسرا bundle
worker-entry.jsکو import نہیں کرتا، Safari کبھی دوسری import request نہیں دیکھے گا، اس لیے entry صرف ایک بار چلے گی۔
دونوں طریقے پورے ایپلیکیشن میں worker کے initialization code کو single-tonic رکھتے ہیں۔
یہ بگ کیوں اہم ہے
Web Workers بھاری کمپیوٹیشن—جیسے ویڈیو انکوڈنگ، امیج پروسیسنگ، کرپٹوگرافی—کو main thread سے دور کرنے کا ایک عام طریقہ ہے۔ ایک خاموش state split ایک مکمل طور پر فعال فیچر کو ایک ایسے وقفے وقفے سے ہونے والے فالئیور میں بدل سکتا ہے جو صرف Safari پر ظاہر ہوتا ہے، جو کہ ڈیسک ٹاپ اور موبائل ڈیوائسز کے ایک بڑے حصے پر ڈیفالٹ براؤزر ہے۔ چونکہ ایرر ایک عام میڈیا-لوڈ فالئیور کے طور پر سامنے آتا ہے، اس لیے ڈویلپرز غلط علامت (symptom) کا پیچھا کرنے میں گھنٹوں ضائع کر سکتے ہیں۔
یہ بگ ایک وسیع تر خطرے کو بھی اجاگر کرتا ہے: ان module-loader semantics پر انحصار کرنا جو تمام براؤزرز میں یکساں طور پر نافذ نہیں ہیں۔ جب ایک bundler کی optimization حکمت عملی ایک واحد shared module instance کا فرض کرتی ہے، تو کوئی بھی انحراف اس مفروضے کو توڑ سکتا ہے۔
دوسرا نقطہ نظر اور کھلے سوالات
Safari کا طرزِ عمل اس کے اپنے module resolution rules کے مطابق ہے، جو workers سے متعلق edge cases میں spec سے تھوڑا مختلف ہے۔ کچھ ڈویلپرز کا کہنا ہے کہ bundlers کو worker کی entry file میں shared code رکھنے سے مکمل طور پر گریز کرنا چاہیے، جس سے یہ مسئلہ براؤزر کے نقص کے بجائے build-time نظم و ضبط کا معاملہ بن جاتا ہے۔ دوسرے اشارہ کرتے ہیں کہ Safari کا یہ انحراف دستاویز شدہ (documented) نہیں ہے، جس کی وجہ سے ڈویلپرز کے پاس اس کا پیشگی اندازہ لگانے کا کوئی قابل اعتماد طریقہ نہیں ہے۔
Apple نے عوامی طور پر اس مسئلے کو تسلیم نہیں کیا ہے، اور اس کے حل کے لیے کوئی معلوم ٹائم لائن موجود نہیں ہے۔ جب تک Safari اپنا loader تبدیل نہیں کرتا، ذمہ داری ڈویلپرز پر ہے کہ وہ اپنے bundles کو دوبارہ ترتیب دیں یا اپنے CI pipelines میں detection logic شامل کریں۔
آگے کیا نظر رکھنا ہے
- براؤزر اپ ڈیٹس – Safari کے ریلیز نوٹس پر نظر رکھیں کہ آیا وہاں module-worker ہینڈلنگ کا کوئی ذکر ہے۔
- بنڈلر کمیونٹی پیچز – Vite، Rollup اور دیگر ٹولز اس پیٹرن سے بچنے کے لیے وارننگز یا خودکار چنکنگ (automatic chunking) کی حکمت عملی متعارف کروا سکتے ہیں جو اس بگ (bug) کا سبب بنتا ہے۔
- ٹیسٹنگ کے طریقے – ریلیز سے پہلے Safari پر حقیقی دنیا کی میڈیا فائلز اور فل اسٹیک ورکر ٹیسٹ شامل کرنے سے خاموش خرابی (silent failure) کا جلد پتہ لگایا جا سکتا ہے۔
خلاصہ
اگر آپ کے Safari صارفین ورکر سے متعلق ناقابل فہم ناکامیوں کا سامنا کرتے ہیں، تو چیک کریں کہ آیا کوئی غیر ورکر بنڈل ورکر کے entry script کو امپورٹ تو نہیں کر رہا۔ ڈبل ایگزیکیوشن بگ خاموشی سے singleton state کو تباہ کر دیتا ہے، لیکن شیئرڈ کوڈ کو entry point سے باہر منتقل کرنے یا entry کو ایک پتلی re-export میں تبدیل کرنے سے براؤزر کے فکس کا انتظار کیے بغیر درست طرزِ عمل بحال ہو جاتا ہے۔
