Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/kvlang/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ KV 空间操作已迁至独立 CLI(kvlang-go 仓 cmd/kvspace):
kvspace [--kvspace dsn] <get|mget|set|del|list|tree|dump|watch|notify|clear>

选项:
--kvspace <dsn> kvspace 地址(默认 redis://127.0.0.1:6379;KVLANG_KVSPACE 可覆盖)
--kvspace <dsn> kvspace 地址(默认 redis://127.0.0.1:6379;KVSPACE 可覆盖)
--debug 单步调试模式(设 .debugger="step",agent 通过 kvspace 协议控制)

示例:
Expand Down
6 changes: 3 additions & 3 deletions cmd/kvlang/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ func initDirs(kv kvspace.KVSpace) {
kvspace.MkIndexRecursive(kv, "/vthread/")
}

// defaultKVSpace 返回 kvspace DSN 默认值:KVLANG_KVSPACE 环境变量覆盖,否则本机 redis。
// defaultKVSpace 返回 kvspace DSN 默认值:KVSPACE 环境变量覆盖,否则本机 redis。
func defaultKVSpace() string {
if v := os.Getenv("KVLANG_KVSPACE"); v != "" {
if v := os.Getenv("KVSPACE"); v != "" {
return v
}
return "redis://127.0.0.1:6379"
}

const kvspaceFlagDesc = "kvspace 地址(DSN,如 redis://host:port、art:// 进程内 ART;默认可由 KVLANG_KVSPACE 覆盖)"
const kvspaceFlagDesc = "kvspace 地址(DSN,如 redis://host:port、art:// 进程内 ART;默认可由 KVSPACE 覆盖)"


18 changes: 13 additions & 5 deletions layout/build.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
// 链接 kvspace-durable 的动态库(cdylib)。
// 布局侧只通过 extern "C" ABI 调用 kvspace_durable,不依赖其 Rust API。
// 前置:先在 ../kvspace-durable 执行 `cargo build --release`,产出 libkvspace_durable.so。
// 链接 kvspace 动态库(cdylib)。布局侧只通过 extern "C" ABI 调用。
// 默认链接 kvspace-durable;KVLANG_KVSPACE_LIB=kvspace-c 时链接 kvspace-c(SHM,durable 兼容 ABI)。
fn main() {
let lib_dir = "/home/peng.li24/github.com/array2d/kvspace-durable/target/release";
let lib = std::env::var("KVLANG_KVSPACE_LIB").unwrap_or_else(|_| "kvspace_durable".into());
let (lib_dir, extra_rpath) = if lib == "kvspace-c" {
("/home/peng.li24/github.com/array2d/kvspace/build", true)
} else {
("/home/peng.li24/github.com/array2d/kvspace-durable/target/release", false)
};
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:rustc-link-lib=dylib=kvspace_durable");
println!("cargo:rustc-link-lib=dylib={lib}");
println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}");
if extra_rpath {
println!("cargo:rustc-link-arg=-Wl,-rpath,/home/peng.li24/github.com/array2d/blockmalloc/build");
println!("cargo:rustc-link-arg=-Wl,-rpath,/home/peng.li24/github.com/array2d/slotsboxmalloc/build");
}
}
1 change: 1 addition & 0 deletions runtime-rwirext/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target/
16 changes: 16 additions & 0 deletions runtime-rwirext/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions runtime-rwirext/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "kvlang-rwext-term"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
libc = "0.2"
5 changes: 5 additions & 0 deletions runtime-rwirext/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
all:
cargo build --release

clean:
cargo clean
8 changes: 8 additions & 0 deletions runtime-rwirext/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
let rt = "/home/peng.li24/github.com/array2d/kvlang/runtime/build";
let dual = "/home/peng.li24/github.com/array2d/kvspace-durable/target/release";
println!("cargo:rustc-link-search=native={rt}");
println!("cargo:rustc-link-lib=dylib=kvlang_runtime");
println!("cargo:rustc-link-arg=-Wl,-rpath,{rt}");
println!("cargo:rustc-link-arg=-Wl,-rpath,{dual}");
}
135 changes: 135 additions & 0 deletions runtime-rwirext/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//! term 扩展 runtime:第一个通过 C ABI(kvlang_rwext)嵌入 C runtime 的扩展。
//! 用 Rust 实现,注册 print/println/cerr,常驻 serve 循环执行外部 rwir。

use std::ffi::{c_char, c_int, CStr, CString};
use std::time::Duration;

#[repr(C)]
struct rwext_conn {
_p: [u8; 0],
}

unsafe impl Send for rwext_conn {}

unsafe extern "C" {
fn rwext_connect(dsn: *const c_char) -> *mut rwext_conn;
fn rwext_disconnect(c: *mut rwext_conn);
fn rwext_register(c: *mut rwext_conn, opcode: *const c_char, nr: i32, nw: i32, sig: *const c_char) -> c_int;
fn rwext_list(c: *mut rwext_conn, prefix: *const c_char) -> *mut c_char;
fn rwext_get(c: *mut rwext_conn, key: *const c_char) -> *mut c_char;
fn rwext_set(c: *mut rwext_conn, key: *const c_char, val: *const c_char) -> c_int;
fn rwext_del(c: *mut rwext_conn, key: *const c_char) -> c_int;
fn rwext_print_line(c: *mut rwext_conn, pc: *const c_char, rawnl: *mut c_int, cerr: *mut c_int) -> *mut c_char;
fn rwext_next_pc(pc: *const c_char) -> *mut c_char;
}

struct Op {
name: &'static str,
sig: &'static str,
nr: i32,
nw: i32,
}

const OPS: &[Op] = &[
Op { name: "print", sig: "rwir print(A:any, ...) -> ()", nr: 1, nw: 0 },
Op { name: "println", sig: "rwir println(A:any, ...) -> ()", nr: 1, nw: 0 },
Op { name: "cerr", sig: "rwir cerr(A:any, ...) -> ()", nr: 1, nw: 0 },
];

fn cs(s: &str) -> CString {
CString::new(s).unwrap()
}

fn take(p: *mut c_char) -> String {
if p.is_null() {
String::new()
} else {
let s = unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned();
unsafe { libc::free(p as *mut libc::c_void) };
s
}
}

fn register(c: *mut rwext_conn) {
for op in OPS {
unsafe {
rwext_register(c, cs(op.name).as_ptr(), op.nr, op.nw, cs(op.sig).as_ptr());
}
}
}

fn serve_op(c: *mut rwext_conn, op: &Op) {
let base = format!("/lib/{}", op.name);
let children = take(unsafe { rwext_list(c, cs(&format!("{base}/")).as_ptr()) });
for child in children.split('\n') {
if !child.starts_with(".todo<") || !child.ends_with('>') {
continue;
}
let vid = &child[6..child.len() - 1];
let todo_key = format!("{base}/{child}");
let pcid = take(unsafe { rwext_get(c, cs(&todo_key).as_ptr()) });
let (pc, id) = match pcid.rfind('|') {
Some(i) => (&pcid[..i], &pcid[i + 1..]),
None => (pcid.as_str(), ""),
};

let mut cur = pc.to_string();
loop {
let mut rawnl = 0i32;
let mut is_cerr = 0i32;
let p = unsafe { rwext_print_line(c, cs(&cur).as_ptr(), &mut rawnl, &mut is_cerr) };
if p.is_null() {
break;
}
let line = take(p);
if is_cerr != 0 {
eprint!("{line}");
if rawnl == 0 {
eprintln!();
}
} else {
print!("{line}");
if rawnl == 0 {
println!();
}
}
cur = take(unsafe { rwext_next_pc(cs(&cur).as_ptr()) });
}

let vt_pc = format!("/vthread/{vid}/\u{2025}pc");
unsafe {
rwext_set(c, cs(&vt_pc).as_ptr(), cs(&cur).as_ptr());
let done_key = format!("{base}/.done<{vid}>");
rwext_set(c, cs(&done_key).as_ptr(), cs(id).as_ptr());
rwext_del(c, cs(&todo_key).as_ptr());
}
}
}

struct Conn(*mut rwext_conn);
unsafe impl Send for Conn {}

fn serve(conn: Conn) {
register(conn.0);
loop {
for op in OPS {
serve_op(conn.0, op);
}
std::thread::sleep(Duration::from_millis(500));
}
}

#[no_mangle]
pub extern "C" fn rwext_term_start(dsn: *const c_char) {
let dsn = if dsn.is_null() {
"redis://127.0.0.1:6379".to_string()
} else {
unsafe { CStr::from_ptr(dsn) }.to_string_lossy().into_owned()
};
let c = unsafe { rwext_connect(cs(&dsn).as_ptr()) };
if c.is_null() {
return;
}
let conn = Conn(c);
std::thread::spawn(move || serve(conn));
}
3 changes: 3 additions & 0 deletions runtime/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build/
*.o
test/run
31 changes: 31 additions & 0 deletions runtime/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
CC = cc
CFLAGS = -std=gnu11 -O2 -Wall -fPIC -D_GNU_SOURCE -Iinclude -Isrc
DUAL = /home/peng.li24/github.com/array2d/kvspace-durable/target/release
KVS = /home/peng.li24/github.com/array2d/kvspace/build
BLK = /home/peng.li24/github.com/array2d/blockmalloc/build
SBO = /home/peng.li24/github.com/array2d/slotsboxmalloc/build
SRCS = src/strbuf.c src/xvalue.c src/kv.c src/keytree.c src/rwir.c \
src/vthread.c src/logx.c src/builtin.c src/kvcpu.c src/runtime.c src/rwext.c
OBJS = $(SRCS:.c=.o)
TARGET = build/libkvlang_runtime.so

# 链接哪个 kvspace 库由 KVLANG_KVSPACE_LIB 控制(kvspace-c 或 kvspace_durable,均导出同一 ABI)
KVLANG_KVSPACE_LIB ?= kvspace_durable
ifeq ($(KVLANG_KVSPACE_LIB), kvspace-c)
KVS_LINK = -L$(KVS) -lkvspace-c -L$(BLK) -lblockmalloc -L$(SBO) -lslotsboxmalloc \
-Wl,-rpath,$(KVS) -Wl,-rpath,$(BLK) -Wl,-rpath,$(SBO)
else
KVS_LINK = -L$(DUAL) -lkvspace_durable -Wl,-rpath,$(DUAL)
endif

all: $(TARGET)

$(TARGET): $(OBJS)
mkdir -p build
$(CC) -shared -o $@ $(OBJS) $(KVS_LINK) -lm -pthread -Wl,--export-dynamic

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

clean:
rm -f $(OBJS) $(TARGET)
14 changes: 14 additions & 0 deletions runtime/include/kvlang_runtime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>

typedef struct kvlang_rt kvlang_rt;

kvlang_rt *kvlang_rt_connect(const char *dsn);
void kvlang_rt_disconnect(kvlang_rt *rt);
void *kvlang_rt_kv(kvlang_rt *rt); /* 内部 kv 句柄,供 rwirext 扩展用 */

int kvlang_rt_execute_pc(kvlang_rt *rt, const char *pc);

int kvlang_rt_execute(kvlang_rt *rt, const char *funcname,
const char *const *args, int nargs,
char **ret, char *err, uint32_t err_cap);
35 changes: 35 additions & 0 deletions runtime/include/kvlang_rwext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include <stdint.h>

/* kvlang 扩展 runtime ABI:供第三方语言(Rust/Python/Go)通过 C ABI 嵌入 C runtime,
* 实现自定义 rwirext(如 term 的 print)。opaque handle + C 字符串,不暴露内部结构。 */

typedef struct rwext_conn rwext_conn;

rwext_conn *rwext_connect(const char *dsn);
void rwext_disconnect(rwext_conn *c);

/* 写 /lib/<opcode> = rwir 签名(幂等) */
int rwext_register(rwext_conn *c, const char *opcode, int32_t nr, int32_t nw, const char *sig);

/* 列目录子项,\n 连接返回(malloc,调用方 free);空目录返回 "" */
char *rwext_list(rwext_conn *c, const char *prefix);

/* 读 key 的 value_string(malloc);None 返回 "" */
char *rwext_get(rwext_conn *c, const char *key);

/* 写 char/utf8 值 */
int rwext_set(rwext_conn *c, const char *key, const char *val);

/* 删 key */
int rwext_del(rwext_conn *c, const char *key);

/* 从 pc 解码指令;若 opcode ∈ {print,println,cerr},resolve 全部 reads 并 display,
* 以自身 sep(print 无分隔、println/cerr 空格分隔)连接返回(malloc);
* 非己方指令返回 NULL(调用方应停止 RunSeq)。
* rawnl/cerr 输出该指令的换行/流属性:print→rawnl=1(不换行,stdout);
* println→rawnl=0(换行,stdout);cerr→rawnl=0,cerr=1(换行,stderr)。 */
char *rwext_print_line(rwext_conn *c, const char *pc, int *rawnl, int *cerr);

/* 当前指令的下一条 PC(malloc) */
char *rwext_next_pc(const char *pc);
Loading
Loading