akovalenko/sbcl-win32-threads — Issue #10: Lisp <--> .NET interoperability
Author: Lovesan
Created: 2011-10-21 18:02:30+00:00
State: open

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)

```c++
/*
* set LIBPATH=%LIBPATH%%windir%\Microsoft.NET\Framework\v4.0.30319\WPF;
* cl /EHac /clr /TP /LD Window.cpp
*/
#using
#using
#using
#using

#include

extern "C" __declspec(dllexport) int _stdcall OpenWindow()
{
try
{
gcroot 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:

```lisp
(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.