Kotlin E03:Native 跨过 C 指针边界要付出什么
E02 把任务状态放进 `commonMain`,但只在 JVM 和 Node.js 上运行过测试。为多平台 Gradle 配置增加 `linuxX64()` 后,同一份状态转移还必须编译成 Native 可执行文件并运行,才能知道这一目标在当前机器上确实可用。至于接入 C 库,`commonMain` 的 sealed 分支不会自动告诉 C 如何接收字符串、谁持有字节数组地址、什么时候能释放它。 这里以本地合成输入验证 Linux x86_64。Kotlin/Native 编译器、Gradle 插件均固定为 2.2.21,使用原 Wrapper 8.14、JDK 21 和宿主 GCC 13.3.0。没有 macOS、Xcode 或 Swift 编译器;Swift/Objective-C 的运行结果标 NOT_RUN,不能凭 Linux C 绑定推出 Apple 目标已验收。 公共规则先要由 Native 自己跑一次 shared-model 沿用 E02 的 commonMain/TaskState.kt,新增 linuxX64 目标。相同的 commonTest 在这个目标下...
Kotlin E02:任务状态能共享到哪里
12 里定义的 `Queued → Running → Succeeded` 没有 JVM 线程、数据库连接,也不需要 Android 界面。39 则把同一套状态接到 Netty、PostgreSQL 和进程退出上。如果要让另一种运行环境复用任务规则,到底该移动哪一段代码?把整个 JVM 工程标成“跨平台”不仅编不过,还会掩盖最关键的问题:业务状态和执行这些状态的基础设施,有不同的可移植边界。 本篇从正在使用的 :domain 模块抽出状态模型,在 JVM 和 Node.js 上各跑一次相同的断言。实验并不涉及 Android、Native、浏览器或多端服务发布;“两端通过”的范围就是这两个运行目标。 先把真正纯粹的规则移出来 原来的 TaskState.kt 有两个不同职责。前半段定义状态、信号和转移;末尾的 acceptSubmission(store, id, text) 依赖 TaskStore,而 store 的具体实现位于 JVM 域模块。移动前半段到独立 Gradle 工程 shared-model/src/commonMain/kotlin/example/tas...
Go E03:cgo 与 unsafe 怎样改变资源边界
E02让任务提交校验不再依赖运行时逐字段检查。本系列的业务仍由原 Go HTTP/SQL 服务处理;本篇只给原分析器接一个受控的本地函数,检查文字输入能否穿过 C 边界。完整工程及实验包、SHA-256、独立复跑步骤随文提供。 先规定谁拥有内存 固定 Go 1.27.1 的 cmd/cgo 文档区分 Go 内存指针与 C 内存指针:Go 指针传给 C 要遵循 pin 规则,C.CBytes 在 C 堆新建数据副本,需要调用者释放。native/sum.go:SumASCII 先拒绝零长度、超过 4096 字节、NUL 或非 ASCII 的输入,再用 C.CBytes 复制字节到 C 内存;只在 task_ascii_sum 调用期间把这份 C 内存通过 unsafe.Pointer 转成 C 字节指针,返回前以 defer C.free 释放。C 函数只在给定长度内求字节和,不保存地址,也不访问网络或数据库。 这条路径没有把 Go slice 的地址留给 C,但使用了 unsafe 和本地编译器,原 Go 类型系统无法替 C 函数证明越界、悬垂或其他内存安全性质。本地函数没有异步...
Go E02:反射与代码生成如何校验任务字段
E01确认了接口、值与取消的局部契约。这次只动同一个任务服务提交入口的字段校验:请求的 Text 不能为空白,配置中的 MaxWords 必须为正。生成校验承担实际 HTTP 路径,反射版本只作对照,业务领取、SQL 与状态转移不复制第二套。完整包、摘要和复跑步骤附在同名目录。 错误在哪一刻出现 字段声明在 taskvalidation/schema.go:Fields:Text string 带 check:"nonblank",MaxWords int 带 check:"positive"。cmd/generate-taskvalidation 用固定 Go 1.27.1 的 go/parser 读取 AST,先核验字段名、类型与标签,再用 go/format 生成 taskvalidation/generated.go:Validate。原 http_tasks.go:create 仍先作 JSON/体积校验,之后调用生成函数;拒绝空白正文仍返回原有 400 和原有错误文本,原 SQL 插入只发生在校验通过之后。 反例把 Text 的...
Kotlin 39:一条任务从请求到重启能证明什么
此前的实验分开验证了 JSON 领域校验、Ktor 回环、协程任务树与真实 PostgreSQL:34的 HTTP registry 仍在内存里,35的 JDBC Store 没有接到这个路由。把两篇测试的 PASS 拼在一起,不能推出“一条 HTTP 请求提交后重启仍能查询”。39 改为同一组隔离 PostgreSQL 数据目录和两个先后启动的真实 Netty JVM,逐项询问:202 出现前是否已提交、Job 失败留下什么、数据库断线怎样返回、重启后 GET 读到的是状态还是正在运行的任务。 本轮保持 Kotlin 2.2.21、Gradle 8.14、JDK 21、coroutines 1.10.2、Ktor 3.3.1、JDBC 42.7.7 与专用 PostgreSQL 16.15。2026-10-10 核对 Ktor 服务测试、PostgreSQL 16 INSERT/UPDATE 与 JDK 21 Connection;精确运行版本和原始响应见 writing-plans/kotlin/verification/39/。只使用本地合成请求,测试初始化自己的数据库进程...
Go E01:Java 经验迁移时容易误读的三个 Go 契约
主线 39已经核验服务的接纳、查询和退出;系列导航仍把 E01–E08 单独计数。迁移 Java 经验时,几个用词看起来熟悉:null、对象引用、线程中断。但 Go 的接口值、struct 值与 context 取消有自己的规则。本篇只在原累计工程上加三项反例测试,不复制 Java 服务。素材另附SHA-256及复跑步骤。 不能用 Java 的 null 猜接口是否为空 Go 1.27.1 的语言规范在 Variables 一节直接给出 var v *T 为 nil、x = v 后接口 x 动态类型为 *T 的例子。TestElectiveJavaTypedNilIsNotMissingTask 把 nil 的 *electiveLookupError 赋给 error:原指针仍为 nil,接口比较 err != nil 却为 true。这是typed nil,不是「有一个非空错误对象」。测试给错误方法加了 nil 接收者保护,以便观察接口内容,而不是靠 panic 判断。 同一任务服务的真实 TaskStore.Lookup("missing") 返回 (T...
Kotlin 38:进程退出时,哪些任务和连接还活着
34 的 HTTP 实验在测试应用与回环端口证明了 202 先于后台任务完成,但那时服务由测试管理;测试结束时由调用方手动停引擎、停内存任务。把类装进发行包后,真实 JVM 收到终止请求的顺序由谁保证?单看“进程没了”无法判断:请求可能仍被受理,挂起任务可能没等清理完成,打印的终态也可能与实际资源退出不一致。 本篇沿用 Kotlin 2.2.21、Ktor 3.3.1、Gradle 8.14、JDK 21。依据是 Gradle application 插件的 installDist/distZip、Ktor embeddedServer 的服务配置、JDK 21 Runtime.addShutdownHook(2026-10-10 核对)。下述只运行本机 127.0.0.1 的合成任务,不构成公网部署。 发行包里有两个不同的入口 累计工程从 00 起的 main(args) 仍是校验 submit alpha 的教学 CLI:它接受输入,不执行后台任务。修改 application.mainClass 去指向 HTTP 服务,会让已经回归了几十篇的 CLI 正反例失去原来的语义。...
Kotlin 37:装箱与协程成本怎样测才不误判
36 的字节码观察确认了挂起函数的 JVM 签名与状态字段,但单凭 `javap` 里多了几个字段,算不出应用的分配量,更算不出 HTTP 请求耗时。08 的集合实验展示 `take(1)` 在首项匹配时 Sequence 可以少做很多转换;那种输入若直接放进性能对比,两条路径其实完成了不同的工作。这里先把工作量对齐,再问几个更窄的问题:同一组任务标签通过 List 与 Sequence 转换、同一组 ID 包装成 value class、同一条合成状态路径通过 `runBlocking`,在当前 JVM 上分别记录什么。 工具是 Kotlin 编译器/插件 2.2.21、JDK 21、Gradle 8.14 以及 OpenJDK JMH 1.37(tag 2effa2c8310e1d3ad03c8ee02024edca9252b46a)。JMH 官方同一 tag 的 JMHSample_02_BenchmarkModes、JMHSample_08_DeadCode 和 JMHSample_27_Params 分别解释模式/单位、死代码消除与固定输入;源码身份及本机解析依赖见 w...
Kotlin 36:suspend 函数怎样变成可恢复的字节码
35 的事务实验把同步 JDBC 留在固定的工作线程,但外层函数仍写作 `suspend fun insert(...)`。这个修饰符并不表示 SQL 有了一条额外的线程,也不表示持有连接时能安全地随处挂起。理解它要从已经写过的 `executeSyntheticTask` 出发:执行任务步骤时函数可以暂停,步骤返回后又要带着 `TaskId` 和先前状态继续。暂停时这些局部变量去了哪里? 本篇保持编译器/Gradle 插件 2.2.21、language/API 2.2、JDK 21 和 jvmTarget 21。通过 Git tag v2.2.21 定位编译器提交 2146684dcba708e5a304758b41a9e4ec9c7eff71,并在这个提交下阅读 AddContinuationLowering.kt 与 CoroutineCodegen.kt。Gradle --version 所说的 内嵌 Kotlin 2.0.21 是 Gradle 自己的脚本环境,不是编译本工程源码的编译器。以下的类名、局部字段和指令只适用于这份固定生成物;可跨版本引用的是 Kotlin...
Kotlin 35:事务中途挂起,连接归谁
34 的 HTTP 服务把“请求已受理”和“后台任务已完成”分开了;任务状态还停在内存中,服务重启就没了。要保存状态,最容易写错的地方并不只是 `INSERT` 语句,而是:一个协程执行 JDBC 时如果换了线程、取消了 Job、甚至连接抛异常,**谁负责整个事务的提交、回滚和关闭**? 本篇继续使用 Kotlin 2.2.21、coroutines 1.10.2、Gradle 8.14、JDK 21、Ktor 3.3.1,在本机隔离临时目录启动 PostgreSQL 16.15 (Ubuntu 16.15-0ubuntu0.24.04.1);JDBC 驱动冻结为 org.postgresql:postgresql:42.7.7。驱动上游 tag REL42.7.7 指向注解对象 7d5a13de18cc77bdbc76a88de36fc61c00799022,剥离后源码 commit 为 9217ed16cb2918ab1b6b9258ae97e6ede244d8a0。版本组合在本机实际编译和运行,不能由这些版本号推断其他数据库驱动有同样的取消行为。依据是 JDK 21 Con...















