From c3733d869d4ba5caf0e0a4cf42c860bbbd94f769 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Mon, 9 Oct 2023 14:13:39 +0300
Subject: [PATCH] fiber: basic api exports

Closes #9237

Add exports for fiber_set_name_n, fiber_name, fiber_id, fiber_csw &
fiber_find.

Also make fiber_set_joinable, fiber_set_ctx & fiber_get_ctx interpret
NULL as the current fiber.

@TarantoolBot document
Title: add basic fiber api to ffi exports.

5 basic functions can now be used via ffi api, which were previously
only accessible via lua api: fiber_set_name_n, fiber_name, fiber_id,
fiber_csw & fiber_find.

fiber_set_joinable now interprets NULL as current fiber.
---
 changelogs/unreleased/fiber-basic-api.md |  6 ++
 extra/exports                            |  6 +-
 src/lib/core/fiber.c                     | 39 +++++++++++--
 src/lib/core/fiber.h                     | 72 ++++++++++++++++--------
 test/app-tap/module_api.c                | 58 +++++++++++++++++++
 test/app-tap/module_api.test.lua         |  2 +-
 6 files changed, 153 insertions(+), 30 deletions(-)
 create mode 100644 changelogs/unreleased/fiber-basic-api.md

diff --git a/changelogs/unreleased/fiber-basic-api.md b/changelogs/unreleased/fiber-basic-api.md
new file mode 100644
index 0000000000..6b7b255437
--- /dev/null
+++ b/changelogs/unreleased/fiber-basic-api.md
@@ -0,0 +1,6 @@
+## feature/fiber
+
+* Exported `fiber_set_name_n`, `fiber_name`, `fiber_id`, `fiber_csw` and
+  `fiber_find` into the public C API and usable via FFI as well.
+* Make `fiber_set_joinable`, `fiber_set_ctx` and `fiber_get_ctx`
+  treat the NULL argument as the current fiber.
diff --git a/extra/exports b/extra/exports
index 3eaaae0ab4..1c780e1719 100644
--- a/extra/exports
+++ b/extra/exports
@@ -232,10 +232,14 @@ fiber_cond_new
 fiber_cond_signal
 fiber_cond_wait
 fiber_cond_wait_timeout
+fiber_csw
+fiber_find
 fiber_get_ctx
+fiber_id
 fiber_is_cancelled
 fiber_join
 fiber_join_timeout
+fiber_name
 fiber_new
 fiber_new_ex
 fiber_reschedule
@@ -243,6 +247,7 @@ fiber_self
 fiber_set_cancellable
 fiber_set_ctx
 fiber_set_joinable
+fiber_set_name_n
 fiber_sleep
 fiber_start
 fiber_time
@@ -655,7 +660,6 @@ coio_readn_ahead
 coio_read_ahead_timeout
 coio_write_timeout
 
-fiber_set_name_n
 authenticate
 user_auth_method_name
 port_c_create
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index b3074e1b1d..6e1a8e1ee2 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -586,12 +586,16 @@ fiber_make_ready(struct fiber *f)
 void
 fiber_set_ctx(struct fiber *f, void *f_arg)
 {
+	if (f == NULL)
+		f = fiber();
 	f->f_arg = f_arg;
 }
 
 void *
 fiber_get_ctx(struct fiber *f)
 {
+	if (f == NULL)
+		f = fiber();
 	return f->f_arg;
 }
 
