[Mono-devel-list] embedding mono with threads

eric lindvall eric at 5stops.com
Wed Jul 9 04:17:20 EDT 2003


I've had all sorts of troubles embedding mono in a world of pthreads.

The biggest problems i've found are that:

- I can't seem to run mono_jit_init() from a thread other than the "main
  thread" (see oxide-3)
- odd things start to happen when i use pthread_create() and then use
  mono_thread_attach() (see oxide-2)
- if mono_runtime_exec_managed_code() isn't used, nothing works right --
  I'm not quite sure how much and what I'm supposed to do with that call
  though.. once I call it, can I run everything else in a different
  thread?

I've found other smaller odd issues, like when i try to use
mono_runtime_invoke() on a MonoObject that is a TransparentProxy, it seems
to run in the default app domain, instead of the app domain of the object
that is being proxied.. but that's a story for another email. (using
mono_object_get_virtual_method() seemed to give me the correct object, but
i am not sure if it's the right answer).

I have included both a tarball of my source as well as included the main
.c files for reference.

I also included a managed parity of the unmanaged code: oxide-bin.exe.

The differences between the three unmaraged versions:

oxide-1: nothing is using pthreads

         result:
	   everything works.

oxide-2: initialization happens in the main thread, but the "request"
         happens in a pthread

         result:
	   Unhandled Exception: System.NullReferenceException: A null value was found where an object instance was required 
	   in [0x000e6] (at /home/eric/src/mono/mcs/class/System.Web/System.Web.Hosting/ApplicationHost.cs:62) 00 System.Web.Hosting.ApplicationHost:CreateApplicationHost (System.Type,string,string) 
	   in [0x0000c] (at /home/eric/src/oxide_test/managed/OxideApplicationHost.cs:46) 00 Fivestops.Oxide.OxideApplicationHost:CreateApplicationHost (string,string)

oxide-3: initialization attempts to happen in a pthread (and segfaults)

         result:
	   Segmentation fault

e.


-------------- next part --------------
A non-text attachment was scrubbed...
Name: oxide_test.tar.gz
Type: application/x-tar-gz
Size: 3037 bytes
Desc: not available
Url : http://lists.ximian.com/pipermail/mono-devel-list/attachments/20030709/bfea4da4/attachment.bin 
-------------- next part --------------

#include <sys/select.h>

#include <mono/jit/jit.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/threads.h>

#define DOCROOT "/home/eric/xsp/test"
#define DLL_LOCATION "oxide.dll"

static MonoDomain *g_domain;
static MonoAssembly *g_assembly;

static MonoObject *g_appHost;

void oxideHandler (void *p);

static char *
GetVariable (char *key)
{
    if (strcmp (key, "ntrans-base") == 0)
    {
        return (DOCROOT);
    }
    else if (strcmp (key, "HasInittedMono") == 0)
    {
        return (NULL); // nope
    }
    else if (strcmp (key, "uri") == 0)
    {
        return ("index.aspx");
    }
    else if (strcmp (key, "query") == 0)
    {
        return ("");
    }

    return (NULL);
}

static void
oxideSetupMono (gpointer userData)
{
    char *assemblyLocation = DLL_LOCATION;

    g_assembly = mono_domain_assembly_open (g_domain, assemblyLocation);

    if (g_assembly == NULL)
    {
        printf ("OXIDE: could not load assembly: %s\n", assemblyLocation);

        mono_environment_exitcode_set (-1);
    }
    else
    {
        printf ("OXIDE: loaded assembly: %s\n", assemblyLocation);

        mono_debug_init_2 (g_assembly);
        g_domain->entry_assembly = g_assembly;

        mono_environment_exitcode_set (0);
    }
}

void
oxideInit (void *p)
{
    int ret = 0;

    g_domain = mono_jit_init ("oxide-domain");

    if (g_domain == NULL)
    {
        printf ("OXIDE: could not initialize application domain.\n");
        
        return;
    }

    mono_debug_init (MONO_DEBUG_FORMAT_MONO);

    mono_runtime_exec_managed_code (g_domain, oxideSetupMono, NULL);
    
    ret = mono_environment_exitcode_get();

    printf ("OXIDE: exit code: %d\n", ret);

    if (ret == 0)
    {
        printf ("OXIDE: successfully started.\n");
    }
    else
    {
        mono_jit_cleanup (g_domain);

        printf ("OXIDE: error, could not start.\n");
    }
}

