sbcl-win32-threads icon indicating copy to clipboard operation
sbcl-win32-threads copied to clipboard

Lisp <--> .NET interoperability

Open Lovesan opened this issue 14 years ago • 2 comments

Hello there.

I've recently figured out that SBCL and .NET runtimes, while working in one process, are not particularly fond of each other, and i'm interested if there's anything anyone can do about that.

Consider the following code (C++/CLI)

/*
 * set LIBPATH=%LIBPATH%%windir%\Microsoft.NET\Framework\v4.0.30319\WPF;
 * cl /EHac /clr /TP /LD Window.cpp
 */
#using <System.dll>
#using <WindowsBase.dll>
#using <PresentationCore.dll>
#using <PresentationFramework.dll>

#include <vcclr.h>

extern "C" __declspec(dllexport) int _stdcall OpenWindow()
{
    try
    {        
        gcroot<System::Windows::Window ^> window
            = gcnew System::Windows::Window;
        window->ShowDialog();
    } catch(System::Exception ^ e)
    {
        System::Console::Error->WriteLine(e);
        return 0;
    }
    return 1;
}

Here's an example of usage of this function for SBCL:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (sb-alien:load-shared-object "ole32.dll")
  (sb-alien:load-shared-object "Window.dll"))

(sb-alien:define-alien-routine
    ("CoInitializeEx" %co-initialize-ex)
    (sb-alien:signed 32)
  (reserved sb-sys:system-area-pointer)
  (mode (sb-alien:unsigned 32)))

(sb-alien:define-alien-routine
    ("CoUninitialize" %co-uninitialize)
    (sb-alien:signed 32))

(sb-alien:define-alien-routine
    ("_OpenWindow@0" %open-window)
    (sb-alien:signed 32))

(defun make-com-thread (function &key (mode :sta))
  (let ((function (lambda ()
                    (%co-initialize-ex
                      (sb-sys:int-sap 0)
                      (ecase mode
                        (:sta 2)
                        (:mta 0)))
                    (unwind-protect
                        (funcall function)
                      (%co-uninitialize)))))
    (sb-thread:make-thread function)))

(defun open-window (&optional new-thread)
  (if new-thread
    (make-com-thread
        (lambda () (%open-window))
      :mode :sta)
    (progn (unless (zerop (%co-initialize-ex (sb-sys:int-sap 0) 2))
             (error "Unable to initialize COM STA"))
           (%open-window)
           (%co-uninitialize))))

While this function(OpenWindow) works perfectly when called from either C, unmanaged C++ or .NET, when called from lisp, it causes .NET runtime(and consequently, entire lisp process) to crash with ''Process terminated due to StackOverflowException" error message.

I suppose the reason of crashes is somehow related to cooperation(or rather, contention) of lisp and .NET garbage collectors in STA environment because:

  • When this function is called from MTA, it causes no crashes(it catches InvalidOperationException, complains about invalid apartment state to stderr an quits).
  • When this function is modified to simply create a window, but not to show it, there is no crash until lisp garbage collector is run(until you call sb-ext:gc, for example)

I've also tried calling this function from Clozure CL. Again, it doesn't work, but instead of simply terminating the process, CCL runtime enters low-level debugger and complains about an exception on foreign stack.

Although resolving this issue is not a matter of urgent need, it would allow better integration of SBCL and Windows platform by opening .NET BCL and libraries for lisp.

Lovesan avatar Oct 21 '11 18:10 Lovesan

Point taken. It would be easier for me to get my hands on it if you shared the DLL, so I won't need a complete .NET development environment.

akovalenko avatar Oct 21 '11 18:10 akovalenko

https://github.com/downloads/Lovesan/doors/Window.zip

btw, SBCL version i'm currently using is "1.0.52.1.mswinmt.969-6acb698", and .NET runtime i'm talking about is "4.0.30319" (the latest version, for this moment)

Lovesan avatar Oct 21 '11 19:10 Lovesan