وسام البهنسي

مدونة مبرمج معماري

أنا أولاً

بعد أن أنهيتُ بعض الإصلاحات في نظام التدوين هذا يوم أمس قمتُ برفع النسخة المحدثة إلى الموقع، لأفاجأ بأن جميع رموز الابتسامات في التعليقات قد تحولت إلى عبارات غريبة وغامضة تشبه تلك الخاصة باستدعاء الجان أو حتى جورج بوش نفسه!

 

أنا لم أقم بأية تعديلات تمسّ التعليقات لا من قريب ولا من بعيد، فما الذي حدث؟ قمتُ برفع النسخة نفسها مرة أخرى، فكانت المفاجأة الثانية هي أن المشكلة قد زالت. وبعد محاولات عدة، توصلت لنتيجة أن الخطأ يظهر عشوائياً في كل مرة يُعاد فيها تشغيل الموقع.

 

هممم… لو كان نظام التدوين مكتوباً بلغة ++C لقلنا أن هذه آثار استخدام متغير غير مهيأ، وبالتالي فإن قيمته تختلف في كل مرة يُعاد تشغيل البرنامج فيها. لكن النظام مكتوب بلغة #C، وكما نعلم فإن #C تقوم بتصفير كل القيم الغير مهيأة. إذن ليست هذه هي المشكلة.

 

الاحتمال الثاني هو ما يدعى بحالة سباق (race condition) بين مسارات تنفيذ مختلفة (threads). في هذه الحالة يتسابق مساري تنفيذ (أو أكثر) للوصول إلى أحد متغيرات البرنامج دون تنسيق، مما يتسبب بتصرفات مختلفة بناء على أي المسارات وصل قبل الآخر، وهذا أمر هلامي لا يمكن توقعه. حيث أن زمن تنفيذ مسار يختلف من لحظة لأخرى وتدخل به عوامل عدة كالبرامج التي تعمل في الخلفية في نظام التشغيل ودرجة حرارة المعالج ودوران القرص الصلب بل وحتى بُعد المشتري عن المريخ.

 

بعد أخذ هذا الاحتمال بعين الاعتبار، وإعادة النظر إلى الكود، وجدتُ أن هذا هو فعلاً ما يحدث. فنظام المدونة يملك قائمة بما يدعى الإضافات (extensions) تختص كل منها بوظيفة محددة كتحويل رموز الابتسامات إلى صور، أو تفعيل الروابط في التعليقات، أو إرسال الرسائل الإلكترونية التنبيهية … الخ.

 

هذه القائمة تتم تعبئتها بشكل مؤتمت من خلال كل إضافة. هذه عينة كود لإحدى الإضافات:

[Extension("تحول الابتسامات المكتوبة في التعليقات إلى صور ", "1.3", "مطوريّ المدونة")]
public class Smilies
{
    static Smilies()
    {
        Comment.Serving += new EventHandler<ServingEventArgs>(Post_CommentServing);
    }
 
    /// <summary>
    /// The event handler that is triggered every time a comment is served to a client.
    /// </summary>
    private static void Post_CommentServing(object sender, ServingEventArgs e)
    {
        if (!string.IsNullOrEmpty(e.Body))
        {
            e.Body = e.Body.Replace("(H)", Convert("cool", "هادئ"));
            e.Body = e.Body.Replace(":'(", Convert("cry", "باكي"));
            e.Body = e.Body.Replace(":$", Convert("embarassed", "خجل"));
            e.Body = e.Body.Replace(":|", Convert("foot-in-mouth", "منزعج"));
            e.Body = e.Body.Replace(":(", Convert("frown", "حزين"));
            e.Body = e.Body.Replace("(A)", Convert("innocent", "بريء"));
            e.Body = e.Body.Replace("(K)", Convert("kiss", "محب"));
            e.Body = e.Body.Replace(":D", Convert("laughing", "ضاحك"));
            e.Body = e.Body.Replace("($)", Convert("money-mouth", "نقود"));
            e.Body = e.Body.Replace(":-#", Convert("sealed", "محكم"));
            e.Body = e.Body.Replace(":)", Convert("smile", "مبتسم"));
            e.Body = e.Body.Replace(":-)", Convert("smile", "مبتسم"));
            e.Body = e.Body.Replace(":-O", Convert("surprised", "متفاجئ"));
            e.Body = e.Body.Replace(":P", Convert("tongue-out", "مازح"));
            e.Body = e.Body.Replace("*-)", Convert("undecided", "متردد"));
            e.Body = e.Body.Replace(";-)", Convert("wink", "غمزة"));
            e.Body = e.Body.Replace("8o|", Convert("yell", "صارخ"));
        }
    }
}

 

 

 

لاحظ أن الباني مُعلن عنه على أنه ثابت (static constructor)، مما يعني أن مكتبة دوت نت ستقوم باستدعاء هذا الإجراء تلقائياً عند تحميل كود الصنف في البرنامج. الإجراء يقوم بتسجيل حَـدَث (event) في نظام التدوين ليعالج محتوى التعليقات قبل أن يتم عرضها في المتصفح.

 

هذا النسق مستخدم في كافة الأصناف الخاصة بالإضافات في المدونة. هذا يعني أن ترتيب استدعاء الإضافات محكوم بأيها يتم تحميله قبل الآخر في البرنامج، وهذه العملية غير محددة (كما تبين لي لاحقاً) وتخضع تماماً لأسلوب مكتبة دوت نت في تحميل الأصناف، والذي -فيما يبدو- أنه أسلوب متعدد المسارات لتسريع الأداء. وهكذا نجد أن ترتيب استدعاء الإضافات في المدونة يخضع تماماً للقوانين الكونية والفلكية والحرارية المحيطة بمخدم الموقع! وبما أن الإضافات يجب أن تعمل بترتيب معين، فإننا دخلنا في المحظور.

 

الآن وصلنا إلى "ما يستفاد من الحديث": لا تستخدم أسلوب الباني الثابت هذا، فهو كريه ومزعج ويفقدك السيطرة على زمام الأمور، فبدلاً من أن تكون أنت المتحكم ببرنامجك، سيصبح برنامجك عرضة للعمل وفقاً لمبدأ الصدفة.

 

هذا الكلام لن يعجب المبرمجين المخضرمين منكم، فمنكم من يقول: طيب طالما هذه الميزة في حكم المكروه، فلماذا وضعَـتْها مايكروسوفت أساساً؟

لهؤلاء الأذكياء أقول: لنفس السبب الذي وُضعت لأجله المتغيرات الثابتة في ++C. هناك ضوابط على استخدام هذه الميزة، وإلا فإنها ستكون وبالاً على المبرمج. باختصار، لا تتعامل مع أصناف خارجية من خلال الباني الثابت لأي صنف ما… وبما أننا معرضون للنسيان فإنني أقول: الباب الذي تأتيك منه الريح، سُدّه واستريح! وقد أعذر من أنذر!

التعليقات (1) -

  • مؤيد

    05/02/2010 12:31:55 ص | الرد

    شكراً على النصيحة، أذكر أني واجهت مشكلة مشابهة واستغرق الأمر وقتاً طويلاً حتى عرفت السبب، سأحاول أن أتذكرها في المرات القادمة.
    وبالمناسبة، سعيد أنك اكتشفت هذا الخطأ واستطعت معرفة سببه بل واصلاحه Smile
    جزيل الشكر!

أضف تعليقاً

Loading