static MonoObject *
oxideAppHostSetup (char *virtualDir, char *baseDir)
{
    MonoMethodDesc *methodDesc;
    MonoMethod *method;
    MonoObject *appHost;
    gpointer params[2];

    methodDesc = mono_method_desc_new ("Fivestops.Oxide.OxideApplicationHost:CreateApplicationHost", TRUE);
    method = mono_method_desc_search_in_image (methodDesc, g_assembly->image);

    if (virtualDir != NULL && *virtualDir != 0)
        params[0] = mono_string_new (g_domain, virtualDir);
    else
        params[0] = NULL;

    if (baseDir != NULL && *baseDir != 0)
        params[1] = mono_string_new (g_domain, baseDir);
    else
        params[1] = NULL;

    appHost = mono_runtime_invoke (method, NULL, params, NULL);

    return (appHost);
}

static MonoObject *
oxideGetAppHost (char *virtualDir, char *baseDir)
{
    if (g_appHost == NULL)
    {
        g_appHost = oxideAppHostSetup (virtualDir, baseDir);
    }

    return (g_appHost);
}

static char *
oxideGetVirtualDir ()
{
    return ("/");
}

static char *
oxideProcessRequest (MonoObject *appHost, char *page, char *query)
{
    MonoMethodDesc *methodDesc;
    MonoMethod *method;
    MonoString *response;
    gpointer params[2];

    methodDesc = mono_method_desc_new ("Fivestops.Oxide.OxideApplicationHost:HandleRequest", TRUE);
    method = mono_method_desc_search_in_image (methodDesc, g_assembly->image);
    method = mono_object_get_virtual_method (appHost, method);

    printf ("OXIDE: HandleRequest: %p\n", method);

    if (method == NULL)
    {
        return ("(error)");
    }

    if (page != NULL && *page != 0)
        params[0] = mono_string_new (g_domain, page);
    else
        params[0] = NULL;

    if (query != NULL && *query != 0)
        params[1] = mono_string_new (g_domain, query);
    else
        params[1] = NULL;

    response = (MonoString *) mono_runtime_invoke (method, appHost, params, NULL);

    return (mono_string_to_utf8 (response));
}


void
oxideHandler (void *p)
{
    MonoObject *appHost;
    char *docRoot;
    char *response;
    void *hasInitted;

    docRoot = GetVariable ("ntrans-base");

    if (docRoot == NULL)
    {
        printf ("OXIDE: no docRoot defined\n");

        return;
    }

    hasInitted = GetVariable ("HasInittedMono");

    if (hasInitted == NULL)
    {
        printf ("OXIDE: calling mono_thread_attach (%p)\n", g_domain);

        mono_thread_attach (g_domain);
    }

    // appHost = oxideGetAppHost (oxideGetVirtualDir(), docRoot);
    appHost = oxideAppHostSetup (oxideGetVirtualDir(), docRoot);

    response = oxideProcessRequest (appHost, GetVariable ("uri"),
            GetVariable ("query"));

    printf ("response = \n%s\n", response);
}

int
main (int argc, char **argv)
{
    oxideInit (NULL);

    oxideHandler (NULL);

    return (0);
}
-------------- next part --------------

#include <sys/select.h>

#include <mono/jit/jit.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/threads.h>

#define DOCROOT "/home/eric/xsp/test"
#define DLL_LOCATION "oxide.dll"

static MonoDomain *g_domain;
static MonoAssembly *g_assembly;

static MonoObject *g_appHost;

void oxideHandler (void *p);

static char *
GetVariable (char *key)
{
    if (strcmp (key, "ntrans-base") == 0)
    {
        return (DOCROOT);
    }
    else if (strcmp (key, "HasInittedMono") == 0)
    {
        return (NULL); // nope
    }
    else if (strcmp (key, "uri") == 0)
    {
        return ("index.aspx");
    }
    else if (strcmp (key, "query") == 0)
    {
        return ("");
    }

    return (NULL);
}

static void
oxideSetupMono (gpointer userData)
{
    char *assemblyLocation = DLL_LOCATION;

    g_assembly = mono_domain_assembly_open (g_domain, assemblyLocation);

    if (g_assembly == NULL)
    {
        printf ("OXIDE: could not load assembly: %s\n", assemblyLocation);

        mono_environment_exitcode_set (-1);
    }
    else
    {
        printf ("OXIDE: loaded assembly: %s\n", assemblyLocation);

        mono_debug_init_2 (g_assembly);
        g_domain->entry_assembly = g_assembly;

        mono_environment_exitcode_set (0);
    }
}

