Extend the Unix hosting API
authorJan Vorlicek <janvorli@microsoft.com>
Wed, 15 Jul 2015 21:08:09 +0000 (23:08 +0200)
committerJan Vorlicek <janvorli@microsoft.com>
Thu, 16 Jul 2015 19:47:36 +0000 (21:47 +0200)
This change modifies the Unix hosting API so that the hosting app can create
as many managed delegates as it needs and execute them as many times it wants.
The new API contains separate functions to initialize and shutdown CoreCLR
and a function to create a delegate.
The current ExecuteAssembly function behavior stays unmodified for now to
ensure that dnx that uses that API and that pulls the binary libcoreclr
is not broken.
After the dnx is updated to use the new coreclr_create_delegate API, I'll remove
the ExecuteAssembly.

Also done:
1) Added support for comments and skipping empty lines in the mscorwks_unixexports.src.
2) Restructured the mscorwks_unixexports.src
3) Added coreclr_execute_assembly to the unixinterface.cpp / exports
4) Modified coreruncommon.cpp to use the new hosting API

CMakeLists.txt
generateexportedsymbols.awk
generateversionscript.awk
src/coreclr/hosts/unixcoreruncommon/coreruncommon.cpp
src/dlls/mscoree/mscorwks_unixexports.src
src/dlls/mscoree/unixinterface.cpp
src/dlls/mscorrc/CMakeLists.txt

index f7872e0..81fd930 100644 (file)
@@ -121,7 +121,7 @@ function(generate_exports_file inputFilename outputFilename)
   add_custom_command(
     OUTPUT ${outputFilename}
     COMMAND ${AWK} -f ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT} ${inputFilename} >${outputFilename}
-    DEPENDS ${inputFilename}
+    DEPENDS ${inputFilename} ${CMAKE_SOURCE_DIR}/${AWK_SCRIPT}
     COMMENT "Generating exports file ${outputFilename}"
   )
   set_source_files_properties(${outputFilename}
index 19312c3..1d9b200 100644 (file)
@@ -2,5 +2,10 @@
        # Remove the CR character in case the sources are mapped from
        # a Windows share and contain CRLF line endings
        gsub(/\r/,"", $0);
-       print "_"  $0;
+
+       # Skip empty lines and comment lines starting with semicolon
+       if (NF && !match($0, /^[:space:]*;/))
+       {
+               print "_"  $0;
+       }
 } 
