This patch adds new golang module in order to bootstrap Golang
applications on OSv. Its role is to pass golang shared object
specific main function name - GoMain - and terminate all lingering
Golang application threads once main thread completes.

To that end two new arguments with default values have been
added to application constructor and application::run* static methods
that allow overriding main function name and specifying post-main
function.

Finally this patch closes the list of patches necessary for basic
Golang support on OSv. It allows running simple apps like golang-example.

Fixes #850

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>

---
 core/app.cc               | 26 +++++++++++++++++++-------
 include/osv/app.hh        | 17 ++++++++++++++---
 modules/golang/.gitignore |  3 +++
 modules/golang/Makefile   | 34 ++++++++++++++++++++++++++++++++++
 modules/golang/go.cc      | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 117 insertions(+), 10 deletions(-)
 create mode 100644 modules/golang/.gitignore
 create mode 100644 modules/golang/Makefile
 create mode 100644 modules/golang/go.cc

diff --git a/core/app.cc b/core/app.cc
index d9a4c6a..dd055fa 100644
--- a/core/app.cc
+++ b/core/app.cc
@@ -125,9 +125,12 @@ shared_app_t application::run(const 
std::vector<std::string>& args)
 shared_app_t application::run(const std::string& command,
                       const std::vector<std::string>& args,
                       bool new_program,
-                      const std::unordered_map<std::string, std::string> *env)
+                      const std::unordered_map<std::string, std::string> *env,
+                      const std::string& main_function_name,
+                      std::function<void()> post_main)
 {
-    auto app = std::make_shared<application>(command, args, new_program, env);
+    auto app = std::make_shared<application>(command, args, new_program, env,
+                                             main_function_name, post_main);
     app->start();
     apps.push(app);
     return app;
@@ -137,9 +140,12 @@ shared_app_t application::run_and_join(const std::string& 
command,
                       const std::vector<std::string>& args,
                       bool new_program,
                       const std::unordered_map<std::string, std::string> *env,
-                      waiter* setup_waiter)
+                      waiter* setup_waiter,
+                      const std::string& main_function_name,
+                      std::function<void()> post_main)
 {
-    auto app = std::make_shared<application>(command, args, new_program, env);
+    auto app = std::make_shared<application>(command, args, new_program, env,
+                                             main_function_name, post_main);
     app->start_and_join(setup_waiter);
     return app;
 }
@@ -147,13 +153,16 @@ shared_app_t application::run_and_join(const std::string& 
command,
 application::application(const std::string& command,
                      const std::vector<std::string>& args,
                      bool new_program,
-                     const std::unordered_map<std::string, std::string> *env)
+                     const std::unordered_map<std::string, std::string> *env,
+                     const std::string& main_function_name,
+                     std::function<void()> post_main)
     : _args(args)
     , _command(command)
     , _termination_requested(false)
     , _runtime(new application_runtime(*this))
     , _joiner(nullptr)
     , _terminated(false)
+    , _post_main(post_main)
 {
     try {
         elf::program *current_program;
@@ -204,7 +213,7 @@ application::application(const std::string& command,
         throw launch_error("Failed to load object: " + command);
     }
 
-    _main = _lib->lookup<int (int, char**)>("main");
+    _main = _lib->lookup<int (int, char**)>(main_function_name.c_str());
     if (!_main) {
         _entry_point = reinterpret_cast<void(*)()>(_lib->entry_point());
     }
@@ -308,6 +317,10 @@ void application::main()
 
     if (_main) {
         run_main();
+
+        if(_post_main) {
+            _post_main();
+        }
     } else {
         // The application is expected not to initialize the environment in
         // which it runs on its owns but to call __libc_start_main(). If that's
@@ -317,7 +330,6 @@ void application::main()
         // vector.
         _entry_point();
     }
-
     // _entry_point() doesn't return
 }
 
diff --git a/include/osv/app.hh b/include/osv/app.hh
index 73a1b38..1f4d2d6 100644
--- a/include/osv/app.hh
+++ b/include/osv/app.hh
@@ -98,12 +98,16 @@ public:
      * \param args Parameters which will be passed to program's main().
      * \param new_program true if a new elf namespace must be started
      * \param env pointer to an unordered_map than will be merged in current 
env
+     * \param main_function_name name of the main function - the default is 
'main'
+     * \param post_main optional function to be executed after program's 
main() ends
      * \throw launch_error
      */
     static shared_app_t run(const std::string& command,
             const std::vector<std::string>& args,
             bool new_program = false,
-            const std::unordered_map<std::string, std::string> *env = nullptr);
+            const std::unordered_map<std::string, std::string> *env = nullptr,
+            const std::string& main_function_name = "main",
+            std::function<void()> post_main = nullptr);
 
     static void join_all() {
         apps.join();
@@ -112,7 +116,9 @@ public:
     application(const std::string& command,
             const std::vector<std::string>& args,
             bool new_program = false,
-            const std::unordered_map<std::string, std::string> *env = nullptr);
+            const std::unordered_map<std::string, std::string> *env = nullptr,
+            const std::string& main_function_name = "main",
+            std::function<void()> post_main = nullptr);
 
     ~application();
 
@@ -136,13 +142,17 @@ public:
      * \param args Parameters which will be passed to program's main().
      * \param new_program true if a new elf namespace must be started
      * \param env pointer to an unordered_map than will be merged in current 
env
+     * \param main_function_name name of the main function - the default is 
'main'
+     * \param post_main optional function to be executed after program's 
main() ends
      * \throw launch_error
      */
     static shared_app_t run_and_join(const std::string& command,
             const std::vector<std::string>& args,
             bool new_program = false,
             const std::unordered_map<std::string, std::string> *env = nullptr,
-            waiter* setup_waiter = nullptr);
+            waiter* setup_waiter = nullptr,
+            const std::string& main_function_name = "main",
+            std::function<void()> post_main = nullptr);
 
     /**
      * Installs a termination callback which will be called when
@@ -243,6 +253,7 @@ private:
     std::shared_ptr<application_runtime> _runtime;
     sched::thread* _joiner;
     std::atomic<bool> _terminated;
+    std::function<void()> _post_main;
     friend struct application_runtime;
 };
 
diff --git a/modules/golang/.gitignore b/modules/golang/.gitignore
new file mode 100644
index 0000000..96b135a
--- /dev/null
+++ b/modules/golang/.gitignore
@@ -0,0 +1,3 @@
+*.so
+obj/*
+*.manifest
diff --git a/modules/golang/Makefile b/modules/golang/Makefile
new file mode 100644
index 0000000..3ffe168
--- /dev/null
+++ b/modules/golang/Makefile
@@ -0,0 +1,34 @@
+INCLUDES = -I. -I../../include -I../../arch/$(ARCH) -I../.. \
+        -I../../build/$(mode)/gen/include
+
+autodepend = -MD -MT $@ -MP
+CXXFLAGS  = -g -rdynamic -Wall -std=c++11 -fPIC $(INCLUDES) $(autodepend)
+
+# the build target executable:
+TARGET = go
+CPP_FILES := $(TARGET).cc
+OBJ_FILES := $(addprefix obj/,$(CPP_FILES:.cc=.o))
+DEPS := $(OBJ_FILES:.o=.d)
+
+LIBS = -lpthread
+
+quiet = $(if $V, $1, @echo " $2"; $1)
+very-quiet = $(if $V, $1, @$1)
+
+$(TARGET).so: $(OBJ_FILES)
+       $(call quiet, $(CXX) $(CXXFLAGS) -shared -o $(TARGET).so $^ $(LIBS), 
LINK $@)
+
+obj/%.o: %.cc
+       $(call quiet, $(CXX) $(CXXFLAGS) -c -o $@ $<, CXX $@)
+
+init:
+       @echo "  MKDIRS"
+       $(call very-quiet, mkdir -p obj)
+.PHONY: init
+
+module: init $(TARGET).so
+       echo '/go.so: $${MODULE_DIR}/go.so' > usr.manifest
+
+clean:
+       rm -f $(TARGET)*.so usr.manifest
+       $(call very-quiet, $(RM) -rf obj)
diff --git a/modules/golang/go.cc b/modules/golang/go.cc
new file mode 100644
index 0000000..0bb1986
--- /dev/null
+++ b/modules/golang/go.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+#include <osv/app.hh>
+
+// This tiny app acts as a front-end to Golang applications running on OSv.
+// In essence it starts new application to execute specified Golang shared
+// object. The spawn application is setup to terminate all remaining Golang
+// runtime threads after main thread exits.
+
+using namespace osv;
+
+// As described in https://github.com/golang/go/issues/11100 some
+// Golang runtime threads do not terminate automatically when
+// main thread exits. This is very similar to JVM shutdown
+// behavior.
+// The stop_all_remaining_golang_threads() function stops
+// all threads associated with the new Golang app started and
+// gets passed as post-main function below.
+// This call is unsafe, in the sense we assume that those
+// renegade threads are not holding any critical resources
+// (e.g., not in the middle of I/O or memory allocation).
+static void stop_all_remaining_golang_threads()
+{
+    while(!application::unsafe_stop_and_abandon_other_threads()) {
+        usleep(100000);
+    }
+}
+
+extern "C"
+int main(int argc, char **argv)
+{
+    const std::string go_program(argv[1]);
+
+    std::vector<std::string> go_program_arguments;
+    for(auto i = 2; i < argc; i++ )
+        go_program_arguments.push_back(argv[i]);
+    //
+    // Setup new app with Golang specific main function name - GoMain - and
+    // post main function that will terminate all remaining Golang runtime 
threads
+    auto app = application::run(go_program, go_program_arguments, false, 
nullptr,
+                                "GoMain", stop_all_remaining_golang_threads);
+    return app->join();
+}
-- 
2.7.4

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to