void
oxideInit (void *p)
{
    int ret = 0;

    g_domain = mono_jit_init ("oxide-domain");

    if (g_domain == NULL)
    {
        printf ("OXIDE: could not initialize application domain.\n");
        
        return;
    }

    mono_debug_init (MONO_DEBUG_FORMAT_MONO);

    mono_runtime_exec_managed_code (g_domain, oxideSetupMono, NULL);
    
    ret = mono_environment_exitcode_get();

    printf ("OXIDE: exit code: %d\n", ret);

    if (ret == 0)
    {
        printf ("OXIDE: successfully started.\n");
    }
    else
    {
        mono_jit_cleanup (g_domain);

        printf ("OXIDE: error, could not start.\n");
    }
}

static MonoObject *
oxideAppHostSetup (char *virtualDir, char *baseDir)
{
    MonoMethodDesc *methodDesc;
    MonoMethod *method;
    MonoObject *appHost;
    gpointer params[2];

    methodDesc = mono_method_desc_new ("Fivestops.Oxide.OxideApplicationHost:CreateApplicationHost", TRUE);
    method = mono_method_desc_search_in_image (methodDesc, g_assembly->image);

    if (virtualDir != NULL && *virtualDir != 0)
        params[0] = mono_string_new (g_domain, virtualDir);
    else
        params[0] = NULL;

    if (baseDir != NULL && *baseDir != 0)
        params[1] = mono_string_new (g_domain, baseDir);
    else
        params[1] = NULL;

    appHost = mono_runtime_invoke (method, NULL, params, NULL);

    return (appHost);
}

static MonoObject *
oxideGetAppHost (char *virtualDir, char *baseDir)
{
    if (g_appHost == NULL)
    {
        g_appHost = oxideAppHostSetup (virtualDir, baseDir);
    }

    return (g_appHost);
}

static char *
oxideGetVirtualDir ()
{
    return ("/");
}

static char *
oxideProcessRequest (MonoObject *appHost, char *page, char *query)
{
    MonoMethodDesc *methodDesc;
    MonoMethod *method;
    MonoString *response;
    gpointer params[2];

    methodDesc = mono_method_desc_new ("Fivestops.Oxide.OxideApplicationHost:HandleRequest", TRUE);
    method = mono_method_desc_search_in_image (methodDesc, g_assembly->image);
    method = mono_object_get_virtual_method (appHost, method);

    printf ("OXIDE: HandleRequest: %p\n", method);

    if (method == NULL)
    {
        return ("(error)");
    }

    if (page != NULL && *page != 0)
        params[0] = mono_string_new (g_domain, page);
    else
        params[0] = NULL;

    if (query != NULL && *query != 0)
        params[1] = mono_string_new (g_domain, query);
    else
        params[1] = NULL;

    response = (MonoString *) mono_runtime_invoke (method, appHost, params, NULL);

    return (mono_string_to_utf8 (response));
}


void
oxideHandler (void *p)
{
    MonoObject *appHost;
    char *docRoot;
    char *response;
    void *hasInitted;

    docRoot = GetVariable ("ntrans-base");

    if (docRoot == NULL)
    {
        printf ("OXIDE: no docRoot defined\n");

        return;
    }

    hasInitted = GetVariable ("HasInittedMono");

    if (hasInitted == NULL)
    {
        printf ("OXIDE: calling mono_thread_attach (%p)\n", g_domain);

        mono_thread_attach (g_domain);
    }

    // appHost = oxideGetAppHost (oxideGetVirtualDir(), docRoot);
    appHost = oxideAppHostSetup (oxideGetVirtualDir(), docRoot);

    response = oxideProcessRequest (appHost, GetVariable ("uri"),
            GetVariable ("query"));

    printf ("response = \n%s\n", response);
}

int
main (int argc, char **argv)
{
    pthread_t thread2;

    oxideInit (NULL);

    pthread_create (&thread2, NULL, (void *(*)(void *)) oxideHandler, NULL);

    pthread_join (thread2, NULL);

    return (0);
}
-------------- next part --------------

#include <sys/select.h>

#include <mono/jit/jit.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/threads.h>

#define DOCROOT "/home/eric/xsp/test"
#define DLL_LOCATION "oxide.dll"

static MonoDomain *g_domain;
static MonoAssembly *g_assembly;

static MonoObject *g_appHost;

void oxideHandler (void *p);

static char *
GetVariable (char *key)
{
    if (strcmp (key, "ntrans-base") == 0)
    {
        return (DOCROOT);
    }
    else if (strcmp (key, "HasInittedMono") == 0)
    {
        return (NULL); // nope
    }
    else if (strcmp (key, "uri") == 0)
    {
        return ("index.aspx");
    }
    else if (strcmp (key, "query") == 0)
    {
        return ("");
    }

    return (NULL);
}

