From d2c6d0fcc509c2183aa0afc4c46cc401ef2ec931 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Thu, 4 Dec 2025 10:56:09 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"!993=20[205=5F1]=20=E5=9F=BA=E4=BA=8E?= =?UTF-8?q?tbox=E5=AE=9E=E7=8E=B0=E5=8D=8F=E7=A8=8B"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b2c639b78501c850725befb25b0f43773ae58347. --- devel/205_1.md | 42 ---- goldfish/liii/coroutine.scm | 27 --- src/goldfish.hpp | 156 ++----------- tests/goldfish/liii/coroutine-test.scm | 304 ------------------------- xmake.lua | 2 +- 5 files changed, 14 insertions(+), 517 deletions(-) delete mode 100644 devel/205_1.md delete mode 100644 goldfish/liii/coroutine.scm delete mode 100644 tests/goldfish/liii/coroutine-test.scm diff --git a/devel/205_1.md b/devel/205_1.md deleted file mode 100644 index ad425ea1..00000000 --- a/devel/205_1.md +++ /dev/null @@ -1,42 +0,0 @@ -# [205_1] 为 Goldfish 添加协程调度与控制能力 - -## 如何检测 - -运行协程相关测试: - -```sh -bin/goldfish tests/goldfish/liii/coroutine-test.scm -``` - -所有用例通过即表示协程的创建、调度以及控制 API 工作正常。 - -## 2025/11/25 - -## What - -- 在 C++ 层基于 TBOX 的 coroutine/scheduler 实现协程调度: - - 使用 `tb_co_scheduler_init / tb_co_scheduler_loop / tb_co_scheduler_exit` 管理调度器; - - 使用 `tb_coroutine_start` 启动协程,并通过统一的 `coroutine_run` 调用 Scheme 闭包; - - 使用 `s7_gc_protect / s7_gc_unprotect_at` 保护协程闭包,避免在协程执行期间被 GC 回收。 -- 提供基础 API: - - `g_coroutine-dispatch`:创建调度器并执行顶层协程及其派生的所有协程,直到全部完成; - - `g_coroutine-create`:在当前调度器下立即创建并启动一个新的协程。 -- 提供协程控制 API: - - `g_coroutine-yield`:当前协程主动让出执行权,由调度器切换到其他协程; - - `g_coroutine-sleep`:当前协程非阻塞睡眠指定毫秒数,醒来后继续执行; -- 在 Scheme 层增加 `(liii coroutine)` 库封装上述 API: - - `coroutine-dispatch` / `coroutine-create` - - `coroutine-yield` / `coroutine-sleep` -- 为上述功能添加测试:`tests/goldfish/liii/coroutine-test.scm`: - - 验证 `coroutine-dispatch` 能执行所有派生协程并正确汇总副作用(加和、列表构造等); - - 验证 `coroutine-yield` 的交替执行顺序; - - 验证 `coroutine-sleep` 的非阻塞行为以及不同睡眠时间下的唤醒顺序; - -## Why - -- 为 Goldfish 提供一种轻量级的协作式并发原语,便于在脚本中组织复杂的异步/并发逻辑; -- 通过 TBOX 的 coroutine/scheduler 复用成熟实现,避免自行管理栈切换和事件轮询; -- 在 Scheme 层暴露统一的 `(liii coroutine)` API,使用户可以用少量原语表达: - - 顺序执行 + 并发创建(`dispatch`/`create`); - - 主动让出(`yield`)和按时间驱动的挂起(`sleep`); - diff --git a/goldfish/liii/coroutine.scm b/goldfish/liii/coroutine.scm deleted file mode 100644 index 348fdc2f..00000000 --- a/goldfish/liii/coroutine.scm +++ /dev/null @@ -1,27 +0,0 @@ -; -; Copyright (C) 2025 The Goldfish Scheme Authors -; -; Licensed under the Apache License, Version 2.0 (the "License"); -; you may not use this file except in compliance with the License. -; You may obtain a copy of the License at -; -; http://www.apache.org/licenses/LICENSE-2.0 -; -; Unless required by applicable law or agreed to in writing, software -; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -; License for the specific language governing permissions and limitations -; under the License. -; - -(define-library (liii coroutine) - (export co-dispatch co-create co-yield co-sleep) - (begin - (define (co-dispatch f) - (g_coroutine-dispatch f)) - (define (co-create f) - (g_coroutine-create f)) - (define (co-yield) - (g_coroutine-yield)) - (define (co-sleep seconds) - (g_coroutine-sleep seconds)))) \ No newline at end of file diff --git a/src/goldfish.hpp b/src/goldfish.hpp index 1e4695c6..bf393bf9 100644 --- a/src/goldfish.hpp +++ b/src/goldfish.hpp @@ -198,8 +198,8 @@ goldfish_exe () { GetModuleFileName (NULL, buffer, GOLDFISH_PATH_MAXN); return string (buffer); #elif TB_CONFIG_OS_MACOSX - char buffer[PATH_MAX]; - uint32_t size= sizeof (buffer); + char buffer[PATH_MAX]; + uint32_t size= sizeof (buffer); if (_NSGetExecutablePath (buffer, &size) == 0) { char real_path[GOLDFISH_PATH_MAXN]; if (realpath (buffer, real_path) != NULL) { @@ -391,137 +391,6 @@ glue_mkdir (s7_scheme* sc) { glue_define (sc, name, desc, f_mkdir, 1, 0); } -// 协程上下文结构,保存 Scheme 解释器、函数和 GC 保护位置 -struct coroutine_ctx { - s7_scheme* sc; // Scheme 解释器实例 - s7_pointer f; // 要执行的 Scheme 函数 - s7_int gc_loc; // GC 保护位置索引 -}; - -// 协程运行函数,在协程中执行 Scheme 函数 -static void -coroutine_run (tb_cpointer_t priv) { - coroutine_ctx* ctx= (coroutine_ctx*) priv; - // 执行 Scheme 函数 - s7_apply_function (ctx->sc, ctx->f, s7_nil (ctx->sc)); - // 执行完毕后取消 GC 保护,平衡保护计数 - if (ctx->gc_loc != -1) s7_gc_unprotect_at (ctx->sc, ctx->gc_loc); - delete ctx; -} - -// 协程调度器函数:创建调度器并运行传入的函数及其派生的所有协程 -static s7_pointer -coroutine_dispatch (s7_scheme* sc, s7_pointer args) { - s7_pointer f = s7_car (args); - coroutine_ctx* ctx = new coroutine_ctx{sc, f, -1}; - tb_co_scheduler_ref_t scheduler= tb_co_scheduler_init (); - if (scheduler == nullptr) { - const char* err_type= "coroutine"; - const char* err_desc= "failed to init coroutine scheduler"; - return s7_error (sc, s7_make_symbol (sc, err_type), s7_list (sc, 2, s7_make_string (sc, err_desc), f)); - } - - // 启动协程 - // 保护闭包免于被 GC 回收,直到协程执行完毕 - ctx->gc_loc= s7_gc_protect (sc, f); - if (!tb_coroutine_start (scheduler, coroutine_run, ctx, 0)) { - const char* err_type= "coroutine"; - const char* err_desc= "failed to start coroutine"; - // 启动失败时取消保护并清理资源 - if (ctx->gc_loc != -1) s7_gc_unprotect_at (sc, ctx->gc_loc); - delete ctx; - // 退出调度器以避免泄漏,然后返回错误 - tb_co_scheduler_exit (scheduler); - return s7_error (sc, s7_make_symbol (sc, err_type), s7_list (sc, 2, s7_make_string (sc, err_desc), f)); - } - - // 以非独占模式运行调度器 - tb_co_scheduler_loop (scheduler, false); - - // 退出调度器 - tb_co_scheduler_exit (scheduler); - - return s7_nil (sc); -} - -// 绑定 coroutine-dispatch 函数到 Scheme 环境 -inline void -glue_coroutine_dispatch (s7_scheme* sc) { - const char* name= "g_coroutine-dispatch"; - const char* desc= "(g_coroutine-dispatch function) => nil, dispatch the passed-in funtion and all its derived " - "coroutines until all are completed"; - s7_define_function (sc, name, coroutine_dispatch, 1, 0, false, desc); -} - -// 创建协程函数:在当前调度器中创建并启动一个新协程 -static s7_pointer -coroutine_create (s7_scheme* sc, s7_pointer args) { - const tb_size_t stacksize= 32 * 1024; // 32KB 栈大小 - - s7_pointer f = s7_car (args); - coroutine_ctx* ctx= new coroutine_ctx{sc, f, -1}; - // 保护闭包免于被 GC 回收,直到协程执行完毕 - ctx->gc_loc= s7_gc_protect (sc, f); - if (!tb_coroutine_start (tb_null, coroutine_run, ctx, stacksize)) { - const char* err_type= "coroutine"; - const char* err_desc= "failed to start coroutine"; - // 启动失败时取消保护并清理资源 - if (ctx->gc_loc != -1) s7_gc_unprotect_at (sc, ctx->gc_loc); - delete ctx; - return s7_error (sc, s7_make_symbol (sc, err_type), s7_list (sc, 2, s7_make_string (sc, err_desc), f)); - } - return s7_nil (sc); -} - -// 绑定 coroutine-create 函数到 Scheme 环境 -inline void -glue_coroutine_create (s7_scheme* sc) { - const char* name= "g_coroutine-create"; - const char* desc= "(g_coroutine-create function) => nil, create coroutine"; - s7_define_function (sc, name, coroutine_create, 1, 0, false, desc); -} - -// 协程让出函数:让出当前协程的执行权 -static s7_pointer -f_coroutine_yield (s7_scheme* sc, s7_pointer args) { - tb_bool_t ok= tb_coroutine_yield (); - return s7_make_boolean (sc, ok); -} - -// 绑定 coroutine-yield 函数到 Scheme 环境 -inline void -glue_coroutine_yield (s7_scheme* sc) { - const char* name= "g_coroutine-yield"; - const char* desc= "(g_coroutine-yield) => boolean, yield current coroutine"; - glue_define (sc, name, desc, f_coroutine_yield, 0, 0); -} - -// 协程休眠函数:让当前协程休眠指定秒数(非阻塞) -static s7_pointer -f_coroutine_sleep (s7_scheme* sc, s7_pointer args) { - s7_double seconds= s7_real (s7_car (args)); - // 将秒转换为毫秒传给 TBOX - tb_coroutine_sleep ((tb_long_t) (seconds * 1000)); - return s7_nil (sc); -} - -// 绑定 coroutine-sleep 函数到 Scheme 环境 -inline void -glue_coroutine_sleep (s7_scheme* sc) { - const char* name= "g_coroutine-sleep"; - const char* desc= "(g_coroutine-sleep seconds) => nil, non-blocking sleep for coroutine"; - glue_define (sc, name, desc, f_coroutine_sleep, 1, 0); -} - -// 注册所有协程相关函数到 Scheme 环境 -inline void -glue_liii_coroutine (s7_scheme* sc) { - glue_coroutine_dispatch (sc); - glue_coroutine_create (sc); - glue_coroutine_yield (sc); - glue_coroutine_sleep (sc); -} - static s7_pointer f_rmdir (s7_scheme* sc, s7_pointer args) { const char* dir_c= s7_string (s7_car (args)); @@ -655,22 +524,24 @@ glue_getpid (s7_scheme* sc) { } static s7_pointer -f_sleep (s7_scheme* sc, s7_pointer args) { - s7_double seconds= s7_real (s7_car (args)); - +f_sleep(s7_scheme* sc, s7_pointer args) { + s7_double seconds = s7_real(s7_car(args)); + // 使用 tbox 的 tb_sleep 函数,参数是毫秒 - tb_msleep ((tb_long_t) (seconds * 1000)); + tb_msleep((tb_long_t)(seconds * 1000)); - return s7_nil (sc); + return s7_nil(sc); } inline void -glue_sleep (s7_scheme* sc) { - const char* name= "g_sleep"; - const char* desc= "(g_sleep seconds) => nil, sleep for the specified number of seconds"; - glue_define (sc, name, desc, f_sleep, 1, 0); +glue_sleep(s7_scheme* sc) { + const char* name = "g_sleep"; + const char* desc = "(g_sleep seconds) => nil, sleep for the specified number of seconds"; + glue_define(sc, name, desc, f_sleep, 1, 0); } + + inline void glue_liii_os (s7_scheme* sc) { glue_os_arch (sc); @@ -1152,7 +1023,6 @@ glue_for_community_edition (s7_scheme* sc) { glue_liii_time (sc); glue_liii_datetime (sc); glue_liii_uuid (sc); - glue_liii_coroutine (sc); } static void diff --git a/tests/goldfish/liii/coroutine-test.scm b/tests/goldfish/liii/coroutine-test.scm deleted file mode 100644 index c9ce043d..00000000 --- a/tests/goldfish/liii/coroutine-test.scm +++ /dev/null @@ -1,304 +0,0 @@ -; -; Copyright (C) 2025 The Goldfish Scheme Authors -; -; Licensed under the Apache License, Version 2.0 (the "License"); -; you may not use this file except in compliance with the License. -; You may obtain a copy of the License at -; -; http://www.apache.org/licenses/LICENSE-2.0 -; -; Unless required by applicable law or agreed to in writing, software -; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -; License for the specific language governing permissions and limitations -; under the License. -; - -(import (liii check) - (liii coroutine)) - -#| -co-dispatch -调度并执行协程及其派生的所有子协程。 - -语法 ----- -(co-dispatch function) - -参数 ----- -function : procedure -要执行的函数,可在其中创建协程。 - -返回值 ----- -nil -总是返回空列表。 - -注意 ----- -co-dispatch 会创建一个调度器并运行传入的函数及其创建的所有协程, -直到所有协程完成。协程按 LIFO(后进先出)顺序调度: -一旦某个协程开始执行,它会一直运行直到主动让出CPU(通过 yield 或 sleep), -然后调度器会选择最近挂起的协程来执行,而不是交替执行。 - -示例 ----- -|# -(check (let ((sum 0)) - (co-dispatch - (lambda () - (co-create (lambda () (set! sum (+ sum 1)))) - (co-create (lambda () (set! sum (+ sum 2)))))) - sum) => 3) - -;; co-dispatch 应该执行传入的函数及其所有派生的协程 -(let ((xs '(1 2 3 4 5))) - (check (let ((sum 0)) - (co-dispatch - (lambda () - (map (lambda (x) (co-create (lambda () (set! sum (+ sum x))))) xs))) - sum) => (apply + xs))) - -#| -co-create -创建一个新的协程并立即开始执行。 - -语法 ----- -(co-create function) - -参数 ----- -function : procedure -要在协程中执行的函数。 - -返回值 ----- -nil -总是返回空列表。 - -注意 ----- -co-create 必须在 co-dispatch 创建的调度器上下文中调用。 -创建的协程会立即开始执行,并按 LIFO(后进先出)顺序调度: -一旦某个协程开始执行,它会一直运行直到主动让出CPU, -然后调度器会选择最近挂起的协程来执行。协程可以嵌套创建其他协程。 - -错误处理 ----- -如果在没有调度器的上下文中调用,会抛出错误。 - -示例 ----- -|# -(check (let ((result '())) - (co-dispatch - (lambda () - (co-create (lambda () (set! result (cons 'a result)))) - (co-create (lambda () (set! result (cons 'b result)))))) - (reverse result)) => '(a b)) - -;; co-dispatch 嵌套创建协程 -(check (let ((v 1)) - (co-dispatch - (lambda () - (co-create - (lambda () - (co-create (lambda () (set! v (* v 10)))) - (set! v (+ v 2)))))) - v) => 30) - -;; co-dispatch 执行纯函数 -;; TODO(jinser): 创建时不要自动启动 -; (check (procedure? (co-create (lambda () 42)) => '())) - -;; co-dispatch 执行纯函数 -(check (co-dispatch (lambda () 42)) => '()) - -;; co-dispatch 多个协程的副作用 -(check (let ((s '())) - (co-dispatch - (lambda () - (co-create (lambda () (set! s (cons #\a s)))) - (co-create (lambda () (set! s (cons #\b s)))) - (co-create (lambda () (set! s (cons #\c s)))))) - s) => '(#\c #\b #\a)) - -#| -co-yield -让出当前协程的执行权,允许其他协程运行。 - -语法 ----- -(co-yield) - -参数 ----- -无 - -返回值 ----- -boolean -返回 #t 表示成功让出,#f 表示失败。 - -注意 ----- -co-yield 用于协程之间的协作调度。 -TBOX 调度器使用 LIFO(后进先出)顺序,后创建的协程在栈顶。 -一旦某个协程开始执行,它会一直运行直到主动让出CPU。 -当协程让出时,调度器会选择最近挂起的协程来执行,而不是交替执行。 - -示例 ----- -|# -(check (let ((trace '())) - (co-dispatch - (lambda () - (co-create - (lambda () - (set! trace (cons 'a trace)) - (co-yield) - (set! trace (cons 'b trace)))) - (co-create - (lambda () - (set! trace (cons 'c trace)))))) - (reverse trace)) => '(a c b)) - -;; co-yield 在协程之间交替执行 -;; 注意:TBOX 使用 LIFO 顺序调度,先创建的先运行,让出给后创建的 -(display "\n=== Test: co-yield alternates execution ===\n") - -;; 定义协程 A 的执行逻辑 -(define (make-coroutine-a trace-setter) - (lambda () - (trace-setter 'a1) - (display "Coroutine A: yielding...\n") - (co-yield) - (display "Coroutine A: step 2\n") - (trace-setter 'a2) - (display "Coroutine A: yielding again...\n") - (display "Coroutine A: step 3 (final)\n") - (co-yield) - (trace-setter 'a3))) - -;; 定义协程 B 的执行逻辑 -(define (make-coroutine-b trace-setter) - (lambda () - (trace-setter 'b1) - (display "Coroutine B: yielding...\n") - (display "Coroutine B: step 2\n") - (co-yield) - (trace-setter 'b2) - (display "Coroutine B: yielding again...\n") - (display "Coroutine B: step 3 (final)\n") - (co-yield) - (trace-setter 'b3))) - -(check (let ((trace '())) - (define (add-trace! symbol) - (set! trace (cons symbol trace))) - (display "\nCoroutine A: step 1\n") - (co-dispatch - (lambda () - (co-create (make-coroutine-a add-trace!)) - (display "Coroutine B: step 1\n") - (co-create (make-coroutine-b add-trace!)))) - (display (string-append "Execution trace: " (object->string (reverse trace)) "\n")) - (reverse trace)) => '(a1 b1 b2 b3 a2 a3)) ; LIFO顺序 - -;; co-yield 返回布尔值表示成功 -(check (co-dispatch - (lambda () - (co-create - (lambda () - (boolean? (co-yield)))))) => '()) - -#| -co-sleep -让当前协程休眠指定的时间,期间允许其他协程运行。 - -语法 ----- -(co-sleep seconds) - -参数 ----- -seconds : real -休眠的秒数,可以是小数。 - -返回值 ----- -nil -总是返回空列表。 - -注意 ----- -co-sleep 是非阻塞的休眠,只会暂停当前协程, -不会阻塞整个程序或调度器。其他协程可以在休眠期间继续执行。 -休眠时间到达后,协程会被重新调度执行。 -调度器使用 LIFO(后进先出)顺序:最近挂起的协程会优先被调度。 - -错误处理 ----- -如果参数不是数字,会抛出类型错误。 - -示例 ----- -|# -(check (let ((order '())) - (co-dispatch - (lambda () - (co-create - (lambda () - (set! order (cons 'start order)) - (co-sleep 0.01) - (set! order (cons 'end order)))) - (co-create - (lambda () - (set! order (cons 'instant order)))))) - (reverse order)) => '(start instant end)) - -;; co-sleep 允许定时调度 -;; 注意:第一个协程立即启动,休眠,第二个协程运行,然后第一个协程唤醒 -(display "\n=== Test: co-sleep scheduling ===\n") -(check (let ((order '())) - (display "\nCoroutine 1: starting, will sleep 0.05s\n") - (co-dispatch - (lambda () - (co-create - (lambda () - (set! order (cons 'fast-start order)) - (display "Coroutine 1: entering sleep...\n") - (co-sleep 0.05) - (display "Coroutine 1: woke up from sleep\n") - (set! order (cons 'fast-end order)))) - (display "Coroutine 2: executing immediately (no sleep)\n") - (co-create - (lambda () - (set! order (cons 'instant order)))))) - (display (string-append "Execution order: " (object->string (reverse order)) "\n")) - (reverse order)) => '(fast-start instant fast-end)) - -;; co-sleep 多个协程 -;; 注意:两个协程按 LIFO 顺序启动(先 1 后 2),休眠时间短的先唤醒 -(display "\n=== Test: multiple coroutines with different sleep times ===\n") -(check (let ((steps '())) - (display "\nCoroutine A: step 1, sleeping 0.02s\n") - (co-dispatch - (lambda () - (co-create - (lambda () - (set! steps (cons 1 steps)) - (co-sleep 0.02) - (display "Coroutine A: step 4 (woke up after longer sleep)\n") - (set! steps (cons 4 steps)))) - (display "Coroutine B: step 2, sleeping 0.01s\n") - (co-create - (lambda () - (set! steps (cons 2 steps)) - (co-sleep 0.01) - (display "Coroutine B: step 3 (woke up first - shorter sleep)\n") - (set! steps (cons 3 steps)))))) - (display (string-append "Steps order: " (object->string (reverse steps)) "\n")) - (reverse steps)) => '(1 2 3 4)) diff --git a/xmake.lua b/xmake.lua index 0f74150e..b449ff0c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -49,7 +49,7 @@ local TBOX_VERSION = "1.7.7" if has_config("tbox") then add_requires("apt::libtbox-dev", {alias="tbox"}) else - tbox_configs = {hash=true, coroutine=true, ["force-utf8"]=true} + tbox_configs = {hash=true, ["force-utf8"]=true} if has_config("pin-deps") then add_requires("tbox " .. TBOX_VERSION, {system=system, configs=tbox_configs}) else -- Gitee