index 2aa1f4f..d1c294b 100644 (file)
@@ -6,7 +6,12 @@ BEGIN {
        # Remove the CR character in case the sources are mapped from
        # a Windows share and contain CRLF line endings
        gsub(/\r/,"", $0);
-       print "        "  $0 ";";
+       
+       # Skip empty lines and comment lines starting with semicolon
+       if (NF && !match($0, /^[:space:]*;/))
+       {
+               print "        "  $0 ";";
+       }
 } 
 END {
        print "    local: *;"
index 4f6ffa0..ecbbf39 100644 (file)
@@ -24,29 +24,31 @@ static const char * const coreClrDll = "libcoreclr.dylib";
 static const char * const coreClrDll = "libcoreclr.so";
 #endif
 
-// Windows types used by the ExecuteAssembly function
-typedef unsigned int DWORD;
-typedef const char16_t* LPCWSTR;
-typedef const char* LPCSTR;
-typedef int32_t HRESULT;
-
-#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)
-
-// Prototype of the ExecuteAssembly function from the libcoreclr.do
-typedef HRESULT (*ExecuteAssemblyFunction)(
-                    LPCSTR exePath,
-                    LPCSTR coreClrPath,
-                    LPCSTR appDomainFriendlyName,
-                    int propertyCount,
-                    LPCSTR* propertyKeys,
-                    LPCSTR* propertyValues,
-                    int argc,
-                    LPCSTR* argv,
-                    LPCSTR managedAssemblyPath,
-                    LPCSTR entryPointAssemblyName,
-                    LPCSTR entryPointTypeName,
-                    LPCSTR entryPointMethodsName,
-                    DWORD* exitCode);
+#define SUCCEEDED(Status) ((Status) >= 0)
+
+// Prototype of the coreclr_initialize function from the libcoreclr.so
+typedef int (*InitializeCoreCLRFunction)(
+            const char* exePath,
+            const char* appDomainFriendlyName,
+            int propertyCount,
+            const char** propertyKeys,
+            const char** propertyValues,
+            void** hostHandle,
+            unsigned int* domainId);
+
+// Prototype of the coreclr_shutdown function from the libcoreclr.so
+typedef int (*ShutdownCoreCLRFunction)(
+            void* hostHandle,
+            unsigned int domainId);
+
+// Prototype of the coreclr_execute_assembly function from the libcoreclr.so
+typedef int (*ExecuteAssemblyFunction)(
+            void* hostHandle,
+            unsigned int domainId,
+            int argc,
+            const char** argv,
+            const char* managedAssemblyPath,
+            unsigned int* exitCode);
 
 bool GetAbsolutePath(const char* path, std::string& absolutePath)
 {
@@ -233,8 +235,23 @@ int ExecuteManagedAssembly(
     void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL);
     if (coreclrLib != nullptr)
     {
-        ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "ExecuteAssembly");
-        if (executeAssembly != nullptr)
+        InitializeCoreCLRFunction initializeCoreCLR = (InitializeCoreCLRFunction)dlsym(coreclrLib, "coreclr_initialize");
+        ExecuteAssemblyFunction executeAssembly = (ExecuteAssemblyFunction)dlsym(coreclrLib, "coreclr_execute_assembly");
+        ShutdownCoreCLRFunction shutdownCoreCLR = (ShutdownCoreCLRFunction)dlsym(coreclrLib, "coreclr_shutdown");
+
+        if (initializeCoreCLR == nullptr)
+        {
+            fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n");
+        }
+        else if (executeAssembly == nullptr)
+        {
+            fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n");
+        }
+        else if (shutdownCoreCLR == nullptr)
+        {
+            fprintf(stderr, "Function coreclr_shutdown not found in the libcoreclr.so\n");
+        }
+        else
         {
             // Allowed property names:
             // APPBASE
@@ -272,30 +289,46 @@ int ExecuteManagedAssembly(
                 "UseLatestBehaviorWhenTFMNotSpecified"
             };
 
-            HRESULT st = executeAssembly(
-                            currentExeAbsolutePath,
-                            coreClrDllPath.c_str(),
-                            "unixcorerun",
-                            sizeof(propertyKeys) / sizeof(propertyKeys[0]),
-                            propertyKeys,
-                            propertyValues,
-                            managedAssemblyArgc,
-                            managedAssemblyArgv,
-                            managedAssemblyAbsolutePath,
-                            NULL,
-                            NULL,
-                            NULL,
-                            (DWORD*)&exitCode);
+            void* hostHandle;
+            unsigned int domainId;
+
+            int st = initializeCoreCLR(
+                        currentExeAbsolutePath, 
+                        "unixcorerun", 
+                        sizeof(propertyKeys) / sizeof(propertyKeys[0]), 
+                        propertyKeys, 
+                        propertyValues, 
+                        &hostHandle, 
+                        &domainId);
 
             if (!SUCCEEDED(st))
             {
-                fprintf(stderr, "ExecuteAssembly failed - status: 0x%08x\n", st);
+                fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st);
                 exitCode = -1;
             }
-        }
-        else
-        {
-            fprintf(stderr, "Function ExecuteAssembly not found in the libcoreclr.so\n");
+            else 
+            {
+                st = executeAssembly(
+                        hostHandle,
+                        domainId,
+                        managedAssemblyArgc,
+                        managedAssemblyArgv,
+                        managedAssemblyAbsolutePath,
+                        (unsigned int*)&exitCode);
+
+                if (!SUCCEEDED(st))
+                {
+                    fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st);
+                    exitCode = -1;
+                }
+
+                st = shutdownCoreCLR(hostHandle, domainId);
+                if (!SUCCEEDED(st))
+                {
+                    fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st);
+                    exitCode = -1;
+                }
+            }
         }
 
         if (dlclose(coreclrLib) != 0)