@@ -649,6 +653,9 @@ fiber_is_cancelled(void)
 void
 fiber_set_joinable(struct fiber *fiber, bool yesno)
 {
+	if (fiber == NULL)
+		fiber = fiber();
+
 	/*
 	 * The C fiber API does not allow to report any error to the caller. At
 	 * the same time using the function in some conditions is unsafe. So in
@@ -1201,15 +1208,11 @@ fiber_loop(MAYBE_UNUSED void *data)
 	}
 }
 
-inline void
-fiber_set_name(struct fiber *fiber, const char *name)
-{
-	fiber_set_name_n(fiber, name, strlen(name));
-}
-
 void
 fiber_set_name_n(struct fiber *fiber, const char *name, uint32_t len)
 {
+	if (fiber == NULL)
+		fiber = fiber();
 	size_t size = len + 1;
 	if (size <= FIBER_NAME_INLINE) {
 		if (fiber->name != fiber->inline_name) {
@@ -1233,6 +1236,30 @@ fiber_set_name_n(struct fiber *fiber, const char *name, uint32_t len)
 	fiber->name[size] = 0;
 }
 
+const char *
+fiber_name(const struct fiber *fiber)
+{
+	if (fiber == NULL)
+		fiber = fiber();
+	return fiber->name;
+}
+
+uint64_t
+fiber_id(const struct fiber *fiber)
+{
+	if (fiber == NULL)
+		fiber = fiber();
+	return fiber->fid;
+}
+
+uint64_t
+fiber_csw(const struct fiber *fiber)
+{
+	if (fiber == NULL)
+		fiber = fiber();
+	return fiber->csw;
+}
+
 static inline void *
 page_align_down(void *ptr)
 {
diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h
index c3f80686a8..afe8c2dba8 100644
--- a/src/lib/core/fiber.h
+++ b/src/lib/core/fiber.h
@@ -299,6 +299,7 @@ fiber_start(struct fiber *callee, ...);
  * fiber_start which means no yields.
  *
  * \param f     fiber to set the context for
+ *              if NULL, the current fiber is used
  * \param f_arg context for the fiber function
  */
 API_EXPORT void
@@ -307,6 +308,7 @@ fiber_set_ctx(struct fiber *f, void *f_arg);
 /**
  * Get the context for the fiber which was set via the fiber_set_ctx
  * function. Can be used to avoid calling fiber_start which means no yields.
+ * If \a f is NULL, the current fiber is used.
  *
  * \retval      context for the fiber function set by fiber_set_ctx function
  *
@@ -354,6 +356,8 @@ fiber_set_cancellable(bool yesno);
  * @pre the fiber is not dead (panic if not).
  * @pre the fiber is not joined yet (panic if not).
  *
+ * \param fiber to (un)set the joinable property.
+ *              If set to NULL, the current fiber is used.
  * \param yesno status to set
  */
 API_EXPORT void
@@ -443,6 +447,49 @@ fiber_clock64(void);
 API_EXPORT void
 fiber_reschedule(void);
 
+/**
+ * Set fiber name.
+ * @param fiber Target fiber, if it's NULL the current fiber is used.
+ * @param name A new name of @a fiber.
+ * @param len Length of the string pointed to by @a name.
+ */
+API_EXPORT void
+fiber_set_name_n(struct fiber *fiber, const char *name, uint32_t len);
+
+/**
+ * Get fiber name.
+ * @param fiber Target fiber, if it's NULL the current fiber is used.
+ * @return pointer to a nul-terminated string.
+ */
+API_EXPORT const char *
+fiber_name(const struct fiber *fiber);
+
+/**
+ * Get fiber id.
+ * @param fiber Target fiber, if it's NULL the current fiber is used.
+ */
+API_EXPORT uint64_t
+fiber_id(const struct fiber *fiber);
+
+/**
+ * Get number of context switches of the given fiber.
+ * @param fiber Target fiber, if it's NULL the current fiber is used.
+ */
+API_EXPORT uint64_t
+fiber_csw(const struct fiber *fiber);
+
+/**
+ * Get a pointer to a live fiber in the current cord by the given fiber id,
+ * which may be used for getting other info about the fiber (name, csw, etc.).
+ *
+ * @param fid Target fiber id.
+ * @return fiber on success, NULL if fiber was not found.
+ *
+ * @sa fiber_name, fiber_csw, fiber_id
+ */
+API_EXPORT struct fiber *
+fiber_find(uint64_t fid);
+
 /**
  * Return slab_cache suitable to use with tarantool/small library
  */
@@ -967,26 +1014,10 @@ fiber_signal_reset(void);
  * @param fiber Fiber to set name for.
  * @param name A new name of @a fiber.
  */
-void
-fiber_set_name(struct fiber *fiber, const char *name);
-
-/** \cond public */
-
-/**
- * Set fiber name providing a length for it.
- * @param fiber Fiber to set name for.
- * @param name A new name of @a fiber.
- * @param len Length of the string pointed to by @a name.
- */
-void
-fiber_set_name_n(struct fiber *fiber, const char *name, uint32_t len);
-
-/** \endcond public */
-
-static inline const char *
-fiber_name(struct fiber *f)
+static inline void
+fiber_set_name(struct fiber *fiber, const char *name)
 {
-	return f->name;
+	fiber_set_name_n(fiber, name, strlen(name));
 }
 
 /** Helper function to check if slice is valid. */
@@ -1125,9 +1156,6 @@ fiber_destroy_all(struct cord *cord);
 void
 fiber_call(struct fiber *callee);
 
-struct fiber *
-fiber_find(uint64_t fid);
-
 void
 fiber_schedule_cb(ev_loop * /* loop */, ev_watcher *watcher, int revents);
 
diff --git a/test/app-tap/module_api.c b/test/app-tap/module_api.c
index f7443a9311..230e540e13 100644
--- a/test/app-tap/module_api.c
+++ b/test/app-tap/module_api.c
@@ -326,6 +326,12 @@ fiber_set_ctx_test_func(va_list va)
 static int
 test_fiber_set_ctx(lua_State *L)
 {
+	/* Set context for the current fiber. */
+	fiber_set_ctx(NULL, (void *)0xCAFEBABEDEADF00D);
+	uint64_t ctx = (uint64_t)fiber_get_ctx(NULL);
+	fail_unless(ctx == 0xCAFEBABEDEADF00D);
+
+	/* Set context for a child fiber. */
 	struct fiber *fiber = fiber_new("test fiber", fiber_set_ctx_test_func);
 	fiber_set_joinable(fiber, true);
 	char data[3] = { '?', '!', '\0' };
@@ -3328,6 +3334,57 @@ test_box_iproto_override_reset(struct lua_State *L)
 
 /* }}} Helpers for `box_iproto_override` Lua/C API test cases */
 
+static int
+fiber_basic_api_func(va_list va)
+{
+	(void)va;
+	const char *name = "oppenheimer";
+	/* fiber_set_joinable now works with NULL. (Consistency!) */
+	fiber_set_joinable(NULL, true);
+	fiber_set_name_n(fiber_self(), name, strlen(name));
+	return 0;
+}
+
+static int
+test_fiber_basic_api(lua_State *L)
+{
+	uint64_t self_id = fiber_id(NULL);
+	struct fiber *t = fiber_find(self_id);
+	fail_unless(fiber_self() == t);
+
+	/* Set/get name of self works. */
+	const char *name = "parent";
+	fiber_set_name_n(NULL, name, strlen(name));
+	string_check_equal(fiber_name(NULL), name);
+
+	/* No such fiber. */
+	t = fiber_find((uint64_t)-1);
+	fail_unless(t == NULL);
+
+	/* Fiber is created and is immediately accessible via fiber_find. */
+	struct fiber *fiber = fiber_new("barbie", fiber_basic_api_func);
+	string_check_equal(fiber_name(fiber), "barbie");
+	uint64_t f_id = fiber_id(fiber);
+	t = fiber_find(f_id);
+	fail_unless(fiber == t);
+
+	/* Check that csw is increased because fiber_start yields. */
+	uint64_t csw0_parent = fiber_csw(NULL);
+	uint64_t csw0_child = fiber_csw(fiber);
+	fiber_start(fiber);
+	uint64_t csw1_parent = fiber_csw(NULL);
+	uint64_t csw1_child = fiber_csw(fiber);
+	fail_unless(csw1_parent == csw0_parent + 1);
+	fail_unless(csw1_child == csw0_child + 1);
+
+	string_check_equal(fiber_name(fiber), "oppenheimer");
+	/* At this point fiber is recycled. */
+	fiber_join(fiber);
+
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
 static int
 test_box_auth_data_prepare(struct lua_State *L)
 {
@@ -3644,6 +3701,7 @@ luaopen_module_api(lua_State *L)
 		{"test_fiber", test_fiber },
 		{"test_fiber_set_ctx", test_fiber_set_ctx },
 		{"test_cord", test_cord },
+		{"test_fiber_basic_api", test_fiber_basic_api },
 		{"pushcdata", test_pushcdata },
 		{"checkcdata", test_checkcdata },
 		{"test_clock", test_clock },
diff --git a/test/app-tap/module_api.test.lua b/test/app-tap/module_api.test.lua
index 29e28a765c..e22a3b716b 100755
--- a/test/app-tap/module_api.test.lua
+++ b/test/app-tap/module_api.test.lua
@@ -746,7 +746,7 @@ local function test_box_access_check_space(test)
 end
 
 require('tap').test("module_api", function(test)
-    test:plan(57)
+    test:plan(58)
     local status, module = pcall(require, 'module_api')
     test:is(status, true, "module")
     test:ok(status, "module is loaded")
-- 
GitLab