Skip to content

Lisp <--> .NET interoperability #10

Description

@Lovesan

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions