commit 01a2447e67a854e1aa962bfbabdf10e223906600 (HEAD -> knielsen_my_context, my/knielsen_async_api)
Author: Kristian Nielsen <knielsen@knielsen-hq.org>
Date:   Thu Sep 5 13:56:44 2024 +0200

    Implement boost::context as a fallback for non-blocking API support
    
    The non-blocking API has native (assembler) implementations for x86_64,
    i386, and (with recent patch) aarch64; these implementations are the most
    efficient. For other architectures, a fallback to ucontext is supported.
    
    But ucontext is not the most efficient, and it is not available on all
    platforms (it has been deprecated in POSIX). The boost::context library
    provides an alternative fallback that is available on more architectures and
    should be more efficient than ucontext (if still not quite as fast as the
    native support).
    
    This patch adds boost::context as a dependency of libmariadb that provides a
    fallback on non-natively supported architectures and is preferred over
    ucontext, when both are available.
    
    One problem with this patch is that it introduces a dependency on C++ and on
    the boost library. This is rather intrusive on a library that is otherwise
    pure C with few dependencies. It would be preferable to avoid this
    dependency when it is not needed. Maybe boost::context should be an optional
    feature enabled by the user when running cmake, and some cmake magic used to
    not introduce the C++ dependency into the project unless explicitly
    requested by the user.
    
    Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a3bea923..01f35548 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,7 +16,7 @@ IF(COMMAND CMAKE_POLICY)
 ENDIF()
 
 
-PROJECT(mariadb-connector-c C)
+PROJECT(mariadb-connector-c LANGUAGES C CXX)
 
 # Is C/C built as subproject?
 get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)
@@ -361,6 +361,9 @@ IF(NOT WITH_SSL STREQUAL "OFF")
   MARK_AS_ADVANCED(SSL_SOURCES)
 ENDIF()
 
+FIND_PACKAGE(Boost 1.40 COMPONENTS context REQUIRED)
+SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${Boost_LIBRARIES})
+
 SET(ENABLED_LOCAL_INFILE "AUTO" CACHE STRING "If we should should enable LOAD DATA LOCAL by default (OFF/ON/AUTO)")
 MARK_AS_ADVANCED(ENABLED_LOCAL_INFILE)
 IF (ENABLED_LOCAL_INFILE MATCHES "^(0|FALSE)$")
diff --git a/cmake/check_include_files.cmake b/cmake/check_include_files.cmake
index d8bbffcd..3e093364 100644
--- a/cmake/check_include_files.cmake
+++ b/cmake/check_include_files.cmake
@@ -43,6 +43,7 @@ CHECK_INCLUDE_FILES (sys/types.h HAVE_SYS_TYPES_H)
 CHECK_INCLUDE_FILES (sys/stat.h HAVE_SYS_STAT_H)
 CHECK_INCLUDE_FILES (sys/un.h HAVE_SYS_UN_H)
 CHECK_INCLUDE_FILES (unistd.h HAVE_UNISTD_H)
+CHECK_INCLUDE_FILE_CXX (boost/fiber/context.hpp HAVE_BOOST_CONTEXT_H)
 
 IF(APPLE)
   SET(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=600)
diff --git a/include/ma_config.h.in b/include/ma_config.h.in
index 4cabfef3..bbe4fb8e 100644
--- a/include/ma_config.h.in
+++ b/include/ma_config.h.in
@@ -28,6 +28,7 @@
 #cmakedefine HAVE_SYS_UN_H 1
 #cmakedefine HAVE_UNISTD_H 1
 #cmakedefine HAVE_UCONTEXT_H 1
+#cmakedefine HAVE_BOOST_CONTEXT_H 1
 
 /*
  * function definitions - processed in LibmysqlFunctions.txt 
diff --git a/include/ma_context.h b/include/ma_context.h
index 2c4ab10a..4c3f0d8d 100644
--- a/include/ma_context.h
+++ b/include/ma_context.h
@@ -34,12 +34,18 @@
 #define MY_CONTEXT_USE_I386_GCC_ASM
 #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__aarch64__)
 #define MY_CONTEXT_USE_AARCH64_GCC_ASM
+#elif defined(HAVE_BOOST_CONTEXT_H)
+#define MY_CONTEXT_USE_BOOST_CONTEXT
 #elif defined(HAVE_UCONTEXT_H)
 #define MY_CONTEXT_USE_UCONTEXT
 #else
 #define MY_CONTEXT_DISABLE
 #endif
 
+#ifdef   __cplusplus
+extern "C" {
+#endif
+
 #ifdef MY_CONTEXT_USE_WIN32_FIBERS
 struct my_context {
   void (*user_func)(void *);
@@ -126,6 +132,32 @@ struct my_context {
 #endif
 
 
+#ifdef MY_CONTEXT_USE_BOOST_CONTEXT
+/*
+  boost::context is a C++-library that provides a portable co-routine fallback
+  for architectures that lack a native my_context implementation, and which is
+  available on some platforms where ucontext is not (ucontext has been
+  deprecated in Posix).
+
+  Since boost::context is in C++, the implementation details must be put into
+  a separate source file ma_boost_context.cc and hidden in an opaque void *.
+*/
+struct my_context {
+  /* Pointer to state that uses C++-types and cannot be compiled in C. */
+  void *internal_context;
+  void *stack;
+  size_t stack_size;
+#ifndef DBUG_OFF
+  void *dbug_state;
+#endif
+  int active;
+#ifdef HAVE_VALGRIND
+  unsigned int valgrind_stack_id;
+#endif
+};
+#endif /* MY_CONTEXT_USE_BOOST_CONTEXT */
+
+
 #ifdef MY_CONTEXT_DISABLE
 struct my_context {
   int dummy;
@@ -259,3 +291,7 @@ struct mysql_async_context {
   */
   struct my_context async_context;
 };
+
+#ifdef   __cplusplus
+}
+#endif
diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt
index 852be8dc..da287762 100644
--- a/libmariadb/CMakeLists.txt
+++ b/libmariadb/CMakeLists.txt
@@ -393,7 +393,7 @@ IF(WITH_DYNCOL)
   SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_dyncol.c)
 ENDIF()
 
-SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_async.c ma_context.c)
+SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} mariadb_async.c ma_context.c ma_boost_context.cc)
 SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} ${MARIADB_NONBLOCK_SYMBOLS})
 
 INCLUDE(${CC_SOURCE_DIR}/cmake/export.cmake)
diff --git a/libmariadb/ma_boost_context.cc b/libmariadb/ma_boost_context.cc
new file mode 100644
index 00000000..cb7625d9
--- /dev/null
+++ b/libmariadb/ma_boost_context.cc
@@ -0,0 +1,117 @@
+#include "ma_config.h"
+#include "mysql.h"
+#include "ma_context.h"
+
+#ifdef MY_CONTEXT_USE_BOOST_CONTEXT
+
+#include <boost/fiber/context.hpp>
+
+namespace ctx=boost::context;
+
+struct my_context_intern {
+  ctx::fiber parent, coro;
+
+  void *stack_top(const my_context *c) {
+    return (unsigned char *)(c->stack) + c->stack_size;
+  }
+
+  /* A StackAlloc for ctx::fiber that reuses our stack. */
+  struct my_stack_alloc {
+    typedef ctx::stack_traits traits_type;
+    my_context *c;
+    my_stack_alloc(my_context *c_arg) : c(c_arg) { };
+    ctx::stack_context allocate() {
+      ctx::stack_context sctx;
+      sctx.size= c->stack_size;
+      sctx.sp= ((my_context_intern *)c->internal_context)->stack_top(c);
+#if defined(BOOST_USE_VALGRIND) && defined(HAVE_VALGRIND)
+      sctx.valgrind_stack_id= c->valgrind_stack_id;
+#endif
+      return sctx;
+    }
+    void deallocate(ctx::stack_context & sctx) {
+      /* Empty, we will re-use the stack. */
+    }
+  };
+};
+
+
+extern "C"
+int
+my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
+{
+  my_context_intern *ci= (my_context_intern *)c->internal_context;
+  ci->coro= ctx::fiber(std::allocator_arg, my_context_intern::my_stack_alloc(c),
+                       [c, f, d](ctx::fiber && parent) {
+      my_context_intern *ci= (my_context_intern *)c->internal_context;
+      ci->parent= std::move(parent);
+      (*f)(d);
+      c->active= 0;
+      return std::move(ci->parent);
+    });
+  c->active= 1;
+  ci->coro= std::move(ci->coro).resume();
+  return c->active;
+}
+
+
+extern "C"
+int
+my_context_continue(struct my_context *c)
+{
+  if (!c->active)
+    return 0;
+  my_context_intern *ci= (my_context_intern *)c->internal_context;
+  ci->coro= std::move(ci->coro).resume();
+  return c->active;
+}
+
+
+extern "C"
+int
+my_context_yield(struct my_context *c)
+{
+  if (!c->active)
+    return -1;
+  my_context_intern *ci= (my_context_intern *)c->internal_context;
+  ci->parent= std::move(ci->parent).resume();
+  return 0;
+}
+
+
+extern "C"
+int
+my_context_init(struct my_context *c, size_t stack_size)
+{
+  memset(c, 0, sizeof(*c));
+  if (!(c->stack= malloc(stack_size)))
+    return -1;                                  /* Out of memory */
+  if (!(c->internal_context= new my_context_intern))
+  {
+    free(c->stack);
+    return -1;
+  }
+  c->stack_size= stack_size;
+#ifdef HAVE_VALGRIND
+  c->valgrind_stack_id=
+    VALGRIND_STACK_REGISTER(c->stack, ((unsigned char *)(c->stack))+stack_size);
+#endif
+  return 0;
+}
+
+
+extern "C"
+void
+my_context_destroy(struct my_context *c)
+{
+  delete (my_context_intern *)c->internal_context;
+  if (c->stack)
+  {
+#ifdef HAVE_VALGRIND
+    VALGRIND_STACK_DEREGISTER(c->valgrind_stack_id);
+#endif
+    free(c->stack);
+  }
+}
+
+#endif /* MY_CONTEXT_USE_BOOST_CONTEXT */
