Loading ...
Sorry, an error occurred while loading the content.

Bug in MS Script Control

Expand Messages
  • Muhammad Ali Shah
    Assalamo Alaikum! I recently found a kind-of-bug in Microsoft Script Control. Actually I had been testing a piece of code for almost one month and couldn t get
    Message 1 of 2 , Jun 10, 2002
    • 0 Attachment
      Assalamo Alaikum!

      I recently found a kind-of-bug in Microsoft Script Control. Actually I had
      been testing a piece of code for almost one month and couldn't get anything
      out of it. I wrote to one of the members of Script Control team at
      Microsoft. He later CCed it to a colleague. This came out as a series of
      email discussion and in the end they agreed that the bug was in their
      control. Below is the email discussion - for record purposes.

      If you are interested read from first email (bottom) to last one (top most).

      <ShahG/>

      ~~~~~~~~~~~~~~~~~
      "Woh nikla suraj, lo pheli kirnain
      Teray chehray pay, ab to muskao na."
      (Palkon pay chamaktay ansoo by Jawad Ahmed)


      -----Original Message-----
      From: Rok Yu [mailto:rokyu@...]
      Sent: Wednesday, June 05, 2002 11:54 PM
      To: Eric Lippert; Muhammad Ali Shah
      Subject: RE: Script Control Threading Issues


      This is actually a known issue. We've received a couple other reports recently.

      We're assessing how to deal with it now.


      -----Original Message-----
      From: Eric Lippert
      Sent: Wednesday, June 05, 2002 11:48 AM
      To: 'Muhammad Ali Shah'
      Cc: Rok Yu
      Subject: RE: Script Control Threading Issues

      Hi Ali,

      I have simplified your repro scenario so that it is a less consistent repro
      but easier to debug.

      Here's the deal:

      The application below creates four worker threads from the main thread.
      Each worker thread spins up a copy of the script control. About 5% of the
      time this program deadlocks on my machine -- all six threads end up in a
      wait state.

      This is a classic thread deadlock situation. The problem is caused because
      the script control needs to spin up a thread of its own to manage the
      timeout property correctly. The critical section code which protects the
      data structures associated with this thread appears to be broken in some
      subtle way.

      Unfortunately my expertise on this code is limited. I'm passing the buck
      to my colleague Rok Yu (cc'd) -- Rok, can you take a look at this? Here is
      a typical situation I debugged:

      * The main thread is blocked on waiting for the four worker threads to
      terminate.

      * Worker thread number one is trying to shut down. It has called the
      Script Control thread shutdown sequence. That is waiting on the timer
      critical section so that it can remove per-thread data from a global list.

      * Worker threads two and three have successfully run the constructor for
      the script control but have not yet run the initializer. They are also
      waiting on the timer critical section.

      * Worker thread four is in the critical section. It has run the
      constructor and is in the initializer. It has just spun up its timer
      thread and it is waiting for the timer thread to signal it that the timer
      thread has started up successfully.

      * The timer thread associated with the script control on thread four is
      somewhere deep in NTDLL waiting on a critical section. I was unable to
      figure out what critical section it was waiting on. (If it is waiting for
      worker thread one to shut down then that would explain the deadlock. I
      can't think of what else NTDLL would be waiting on.)

      The program that repros the deadlock situation is below. A rather more
      complex program that is a more solid repro but harder to debug is even
      further below.

      Eric

      #include "stdafx.h"
      #include <atlbase.h>
      #define MAX_THREADS 4
      #import "D:\vbscript\obj\d6x86\msscript.ocx"
      using namespace MSScriptControl;
      long threadCount = 0;
      long threadExited = 0;
      DWORD WINAPI runScript (LPVOID params);
      void main (void)
      {
      DWORD dwThreadId;
      CoInitialize(NULL);
      for (int i=0; i<MAX_THREADS; i++)
      {
      HANDLE h = CreateThread (NULL, 0, runScript, 0, 0,
      &dwThreadId);
      printf ("0 | Invoked thread %d.\n", dwThreadId);
      CloseHandle (h);
      }
      while (threadExited != MAX_THREADS)
      {
      Sleep (1000);
      printf ("0 | Waiting for %d threads to die down.\n",
      threadCount - threadExited);
      }
      }

      DWORD WINAPI runScript (LPVOID threadParams)
      {
      int iThreadId;
      InterlockedIncrement(&threadCount);
      iThreadId = GetCurrentThreadId();
      printf ("%d | thread invoked.\n", iThreadId);
      CoInitialize (NULL);
      Sleep(10);
      printf ("%d | Creating Control.\n", iThreadId);
      IScriptControlPtr pScriptControl (__uuidof (ScriptControl));
      CoUninitialize ();
      printf ("%d | leaving thread.\n", iThreadId);
      InterlockedIncrement (&threadExited);
      return 0;
      }





      -----Original Message-----
      From: Muhammad Ali Shah [mailto:muhammad.ali@...]
      Sent: Tuesday, June 04, 2002 10:45 PM
      To: Eric Lippert
      Subject: RE: Script Control Threading Issues

      Hi,

      I hope I am not disturbing you too much. Please let me know if you got some
      time to try out the code. I know this ain't a liability on your side but a
      little time of yours will help me very much.

      Regards,
      Ali

      -----Original Message-----
      From: Eric Lippert [mailto:ericli@...]
      Sent: Thursday, May 23, 2002 11:15 PM
      To: Muhammad Ali Shah
      Subject: RE: Script Control Threading Issues


      You are correct that you should not need the mutex to protect the script
      control in this scenario -- if there is one script control per thread then
      that should work just fine. (Of course you still need to protect the
      counters.)

      That said, I can't see any problem with your code offhand. I will try to
      find some time next week to run it, but next week is a short week in the
      United States (it's memorial day long weekend next week) so I might not get
      a chance. Bug me again next week if I do not get back to you.

      Eric




      -----Original Message-----
      From: Muhammad Ali Shah [mailto:muhammad.ali@...]
      Sent: Thursday, May 23, 2002 8:26 AM
      To: Eric Lippert
      Subject: RE: Script Control Threading Issues

      Thanks for a detailed reply. It was really informative.

      I have written a simple piece of code that should work according to the
      best of my knowledge. It will be very helpful if you can explain this
      behavior. I create MAX_THREADS in main() and inside each thread I increment
      thread count. When the thread is leaving I increment threadExited. The main
      thread waits till threads created become equal to thread exited.

      However, this code hangs up. It works if I enable the "Sleep (10)" line
      after the WaitForSingleObject (hSeq, INFINITE). Note, I am not even calling
      run on my script control and the code being added is a blank one.

      -------------------------------------------
      #include <atlbase.h>


      #define MAX_THREADS 100
      #import "C:\winnt\system32\msscript.ocx" // msscript.ocx
      using namespace MSScriptControl;


      int threadCount = 1;
      long threadExited = 0;
      DWORD WINAPI runScript (LPVOID params);
      HANDLE hMutex;
      HANDLE hSeq;



      void main (void)
      {
      DWORD dwThreadId;
      CoInitialize(NULL);


      hMutex = CreateMutex (0, FALSE, "My_Mutex_For_My_Program.");
      hSeq = CreateMutex (0, FALSE, "Sequential_Access");

      for (int i=0; i<MAX_THREADS; i++)
      {
      printf ("0 | Invoking thread %d.\n", i);
      HANDLE h = CreateThread (NULL, 0, runScript, 0, 0,
      &dwThreadId);
      CloseHandle (h);
      }
      while (threadExited != MAX_THREADS)
      {
      Sleep (1000);
      printf ("0 | Waiting for %d threads to die down.\n",
      threadCount - threadExited);
      }
      }



      DWORD WINAPI runScript (LPVOID threadParams)
      {
      int iThreadId;

      WaitForSingleObject (hMutex, INFINITE);
      iThreadId = threadCount++;
      ReleaseMutex (hMutex);


      printf ("%d | thread invoked.\n", iThreadId);
      CoInitialize (NULL);
      try
      {
      WaitForSingleObject (hSeq, INFINITE);
      //Sleep (10);

      char cCode [255];
      sprintf (cCode, "");

      printf ("%d | Creating Control.\n", iThreadId);
      IScriptControlPtr pScriptControl (__uuidof (ScriptControl));
      pScriptControl->Language = "VBScript";

      printf ("%d | Control created. Adding Code.\n", iThreadId);
      pScriptControl->AddCode (cCode);

      ReleaseMutex (hSeq);
      }
      catch (...)
      {
      printf ("%d | exception caught.\n", iThreadId);
      ReleaseMutex (hSeq);
      }

      CoUninitialize ();

      printf ("%d | leaving thread.\n", iThreadId);
      InterlockedIncrement (&threadExited);
      return 0;
      }

      -------------------------------------------

      Please take a close look at the try/catch block inside RunScript method.
      If you look at the output, when the program hangs up, the main thread keep
      waiting for all the threads to die down. But one of the threads gets hung
      up when it is creating the script control object.

      It will be very helpful if you can tell me why the code is behaving the way
      it is. And secondly how to go about resolving it. As far as I know, I don't
      even need the WaitForSingleObject and ReleaseMutex stuff in this simple
      scenario.


      Regards,
      Ali


      -----Original Message-----
      From: Eric Lippert [mailto:ericli@...]
      Sent: Wednesday, May 22, 2002 8:24 PM
      To: Muhammad Ali Shah
      Subject: RE: Script Control Threading Issues


      Hi,

      It's not 100% clear to me what is going on. Are you sharing one script
      control over multiple threads? If so then you are calling the script
      control with the "rental" threading model. That's not legal -- the script
      control is an "apartment" threaded object.

      A "rental" object makes the following contract: "You can call me from any
      thread but there must never be two threads executing in my code at the same
      time. It is your responsibility to synchronize access to me."

      An "apartment" object makes a different contract: "You can only call me
      from the thread you used to create this instance. You may create and call
      multiple instances on multiple threads but may not call a specific instance
      from any thread other than its initializing thread."

      The names are from analogies. Imagine that you own a resource, like a
      car. You can rent the car to people, but only to one person at a time. The
      "rental" model says that the object is like a car and the renter is like a
      thread -- any renter can drive it, but only one renter can drive it at a time.

      The "apartment" analogy is a little different. Imagine that threads are
      people in an apartment building and the objects are televisions. You can
      only watch television in your own apartment. You can't break into someone
      else's apartment and change the channel! But you can have as many TVs on
      as many different channels in your own apartment as you want, and everyone
      in every apartment can be watching their televisions at the same time.

      The Script Control and the script engines are apartment objects. (Well,
      technically the script engines are free-threaded, but for practical
      purposes they are apartment objects.)

      If you have ten threads then you can create ten script controls, one on
      each thread, and you do not have to worry about synchronizing access. You
      can call any script control on the thread that created it, any time. But
      you cannot create ten threads and share a script control between them,
      synchronizing access to fool it into believing that it is
      single-threaded. You can't rent out your television and move it from
      apartment to apartment.

      What's the difference, you might ask. The rental model
      LOOKSsingle-threaded, so it should work, right? Wrong. Since the script
      control is apartment threaded it knows that it should only be called on the
      thread that created it. It takes advantage of this fact in order to store
      information in Thread Local Storage. If you call on the wrong thread then
      the script control will get bogus information from the Thread Local Storage!

      I'm not sure if that answers your question. Let me know if you have more
      questions.

      Eric


      -----Original Message-----
      From: Muhammad Ali Shah [mailto:muhammad.ali@...]
      Sent: Wednesday, May 22, 2002 5:45 AM
      To: Eric Lippert
      Subject: Script Control Threading Issues

      Dear Sir,

      This would require a bit of your time. I have failed to get a response from
      respective Newsgroups.

      We are using Script Control in a multi-threaded environment. The scenario,
      however, is very simple. Each thread calls a synchronized method on an
      object which has code like this:

      myMethod () {
      WaitForSingleObject (hMutex, INFINITE);
      try {
      IScriptControlPtr pScript (__uuidof (IScriptControl));
      pScript->Language = "VBScript";
      pScript->AddCode (myCode);
      :
      :
      pScript->Run ("main", ¶ms);
      }
      catch (...) {
      ReleaseMutex (hMutex);
      return false;
      }

      ReleaseMutex (hMutex);
      return true;
      }

      This means that only one thread will have created a script control object
      at a time. However, the application works fine only when one thread is
      active. Otherwise - in a multi-threaded environment - the executable just
      hangs up (except the main thread). This situation should not occur because
      script control is created locally by the method and only one thread will be
      active in this method at any given time - a scenario that is quite similar
      to a single-threaded environment.

      Can you please tell me what's wrong with the code and how can I go about
      handling it?

      Regards,
      Muhammad Ali Shah
      Avanza Solutions, Karachi.
    • Umar
      Very interesting indeed. So these guys do actually reply to emails.
      Message 2 of 2 , Jun 10, 2002
      • 0 Attachment
        Very interesting indeed. So these guys do actually reply to emails.


        On Monday 10 June 2002 06:12 pm, you wrote:
        > Assalamo Alaikum!
        >
        > I recently found a kind-of-bug in Microsoft Script Control. Actually I had
        > been testing a piece of code for almost one month and couldn't get anything
        > out of it. I wrote to one of the members of Script Control team at
        > Microsoft. He later CCed it to a colleague. This came out as a series of
        > email discussion and in the end they agreed that the bug was in their
        > control. Below is the email discussion - for record purposes.
        >
        > If you are interested read from first email (bottom) to last one (top
        > most).
        >
        > <ShahG/>
        >
        > ~~~~~~~~~~~~~~~~~
        > "Woh nikla suraj, lo pheli kirnain
        > Teray chehray pay, ab to muskao na."
        > (Palkon pay chamaktay ansoo by Jawad Ahmed)
      Your message has been successfully submitted and would be delivered to recipients shortly.