SWI-Prolog / contrib-swiplcs

Interface from C# to SWI-Prolog
http://www.lesta.de/Prolog/SwiPlCs/Generated/Index.html
24 stars 10 forks source link

unhandled System.AccessViolationException #2

Open SaeedHussein opened 10 years ago

SaeedHussein commented 10 years ago

Hi, I am new here and i hope that i will find a solution for my problem. The background of the problem is as follows: I am trying to build an expert system that constitute a C# front-end which is interacting with Swi-prolog. I have downloaded SwiPlCs.dll and added a reference to it in a Visual Studio project(Win form app) that i have created to test if i can query prolog from c#(i followed the example used in the documentation). it worked fine. Then, in a more complicated scenario, i have built a Wcf service that will act as an intermediary layer between Swi prolog and C# client application(it consumes the service). the service is hosted in IIS 7.0. For the sake of simplicity, lets say my service contains three methods; the first method initializes the prolog engine, consults prolog source file then queries the file. the second method performs another query. the third method calls PlCleanup().

The client invokes the first method just fine and a result of the first query is successfully returned. when client tries to call the second method that is performing the second query an exception is thrown with message (attempted to read or write protected memory) which causes the application to freeze. i checked the event viewer and this is what i get: Application: w3wp.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.AccessViolationException Stack: at SbsSW.SwiPlCs.SafeNativeMethods.PL_new_term_ref() at SbsSW.SwiPlCs.PlQuery..ctor(System.String, System.String) at SbsSW.SwiPlCs.PlQuery..ctor(System.String) at PrologQueryService.PrologQueryService.DetermineAgeGroup(Int32) at DynamicClass.SyncInvokeDetermineAgeGroup(System.Object, System.Object[], System.Object[]) at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(System.ServiceModel.Dispatcher.MessageRpc ByRef) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean) at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext) at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext) at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult) at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(System.IAsyncResult) at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult) at System.ServiceModel.Diagnostics.TraceUtility+<>cDisplayClass4.b2(System.AsyncCallback, System.IAsyncResult) at System.Runtime.AsyncResult.Complete(Boolean) at System.Runtime.InputQueue1+AsyncQueueReader[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Set(Item<System.__Canon>) at System.Runtime.InputQueue1[[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Dispatch() at System.ServiceModel.Channels.ReliableDuplexSessionChannel.ProcessDuplexMessage(System.ServiceModel.Channels.WsrmMessageInfo) at System.ServiceModel.Channels.ServerReliableDuplexSessionChannel.ProcessMessage(System.ServiceModel.Channels.WsrmMessageInfo) at System.ServiceModel.Channels.ReliableDuplexSessionChannel.HandleReceiveComplete(System.IAsyncResult) at System.ServiceModel.Channels.ReliableDuplexSessionChannel.OnReceiveCompletedStatic(System.IAsyncResult) at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult) at System.Runtime.AsyncResult.Complete(Boolean) at System.Runtime.AsyncResult.Complete(Boolean, System.Exception) at System.ServiceModel.Channels.ReliableChannelBinder1+InputAsyncResult1[[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].OnInputComplete(System.IAsyncResult) at System.ServiceModel.Channels.ReliableChannelBinder1+InputAsyncResult1[[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].OnInputCompleteStatic(System.IAsyncResult) at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult) at System.Runtime.AsyncResult.Complete(Boolean) at System.Runtime.AsyncResult.Complete(Boolean, System.Exception) at System.ServiceModel.Security.SecuritySessionServerSettings+ServerSecuritySessionChannel+ReceiveRequestAsyncResult.Complete(Boolean, System.Exception) at System.ServiceModel.Security.SecuritySessionServerSettings+ServerSecuritySessionChannel+ReceiveRequestAsyncResult.OnReceive(System.IAsyncResult) at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult) at System.Runtime.AsyncResult.Complete(Boolean) at System.Runtime.AsyncResult.Complete(Boolean, System.Exception) at System.ServiceModel.Channels.ReliableChannelBinder1+InputAsyncResult1[[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].OnInputComplete(System.IAsyncResult) at System.ServiceModel.Channels.ReliableChannelBinder1+InputAsyncResult1[[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].OnInputCompleteStatic(System.IAsyncResult) at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult) at System.ServiceModel.Diagnostics.TraceUtility+<>cDisplayClass4.b2(System.AsyncCallback, System.IAsyncResult) at System.Runtime.AsyncResult.Complete(Boolean) at System.Runtime.InputQueue`1+AsyncQueueReader[[System.Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Set(Item) at System.Runtime.InputQueue1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Dispatch() at System.Runtime.InputQueue1[[System._Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].OnDispatchCallback(System.Object) at System.Runtime.ActionItem+DefaultActionItem.TraceAndInvoke() at System.Runtime.ActionItem+DefaultActionItem.Invoke() at System.Runtime.ActionItem+CallbackHelper.InvokeWithoutContext(System.Object) at System.Runtime.IOThreadScheduler+ScheduledOverlapped.IOCallback(UInt32, UInt32, System.Threading.NativeOverlapped) at System.Runtime.Fx+IOCompletionThunk.UnhandledExceptionFrame(UInt32, UInt32, System.Threading.NativeOverlapped_) at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)

UweLesta commented 10 years ago

Hello Saeed,

I am not a WCF expert. So may be I am wrong.

First, it is always a good idea if a web service are stateless.

Second, it is easy to build a full web service in SWI-Prolog. See http://www.swi-prolog.org/pldoc/index.html

If you still want to go with WCF and SwiPlCs and your web service is not stateless:

AFAIK the IIS has dispatch an incoming request to one thread of a thread pool. If the first request initialise Prolog in thread A, most likely the second request go to an other thread (B). So you need to manage the relations between the threads from the thread pool of the IIS and the SWI-Prolog threads. I had done this a long time ago on an earlier IIS version by the following global.asax.cs file, which is from http://www.lesta.de/prolog/chessboard/chessboard.aspx

namespace WebTestPl { ///

/// Summary description for Global.
/// </summary>

public class Global : System.Web.HttpApplication
{
    public Global()
    {
        InitializeComponent();
    }     

    static public void log(string msg)
    {
        try 
        {
            FileStream fs = new FileStream("WebChessBoard.log", FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamWriter w = new StreamWriter(fs);
            w.BaseStream.Seek(0, SeekOrigin.End);
            w.Write("{0} :", DateTime.Now.ToLongTimeString());
            w.Write(msg + "\n");
            w.Flush();
            w.Close();
        }
        catch (Exception e)
        {
            string x = e.Message;
        }
    }

    static public void PlEngineAttach(HttpSessionState pSession)
    {
        try 
        {
            if (pSession["PlMtEngine"] != null)
            {
                PlMtEngine mple = (PlMtEngine)pSession["PlMtEngine"];
                mple.PL_set_engine();
                log("  #      PL_set_engine " + mple.ToString());
            } 
            else 
            {   // error
                throw (new Exception("PlEngineAttach failed : Session['PlMtEngine'] is null"));
            }
        }
        catch (Exception e)
        {
            throw(e);
        }
    } // PlEngineAttach

    static public void PlEngineDetach(HttpSessionState pSession)
    {
        try 
        {
            if (pSession["PlMtEngine"] != null)
            {
                PlMtEngine mple = (PlMtEngine)pSession["PlMtEngine"];
                mple.PL_detach_engine();
                log("  #      PL_detach_engine " + mple.ToString());
            } 
            else 
            {   // error
                throw (new Exception("PlEngineDetach failed : Session['PlMtEngine'] is null"));
            }
        }
        catch (Exception e)
        {
            throw(e);
        }
    } // PlEngineDetach

    protected void Application_Start(Object sender, EventArgs e)
    {
        Application["RM"] = ResourceManager.CreateFileBasedResourceManager("texte",
            Server.MapPath("resources")
            + Path.DirectorySeparatorChar,
            null);

        string qlf = ConfigurationSettings.AppSettings["Prolog_qlf"];

        String [] param ={"ChessBoard.exe", "-x", qlf, "null"};
        PlEngine ple = new PlEngine(4, param);
        Application["Prolog"] = ple;
        log("Application_Start (new PlEngine)");
    }

    protected void Session_Start(Object sender, EventArgs e)
    {
        Session.Timeout = 5;    // 5 minutes until this session die when nothing has happen
        string qlf = ConfigurationSettings.AppSettings["Prolog_qlf"];
        String [] param ={"ChessBoard.exe", "-x", qlf, "null"};
        if( ! PlEngine.is_initialised(4,param))
        {
            PlEngine ple = new PlEngine(4, param);
            Application["Prolog"] = ple;
            log("Session_Start  (new PlEngine)");
        }

        PlMtEngine mple = new PlMtEngine();
        Session["PlMtEngine"] = mple;
        log("Session_Start new PlMtEngine() SessionID=" + Session.SessionID);
    }

    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        // For each request initialise the culture values with
        // the user language as specified by the browser.
        try 
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo(Request.UserLanguages[0]);
        }
        catch(Exception) 
        {   // provide fallback for not supported languages.
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
        }
        Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

        // prolog part
        log("Application_BeginRequest");
    }

    protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
    {
        log("  #  Application_PreRequestHandlerExecute " + Session.SessionID);
        PlEngineAttach(Session);
    }

    protected void Application_PostRequestHandlerExecute(Object sender, EventArgs e)
    {
        log("  #  Application_PostRequestHandlerExecute " + Session.SessionID);
        PlEngineDetach(Session);
    }

    protected void Application_EndRequest(Object sender, EventArgs e)
    {
        log("Application_EndRequest");
    }

    protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        log("Application_AuthenticateRequest");
    }

    protected void Application_Error(Object sender, EventArgs e)
    {
        log("    **** Application_Error");
    }

    protected void Session_End(Object sender, EventArgs e)
    {
        // destroy all Prolog objects in the right order if they exist
        if (Session["SolutionQuery"] != null)
        {
            PlQuery q2 = (PlQuery)Session["SolutionQuery"];
            q2.free();
            Session["SolutionQuery"] = null;
        }
        if (Session["PlMtEngine"] != null)
        {
            PlMtEngine mple = (PlMtEngine)Session["PlMtEngine"];
            mple.free();
            Session["PlMtEngine"] = null;
        } 

        log("Session_End delete PlMtEngine SessionID=" + Session.SessionID);
    }

    protected void Application_End(Object sender, EventArgs e)
    {
        PlEngine ple = (PlEngine)Application["Prolog"];
        ple.PL_cleanup();
        log("Application_End PL_cleanup()");
    }

    #region Web Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {    
    }
    #endregion
}

}

SaeedHussein commented 10 years ago

Hi lesta, Sorry for the late reply. I just wanted to inform you that i have managed to find a work around for the issue. Now, the client application can start communicating with the Wcf service which in turn will query Swi-prolog. I did two fixes to resolve the issue: 1- Setting the instanceContextMode to PerSession. and ConcurrencyMode to Single with these attributes set to these values, one instance of the service will be created for each client. And requests will be processed sequentially one after the other. 2- Calling PlCleanup() after every query. Every method in my Wcf service that is querying prolog, has to initialize the engine, consult prolog source file, pose a query then exit by invoking the PlCleanup() method.