index 81a99d6..e96b25a 100644 (file)
@@ -1,7 +1,30 @@
+; Unix hosting API
+coreclr_create_delegate
+coreclr_execute_assembly
+coreclr_initialize
+coreclr_shutdown
+
+; Obsolete Unix hosting API, to be removed
+ExecuteAssembly
+
+; PAL initialization and module registration
+PAL_InitializeCoreCLR 
+PAL_RegisterModule
+PAL_UnregisterModule
+
+; Functions exported by the coreclr
+CoreDllMain
+DllMain
+GetCLRRuntimeHost
+
+; Functions used by CoreFX
+EnsureOpenSslInitialized
+ForkAndExecProcess
+
+; Win32 API and other PAL functions used by the mscorlib
 CloseHandle
 CoCreateGuid
 CopyFileW
-CoreDllMain
 CoTaskMemAlloc
 CoTaskMemFree
 CreateDirectoryW
@@ -10,19 +33,14 @@ CreateFileW
 CreateMutexW
 CreateSemaphoreW
 DeleteFileW
-DllMain
 DuplicateHandle
-EnsureOpenSslInitialized
-ExecuteAssembly
 FindClose
 FindFirstFileW
 FindNextFileW
 FlushFileBuffers
-ForkAndExecProcess
 FormatMessageW
 FreeEnvironmentStringsW
 GetACP
-GetCLRRuntimeHost
 GetConsoleCP
 GetConsoleOutputCP
 GetCurrentDirectoryW
@@ -53,10 +71,7 @@ MultiByteToWideChar
 OpenEventW
 OpenMutexW
 OpenSemaphoreW
-PAL_InitializeCoreCLR 
 PAL_Random
-PAL_RegisterModule
-PAL_UnregisterModule
 QueryPerformanceCounter  
 QueryPerformanceFrequency
 RaiseException
index 2141917..5c5e1da 100644 (file)
@@ -88,48 +88,30 @@ static LPCWSTR* StringArrayToUnicode(int argc, LPCSTR* argv)
 }
 
 //
-// Execute a managed assembly with given arguments
+// Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain
 //
 // Parameters:
 //  exePath                 - Absolute path of the executable that invoked the ExecuteAssembly
-//  coreClrPath             - Absolute path of the libcoreclr.so
 //  appDomainFriendlyName   - Friendly name of the app domain that will be created to execute the assembly
 //  propertyCount           - Number of properties (elements of the following two arguments)
 //  propertyKeys            - Keys of properties of the app domain
 //  propertyValues          - Values of properties of the app domain
-//  argc                    - Number of arguments passed to the executed assembly
-//  argv                    - Array of arguments passed to the executed assembly
-//  managedAssemblyPath     - Path of the managed assembly to execute (or NULL if using a custom entrypoint).
-//  enntyPointAssemblyName  - Name of the assembly which holds the custom entry point (or NULL to use managedAssemblyPath).
-//  entryPointTypeName      - Name of the type which holds the custom entry point (or NULL to use managedAssemblyPath).
-//  entryPointMethodName    - Name of the method which is the custom entry point (or NULL to use managedAssemblyPath).
-//  exitCode                - Exit code returned by the executed assembly
+//  hostHandle              - Output parameter, handle of the created host
+//  domainId                - Output parameter, id of the created app domain 
 //
 // Returns:
 //  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
 //
 extern "C"
