Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mono running in research #5380

Merged
merged 1 commit into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DockerfileJupyter
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ RUN chmod -R 777 ${WORK}

RUN pip install clr-loader

# Work around for https://github.com/pythonnet/clr-loader/issues/8
COPY fix_ffi_mono.py /opt/miniconda3/lib/python3.6/site-packages/clr_loader/ffi/mono.py
COPY fix_mono.py /opt/miniconda3/lib/python3.6/site-packages/clr_loader/mono.py
ENV LD_LIBRARY_PATH=/lib/
RUN ln -s /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libc.so

EXPOSE 8888
WORKDIR $WORK

Expand Down
30 changes: 30 additions & 0 deletions Research/fix_ffi_mono.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# flake8: noqa

cdef = []

cdef.append(
"""
typedef struct _MonoDomain MonoDomain;
typedef struct _MonoAssembly MonoAssembly;
typedef struct _MonoImage MonoImage;
typedef struct _MonoMethodDesc MonoMethodDesc;
typedef struct _MonoMethod MonoMethod;
typedef struct _MonoObject MonoObject;

MonoDomain* mono_jit_init(const char *root_domain_name);
void mono_jit_cleanup(MonoDomain *domain);
MonoAssembly* mono_domain_assembly_open(MonoDomain *domain, const char *name);
MonoImage* mono_assembly_get_image(MonoAssembly *assembly);

void mono_config_parse(const char* path);
void mono_domain_set_config(MonoDomain *domain, const char *base_dir, const char *config_file_name);

MonoMethodDesc* mono_method_desc_new(const char* name, bool include_namespace);
MonoMethod* mono_method_desc_search_in_image(MonoMethodDesc *method_desc, MonoImage *image);
void mono_method_desc_free(MonoMethodDesc *method_desc);

MonoObject* mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc);

void* mono_object_unbox(MonoObject *object);
"""
)
112 changes: 112 additions & 0 deletions Research/fix_mono.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import atexit

from .ffi import load_mono, ffi


__all__ = ["Mono"]


_MONO = None
_ROOT_DOMAIN = None


class Mono:
def __init__(self, libmono, domain=None, config_file=None):
self._assemblies = {}

initialize(config_file=config_file, libmono=libmono)

if domain is None:
self._domain = _ROOT_DOMAIN
else:
raise NotImplementedError

def get_callable(self, assembly_path, typename, function):
assembly = self._assemblies.get(assembly_path)
if not assembly:
assembly = _MONO.mono_domain_assembly_open(
self._domain, assembly_path.encode("utf8")
)
_check_result(assembly, f"Unable to load assembly {assembly_path}")
self._assemblies[assembly_path] = assembly

image = _MONO.mono_assembly_get_image(assembly)
_check_result(image, "Unable to load image from assembly")

desc = MethodDesc(typename, function)
method = desc.search(image)
_check_result(
method, f"Could not find method {typename}.{function} in assembly"
)

return MonoMethod(method)


class MethodDesc:
def __init__(self, typename, function):
self._desc = f"{typename}:{function}"
self._ptr = _MONO.mono_method_desc_new(
self._desc.encode("utf8"), 1 # include_namespace
)

def search(self, image):
return _MONO.mono_method_desc_search_in_image(self._ptr, image)

def __del__(self):
if _MONO:
_MONO.mono_method_desc_free(self._ptr)


class MonoMethod:
def __init__(self, ptr):
self._ptr = ptr

def __call__(self, ptr, size):
exception = ffi.new("MonoObject**")
params = ffi.new("void*[2]")

# Keep these alive until the function is called by assigning them locally
ptr_ptr = ffi.new("void**", ptr)
size_ptr = ffi.new("int32_t*", size)

params[0] = ptr_ptr
params[1] = size_ptr

res = _MONO.mono_runtime_invoke(self._ptr, ffi.NULL, params, exception)
_check_result(res, "Failed to call method")

unboxed = ffi.cast("int32_t*", _MONO.mono_object_unbox(res))
_check_result(unboxed, "Failed to convert result to int")

return unboxed[0]


def initialize(config_file: str, libmono: str) -> None:
global _MONO, _ROOT_DOMAIN
if _MONO is None:
_MONO = load_mono(libmono)

if config_file is None:
config_bytes = ffi.NULL
else:
config_bytes = config_file.encode("utf8")

_ROOT_DOMAIN = _MONO.mono_jit_init(b"clr_loader")

_MONO.mono_domain_set_config(_ROOT_DOMAIN, b"/etc/mono/", b"config");

_check_result(_ROOT_DOMAIN, "Failed to initialize Mono")
atexit.register(_release)


def _release():
global _MONO, _ROOT_DOMAIN
if _ROOT_DOMAIN is not None and _MONO is not None:
_MONO.mono_jit_cleanup(_ROOT_DOMAIN)
_MONO = None
_ROOT_DOMAIN = None


def _check_result(res, msg):
if res == ffi.NULL or not res:
raise RuntimeError(msg)