static void
oxideSetupMono (gpointer userData)
{
    char *assemblyLocation = DLL_LOCATION;

    g_assembly = mono_domain_assembly_open (g_domain, assemblyLocation);

    if (g_assembly == NULL)
    {
        printf ("OXIDE: could not load assembly: %s\n", assemblyLocation);

        mono_environment_exitcode_set (-1);
    }
    else
    {
        printf ("OXIDE: loaded assembly: %s\n", assemblyLocation);

        mono_debug_init_2 (g_assembly);
        g_domain->entry_assembly = g_assembly;

        mono_environment_exitcode_set (0);
    }
}

void
oxideInit (void *p)
{
    int ret = 0;

    g_domain = mono_jit_init ("oxide-domain");

    if (g_domain == NULL)
    {
        printf ("OXIDE: could not initialize application domain.\n");
        
        return;
    }

    mono_debug_init (MONO_DEBUG_FORMAT_MONO);

    mono_runtime_exec_managed_code (g_domain, oxideSetupMono, NULL);
    
    ret = mono_environment_exitcode_get();

    printf ("OXIDE: exit code: %d\n", ret);

    if (ret == 0)
    {
        printf ("OXIDE: successfully started.\n");
    }
    else
    {
        mono_jit_cleanup (g_domain);

        printf ("OXIDE: error, could not start.\n");
    }
}

static MonoObject *
oxideAppHostSetup (char *virtualDir, char *baseDir)
{
    MonoMethodDesc *methodDesc;
    MonoMethod *method;
    MonoObject *appHost;
    gpointer params[2];

    methodDesc = mono_method_desc_new ("Fivestops.Oxide.OxideApplicationHost:CreateApplicationHost", TRUE);
    method = mono_method_desc_search_in_image (methodDesc, g_assembly->image);

    if (virtualDir != NULL && *virtualDir != 0)
        params[0] = mono_string_new (g_domain, virtualDir);
    else
        params[0] = NULL;

    if (baseDir != NULL && *baseDir != 0)
        params[1] = mono_string_new (g_domain, baseDir);
    else
        params[1] = NULL;

    appHost = mono_runtime_invoke (method, NULL, params, NULL);

    return (appHost);
}

static MonoObject *
oxideGetAppHost (char *virtualDir, char *baseDir)
{
    if (g_appHost == NULL)
    {
        g_appHost = oxideAppHostSetup (virtualDir, baseDir);
    }

    return (g_appHost);
}

static char *
oxideGetVirtualDir ()
{
    return ("/");
}

static char *
oxideProcessRequest (MonoObject *appHost, char *page, char *query)
{
    MonoMethodDesc *methodDesc;
    MonoMethod *method;
    MonoString *response;
    gpointer params[2];

    methodDesc = mono_method_desc_new ("Fivestops.Oxide.OxideApplicationHost:HandleRequest", TRUE);
    method = mono_method_desc_search_in_image (methodDesc, g_assembly->image);
    method = mono_object_get_virtual_method (appHost, method);

    printf ("OXIDE: HandleRequest: %p\n", method);

    if (method == NULL)
    {
        return ("(error)");
    }

    if (page != NULL && *page != 0)
        params[0] = mono_string_new (g_domain, page);
    else
        params[0] = NULL;

    if (query != NULL && *query != 0)
        params[1] = mono_string_new (g_domain, query);
    else
        params[1] = NULL;

    response = (MonoString *) mono_runtime_invoke (method, appHost, params, NULL);

    return (mono_string_to_utf8 (response));
}


void
oxideHandler (void *p)
{
    MonoObject *appHost;
    char *docRoot;
    char *response;
    void *hasInitted;

    docRoot = GetVariable ("ntrans-base");

    if (docRoot == NULL)
    {
        printf ("OXIDE: no docRoot defined\n");

        return;
    }

    hasInitted = GetVariable ("HasInittedMono");

    if (hasInitted == NULL)
    {
        printf ("OXIDE: calling mono_thread_attach (%p)\n", g_domain);

        mono_thread_attach (g_domain);
    }

    // appHost = oxideGetAppHost (oxideGetVirtualDir(), docRoot);
    appHost = oxideAppHostSetup (oxideGetVirtualDir(), docRoot);

    response = oxideProcessRequest (appHost, GetVariable ("uri"),
            GetVariable ("query"));

    printf ("response = \n%s\n", response);
}

int
main (int argc, char **argv)
{
    pthread_t thread1;
    pthread_t thread2;

    pthread_create (&thread1, NULL, (void *(*)(void *)) oxideInit, NULL);

    pthread_join (thread1, NULL);

    pthread_create (&thread2, NULL, (void *(*)(void *)) oxideHandler, NULL);

    pthread_join (thread2, NULL);

    return (0);
}


More information about the Mono-devel-list mailing list