-HRESULT ExecuteAssembly(
-            LPCSTR exePath,
-            LPCSTR coreClrPath,
-            LPCSTR appDomainFriendlyName,
+int coreclr_initialize(
+            const char* exePath,
+            const char* appDomainFriendlyName,
             int propertyCount,
-            LPCSTR* propertyKeys,
-            LPCSTR* propertyValues,
-            int argc,
-            LPCSTR* argv,
-            LPCSTR managedAssemblyPath,
-            LPCSTR entryPointAssemblyName,
-            LPCSTR entryPointTypeName,
-            LPCSTR entryPointMethodName,
-            DWORD* exitCode)
+            const char** propertyKeys,
+            const char** propertyValues,
+            void** hostHandle,
+            unsigned int* domainId)
 {
-    if (exitCode == NULL)
-    {
-        return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
-    }
-    *exitCode = -1;
-
     DWORD error = PAL_InitializeCoreCLR(exePath);
     HRESULT hr = HRESULT_FROM_WIN32(error);
 
@@ -161,8 +143,6 @@ HRESULT ExecuteAssembly(
     ConstWStringArrayHolder propertyValuesW;
     propertyValuesW.Set(StringArrayToUnicode(propertyCount, propertyValues), propertyCount);
 
-    DWORD domainId;
-
     hr = host->CreateAppDomainWithManager(
         appDomainFriendlyNameW,
         // Flags:
@@ -187,7 +167,172 @@ HRESULT ExecuteAssembly(
         propertyCount,
         propertyKeysW,
         propertyValuesW,
-        &domainId);
+        domainId);
+
+    if (SUCCEEDED(hr))
+    {
+        host.SuppressRelease();
+        *hostHandle = host;
+    }
+
+    return hr;
+}
+
+//
+// Shutdown CoreCLR. It unloads the app domain and stops the CoreCLR host.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain 
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+extern "C"
+int coreclr_shutdown(
+            void* hostHandle,
+            unsigned int domainId)
+{
+    ReleaseHolder<ICLRRuntimeHost2> host(reinterpret_cast<ICLRRuntimeHost2*>(hostHandle));
+    HRESULT hr = host->UnloadAppDomain(domainId,
+                                       true); // Wait until done
+    IfFailRet(hr);
+
+    hr = host->Stop();
+
+    // The PAL_Terminate is not called here since it would terminate the current process.
+
+    return hr;
+}
+
+//
+// Create a native callable delegate for a managed method.
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain 
+//  entryPointAssemblyName  - Name of the assembly which holds the custom entry point
+//  entryPointTypeName      - Name of the type which holds the custom entry point
+//  entryPointMethodName    - Name of the method which is the custom entry point
+//  delegate                - Output parameter, the function stores a pointer to the delegate at the specified address
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+extern "C"
+int coreclr_create_delegate(
+            void* hostHandle,
+            unsigned int domainId,
+            const char* entryPointAssemblyName,
+            const char* entryPointTypeName,
+            const char* entryPointMethodName,
+            void** delegate)
+{
+    ICLRRuntimeHost2* host = reinterpret_cast<ICLRRuntimeHost2*>(hostHandle);
+
+    ConstWStringHolder entryPointAssemblyNameW = StringToUnicode(entryPointAssemblyName);
+    ConstWStringHolder entryPointTypeNameW = StringToUnicode(entryPointTypeName);
+    ConstWStringHolder entryPointMethodNameW = StringToUnicode(entryPointMethodName);
+
+    HRESULT hr = host->CreateDelegate(
+                            domainId,
+                            entryPointAssemblyNameW,
+                            entryPointTypeNameW,
+                            entryPointMethodNameW,
+                            (INT_PTR*)delegate);
+    
+    return hr;
+}
+
+//
+// Execute a managed assembly with given arguments
+//
+// Parameters:
+//  hostHandle              - Handle of the host
+//  domainId                - Id of the domain 
+//  argc                    - Number of arguments passed to the executed assembly
+//  argv                    - Array of arguments passed to the executed assembly
+//  managedAssemblyPath     - Path of the managed assembly to execute (or NULL if using a custom entrypoint).
+//  exitCode                - Exit code returned by the executed assembly
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+extern "C"
+int coreclr_execute_assembly(
+            void* hostHandle,
+            unsigned int domainId,
+            int argc,
+            const char** argv,
+            const char* managedAssemblyPath,
+            unsigned int* exitCode)
+{
+    if (exitCode == NULL)
+    {
+        return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
+    }
+    *exitCode = -1;
+
+    ICLRRuntimeHost2* host = reinterpret_cast<ICLRRuntimeHost2*>(hostHandle);
+
+    ConstWStringArrayHolder argvW;
+    argvW.Set(StringArrayToUnicode(argc, argv), argc);
+    
+    ConstWStringHolder managedAssemblyPathW = StringToUnicode(managedAssemblyPath);
+
+    HRESULT hr = host->ExecuteAssembly(domainId, managedAssemblyPathW, argc, argvW, exitCode);
+    IfFailRet(hr);
+
+    return hr;
+}
+
+//
+// Execute a managed assembly with given arguments
+//
+// Parameters:
+//  exePath                 - Absolute path of the executable that invoked the ExecuteAssembly
+//  coreClrPath             - Absolute path of the libcoreclr.so
+//  appDomainFriendlyName   - Friendly name of the app domain that will be created to execute the assembly
+//  propertyCount           - Number of properties (elements of the following two arguments)
+//  propertyKeys            - Keys of properties of the app domain
+//  propertyValues          - Values of properties of the app domain
+//  argc                    - Number of arguments passed to the executed assembly
+//  argv                    - Array of arguments passed to the executed assembly
+//  managedAssemblyPath     - Path of the managed assembly to execute (or NULL if using a custom entrypoint).
+//  enntyPointAssemblyName  - Name of the assembly which holds the custom entry point (or NULL to use managedAssemblyPath).
+//  entryPointTypeName      - Name of the type which holds the custom entry point (or NULL to use managedAssemblyPath).
+//  entryPointMethodName    - Name of the method which is the custom entry point (or NULL to use managedAssemblyPath).
+//  exitCode                - Exit code returned by the executed assembly
+//
+// Returns:
+//  HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
+//
+extern "C"
+HRESULT ExecuteAssembly(
+            LPCSTR exePath,
+            LPCSTR coreClrPath,
+            LPCSTR appDomainFriendlyName,
+            int propertyCount,
+            LPCSTR* propertyKeys,
+            LPCSTR* propertyValues,
+            int argc,
+            LPCSTR* argv,
+            LPCSTR managedAssemblyPath,
+            LPCSTR entryPointAssemblyName,
+            LPCSTR entryPointTypeName,
+            LPCSTR entryPointMethodName,
+            DWORD* exitCode)
+{
+    if (exitCode == NULL)
+    {
+        return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
+    }
+    *exitCode = -1;
+
+    ReleaseHolder<ICLRRuntimeHost2> host; //(reinterpret_cast<ICLRRuntimeHost2*>(hostHandle));
+    DWORD domainId;
+
+    HRESULT hr = coreclr_initialize(exePath, appDomainFriendlyName, propertyCount, propertyKeys, propertyValues, &host, &domainId);
     IfFailRet(hr);
 
     ConstWStringArrayHolder argvW;
@@ -219,14 +364,8 @@ HRESULT ExecuteAssembly(
 
         *exitCode = pHostMain(argc, argvW);
     }
-    
-    hr = host->UnloadAppDomain(domainId,
-                               true); // Wait until done
-    IfFailRet(hr);
-
-    hr = host->Stop();
 
-    // The PAL_Terminate is not called here since it would terminate the current process.
+    hr = coreclr_shutdown(host, domainId);
 
     return hr;
 }
index 198fe8d..e139bc3 100644 (file)
@@ -28,7 +28,7 @@ else()
             COMMAND ${CMAKE_CXX_COMPILER} -E -P ${PREPROCESS_DEFINITIONS} ${INCLUDE_DIRECTORIES} -o ${PREPROCESSED_SOURCE} -x c ${SOURCE}
             # Convert the preprocessed .rc file to a C++ file which will be used to make a static lib.
             COMMAND ${AWK} -f ${RC_TO_CPP} -f ${PROCESS_RC} ${PREPROCESSED_SOURCE} >${RESOURCE_ENTRY_ARRAY_CPP}
-            DEPENDS ${SOURCE}
+            DEPENDS ${SOURCE} ${RC_TO_CPP} ${PROCESS_RC}
         )
 
         include_directories(${RESOURCE_STRING_HEADER_DIR})