원문 캡처 · github.com
원문 캡처 · github.com

CWIST는 C17로 작성한 웹 프레임워크 겸 애플리케이션 서버다. HTTP/1.1·HTTP/2·HTTP/3(QUIC), WebSocket, WebTransport, 하이브리드 포스트양자 TLS, SQLite ORM을 포함하며, 기본 이벤트 기반 리액터와 연결별 스레드를 쓰는 Classic pool 중 작업에 맞는 방식을 선택할 수 있다. 프로젝트는 직접 빌드하거나 Homebrew로 설치할 수 있지만, 성능 수치는 한 커밋과 CI 환경에서 얻은 결과이며 보편적인 보장은 아니라고 밝힌다.

설치와 첫 실행

Linux·macOS·BSD에서는 저장소에 포함된 의존성을 이용해 빌드할 수 있다. 설치 단계의 sudo make install은 선택 사항이며, PREFIX로 설치 경로를 바꿀 수 있다.

sh
git clone https://github.com/c4punks/CWIST.git
cd cwist
make
sudo make install # optional, installs to /usr/local (override with PREFIX=/opt/cwist)

macOS와 Linuxbrew 사용자는 CWIST tap에서 설치하거나 tap을 별도로 추가하지 않고 설치할 수 있다.

sh
brew tap c4punks/cwist
brew install c4punks/cwist/cwist
sh
brew install c4punks/cwist/cwist

Homebrew 공식은 릴리스 소스 tarball(dist/cwist-<version>.tar.gz)을 빌드한다. 번들 의존성이 포함되며 libcwist.a, 공개 헤더, cwist 명령줄 도구, cwist.pc pkg-config 메타데이터를 Homebrew 경로에 설치한다.

다음은 최소 서버 예제다.

c
#include <cwist/app.h>

static void hello(cwist_http_request *req, cwist_http_response *res) {
 (void)req;
 cwist_sstring_assign(res->body, "Hello from CWIST!");
}

int main(void) {
 cwist_app *app = cwist_app_create();
 cwist_app_get(app, "/", hello);
 cwist_app_listen(app, 8080);
 cwist_app_destroy(app);
 return 0;
}
sh
gcc -o server main.c -lcwist -lssl -lcrypto -lz -lzstd -lbrotlienc -lbrotlicommon -luriparser -lcjson -ldl -lpthread -lm
./server

데이터베이스, 포스트양자 TLS, 메트릭, RDBMS 자동 감지를 함께 설정하는 예제는 다음과 같다.

c
#include <cwist/app.h>

static void hello(cwist_http_request *req, cwist_http_response *res) {
 (void)req;
 cwist_sstring_assign(res->body, "Hello from CWIST!");
}

int main(void) {
 cwist_app *app = cwist_app_create();

 /* SQLite + ORM-ready database */
 cwist_app_use_db(app, ":memory:");

 /* Post-Quantum TLS (hybrid X25519MLKEM768) */
 cwist_app_use_pqc_layer(app, true);

 /* Observability endpoints */
 cwist_app_enable_metrics(app);
 cwist_app_enable_healthz(app);

 /* Auto-detect PostgreSQL / MySQL / MariaDB on localhost */
 cwist_app_auto_rdbms(app, 5432);

 cwist_app_get(app, "/", hello);

 cwist_app_listen(app, 8080);
 cwist_app_destroy(app);
 return 0;
}

실행 방식과 성능 수치

기본값인 CWIST reactor는 이벤트 루프 하나에서 다수의 연결을 다룬다. 연결 수가 스레드 수와 분리되며, 연결마다 대기 스레드를 두지 않는다. Linux에서는 io_uring을 사용하고 설정에 실패하면 epoll로 전환한다. macOS와 BSD에서는 kqueue를 사용한다. 기본 리액터를 쓰려면 환경 변수를 설정하지 않거나 CWIST_C1M_MODE=1로 지정한다.

CWIST Classic pool은 CWIST_C1M_MODE=0으로 선택한다. 연결마다 필요할 때 분리(detached) 스레드를 만들고, 작업을 해당 작업자 스레드에서 처리한다. 프로젝트는 짧은 요청이 몰리는 상황에서 낮은 중앙값 지연을 중시할 때 이 방식을 고려하라고 안내한다. 연결이 많고 대부분 유휴 상태인 서비스에서는 연결마다 스레드를 두지 않는 리액터가 적합하다는 설명이다.

게시된 wrk -t12 -c400 -d10s 결과는 10초 예열 후 측정한 단일 실행이다. 아래 값은 각 서버의 처리량, 지연, RSS, 컨텍스트 전환 수다.

서버요청/초지연RSSCsw
CWIST Classic pool1096482.17ms (P90 4.55ms, P99 9.12ms, P99.999 47.88ms)59680KiB1087549
CWIST reactor1419332.75ms (P90 4.85ms, P99 7.44ms, P99.999 12.95ms)22176KiB110861
Actix1294343.03ms (P90 5.14ms, P99 7.88ms, P99.999 15.24ms)12264KiB156288
Axum1104433.54ms (P90 5.99ms, P99 8.84ms, P99.999 15.62ms)14244KiB199862
Gin (Go)787556.70ms (P90 15.89ms, P99 36.37ms, P99.999 78.68ms)30056KiB288184
Spring Boot439979.07ms (P90 11.80ms, P99 19.04ms, P99.999 98.58ms)1324904KiB211617

프로젝트는 Classic pool이 이 실행에서 중앙값부터 p99까지 Axum보다 앞서지만, 가장 긴 꼬리 구간에서는 Axum의 지연 분포가 더 좁다고 설명한다. 그 차이는 아직 해결 과제로 남아 있다. CWIST 내부 튜닝 변형은 cooperative-queuing.md와 benchmarks/webserver.json에 기록했으며, 비교 행에는 더 이상 표시하지 않는다고 한다.

수치는 특정 커밋, 부하 프로필, CI 환경에서 측정했다. 실행마다 GitHub가 배정하는 CPU 모델이 달라지며, 프로젝트는 이 차이가 대부분의 코드 변경보다 결과에 더 큰 영향을 준다고 경고한다. 따라서 수치는 보편적 성능 보장이 아니다. 벤치마크 절차와 JVM 옵션, 공정성 설정은 webserver-benchmark.md에 공개돼 있다. 지연 분포 그래프는 서버 추세와 지연 분포에서 볼 수 있다.

CPU별 비교도 실행 횟수와 중앙값을 함께 기록했다. 같은 열 안에서만 비교하도록 안내한다.

실행기 CPU실행 횟수CWIST Classic msCWIST msAxum msCWIST req/sAxum req/s
AMD EPYC 7763 64-Core Processor442.052.943.55140,049110,485
AMD EPYC 9V74 80-Core Processor171.632.522.64181,522149,122
AMD EPYC 9V45 96-Core Processor111.262.071.99218,093196,379
Intel(R) Xeon(R) 6973P-C50.971.741.64301,233241,483
INTEL(R) XEON(R) PLATINUM 8573C41.171.851.98251,901200,955
Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz11.412.432.39215,862165,616

벤치마크의 Spring 환경은 OpenJDK 25.0.4.1(2026-08-18 LTS), Spring Boot 3.2.3, Spring WebFlux와 Reactor Netty의 native epoll 조합이다. G1GC와 JDK 25 Leyden AOT를 사용했고 가상 스레드는 비활성화했다. JVM 설정은 다음과 같다.

text
-Xms1024m -Xmx1024m -XX:+UseG1GC -XX:GCTimeRatio=99 -XX:G1HeapRegionSize=1m -XX:+AlwaysPreTouch -XX:CompileThreshold=1500 -XX:CICompilerCount=4 -Djava.security.egd=file:/dev/urandom -Djava.net.preferIPv4Stack=true -Dio.netty.allocator.type=pooled -Dio.netty.leakDetection.level=disabled -Dio.netty.buffer.checkBounds=false -Dio.netty.buffer.checkAccessible=false -Dreactor.netty.ioWorkerCount=4 -Xlog:gc*:file=/tmp/spring_gc.log:time,uptime,level,tags -XX:+AOTClassLinking -XX:AOTCache=/tmp/spring_bench/app.aot (JEP 483 + JEP 514 single-step AOT) 

기능과 플랫폼

CWIST는 평문 C로 작성됐고 정적 링크를 지원한다. 프로젝트가 열거한 기능에는 HTTP/1.1, HTTP/2(h2/h2c), HTTP/3(QUIC), WebSocket, WebTransport, BoringSSL 기반 TLS, JWT, DB Crypt, 라우팅과 미들웨어, 메트릭, 상태 확인, 요청 제한이 포함된다. HTTP/3는 lsquic를 사용한다. 서버 측 세션을 제공하며, dev 브랜치에는 LSQUIC PR #629를 바탕으로 한 실험용 네이티브 C 클라이언트가 있다.

포스트양자 TLS 계층은 cwist_app_use_pqc_layer(app, true) 한 번으로 하이브리드 X25519MLKEM768을 활성화한다. 프로젝트 설명에 따르면 이 설정은 TLS 1.3을 최소 버전으로 강제하고 레거시 TLS 1.0~1.2 암호군을 제외한다. 서버 측 제로카피 I/O, io_uring·epoll·kqueue 리액터, 잠금 없는 작업 큐, libttak의 세대별 아레나 할당자도 포함한다.

데이터 기능으로는 SQLite3와 ORM, 메모리에서 읽고 커밋할 때 디스크에 동기화하는 Nuke DB, PostgreSQL·MySQL·MariaDB 자동 감지가 있다. 자동 감지는 포트 번호를 추측하지 않고 PostgreSQL StartupMessage를 보내거나 MySQL 핸드셰이크 패킷을 읽어 프로토콜 지문으로 구분한다. 라우팅은 Express 스타일 :param 경로, Mux 라우터, 연결형 미들웨어를 지원한다고 소개한다.

gRPC·Protobuf 기능에는 단항 및 스트리밍 경로, 점진적 프레이밍, 상태 확인·리플렉션 서비스, Trailers-Only 오류 응답, proto 모델·인코더·디코더 생성이 포함된다. 스칼라, repeated, 중첩 메시지, enum을 지원하며, 채널 클라이언트는 DNS·IPv4·IPv6 해석, pick_first·round_robin 로드 밸런싱, 백오프·pushback·스로틀링·투명 재시도를 포함한 gRFC A6 재시도를 제공한다고 한다. HTML 빌더, CSS 컴포저, 템플릿 엔진, JSON 빌더도 제공한다.

CWIST는 Linux, macOS, BSD에서 빌드된다. HTTP/3 서버와 네이티브 클라이언트는 모든 플랫폼에서 비차단 UDP 소켓을 사용한다. Linux는 epoll, BSD 계열은 이식 가능한 폴링 경로를 사용한다. ECN 메타데이터는 호스트가 필요한 소켓 옵션을 제공할 때만 활성화되므로, 선택적 API가 없어도 HTTP/3 빌드가 막히지 않는다는 설명이다.

연결 규모와 제약

프로젝트는 2026-09-29에 main의 2f5b4786에서 benchmarks/web-frameworks/cwist_bench와 CWIST_WORKERS=12로 연결 규모를 재측정했다. Linux의 AMD Ryzen 5 5600X(6코어·12스레드), RAM 62GB, Debian 13의 Linux 6.12.107, GCC 14.2.0 환경에서 루프백으로 실행했다. 리액터의 결과는 다음과 같다. RSS는 12개 fork 작업자 프로세스의 최고 VmHWM 합계다.

규모결과소요 시간서버 최고 RSS
C10K10,000 / 10,000 연결 수립 및 응답(100%)약 0.9초약 301MiB
C100K100,000 / 100,000 응답(100%)약 12.1초약 2.35GiB
C1M (8×125K)1,000,000 / 1,000,000 응답(100%)클라이언트 프로세스당 약 26.5~27.5초약 22.9GiB

현재 연결 하나가 서버 메모리를 약 24KB 사용한다. 2026년 8월에 공개한 C10K·C100K·C1M의 약 111MB·124MB·244MB 수치는 연결 경로를 이후 변경하기 전에 측정한 값이며, 현재 C1M에서는 연결당 메모리가 주요 비용이라고 프로젝트는 설명한다.

같은 장비와 클라이언트에서 측정한 Classic 경로는 다음과 같다.

규모결과소요 시간서버 최고 RSS
C10K10,000 / 10,000 응답(100%)약 1.0초약 976MiB
C100K100,000 / 100,000 응답(100%)약 12.8초약 9.0GiB

Classic에서 C100K를 유지하려면 10만 연결에 하나씩 스레드가 필요하다. pids.max 또는 TasksMax를 10만보다 높게 설정해야 하며, 데스크톱 앱의 프로세스 범위 제한은 약 7만6천인 경우가 많다. 기본 kernel.threads-max는 충분하다고 설명한다. 10만 개의 256KiB 스택에는 약 26GB의 가상 커밋 예산이 필요하고, 이 가운데 약 9.2GB가 실제 상주한다. RAM×비율과 스왑이 부족하면 vm.overcommit_ratio를 높일 수 있다고 안내한다. 이 방식으로 C1M은 불가능하며, 백만 스레드는 threads-max를 넘기므로 대규모 연결에는 리액터를 쓰도록 설계했다는 설명이다.

측정 클라이언트는 tests/bench_cxm.c다. 다중 프로세스 epoll 부하 클라이언트가 여러 127.0.0.x 주소에 소스 포트를 결정적으로 순환 배정하고, TIME_WAIT 구간에서 재실행해도 충돌하지 않도록 모든 클라이언트 소켓에 SO_REUSEADDR을 적용했다. C100K 이상을 위한 커널 조건은 다음과 같다.

  • ulimit -n 1050000을 설정한다. C1M에서는 클라이언트와 서버 양쪽에서 연결마다 파일 디스크립터가 하나씩 필요하므로 fs.file-max가 8M 이상이어야 한다.
  • 루프백 트래픽도 conntrack을 거친다. 기본값 262144는 연결 수를 약 263K 부근에서 제한하므로 net.netfilter.nf_conntrack_max=4194304가 필요하다.
  • 클라이언트에서 net.ipv4.ip_local_port_range="1024 65535"를 설정한다.

현재 비동기 경로에는 제약이 있다. HTTPS는 여전히 요청 단계를 풀 스레드에서 실행한다. 핸들러가 소켓의 전송 속도보다 빠르게 쓰면 CWIST_HTTP_TIMEOUT_MS가 정한 범위 안에서 리액터 스레드가 기다린다. 유휴 상태의 평문 keep-alive 연결은 아직 타이머로 정리하지 않는다.

v3.7.1부터는 유휴 TLS 연결 때문에 풀 스레드가 대기하지 않도록 연결을 프로세스별 epoll 집합에 보관하고 데이터가 도착하면 풀로 돌려보낸다. CWIST_HTTPS_IDLE_TIMEOUT_MS를 사용할 수 있고 CWIST_HTTPS_PARK=0으로 기능을 끌 수 있다. 이전에는 HTTPS 풀 스레드 수만큼만 TLS 연결을 동시에 처리했으며, 395,729개 연결을 보유한 상태에서 최대 25개만 처리한 사례가 있었다. 같은 호스트에서 12개 작업자로 fly.board를 통해 측정한 결과는 TLS HTTP/1.1에서 1,000,000개 연결을 모두 보유·서비스하며 서버 PSS 19.9GB, TLS HTTP/2에서는 30.1GB였다. 연결은 초당 8,000개씩 열었고 실패는 없었다. 초당 20,000개를 열 때는 전체 핸드셰이크의 약 37%가 45초 핸드셰이크 제한을 넘겼다. 자세한 내용은 fly.board 확장성 벤치마크에 있다.

HTTPS 수용은 cwist_https_dispatch()를 거친다. 비차단 SSL_accept를 한 번 시도하고, 완료되지 않은 핸드셰이크는 Linux에서 단일 셰퍼드 스레드가 관리하는 전용 epoll 집합에 보관한다. 다른 플랫폼은 블로킹 풀 경로로 대체된다. 보관한 핸드셰이크는 컴파일 시 정한 CWIST_HTTPS_HANDSHAKE_TIMEOUT_MS(45초)가 지나면 정리한다. 이 방식은 동기 핸드셰이크가 연결 폭주 중 수용 루프를 멈춰 수용 큐 넘침, 조용히 버려지는 핸드셰이크, 'phantom' ESTABLISHED 클라이언트가 생기던 문제를 대체했다. 백만 요청의 루프백 연결 폭주(h2load)에서 이전 방식은 몇 분 안에 교착했지만 셰퍼드 방식은 실행을 완료했다. 셰퍼드 뒤에서 대기하다 느려진 핸드셰이크를 h2load 자체 타임아웃으로 회수하는 과정에서 요청이 버려진다고 설명한다. 셰퍼드는 이후 CWIST_HTTPS_HS_MAX_SHARDS개의 스레드로 분할됐고, 파일 디스크립터 해시로 배정한다. 기본 분할 수는 요청 작업자 수에서 정한다. 연결 폭주 부하에 맞춘 CWIST_HTTPS_HS_SHARDS 조정이 다음 성능 후보이며, make test_https가 이 경로를 시험한다.

개발 도구와 빌드 설정

새 프로젝트에는 .cwpro 개발 명령이 포함된다. 프로젝트 디렉터리에서 watcher를 실행하면 영향받는 번역 단위를 계산하고 점진적으로 빌드한 뒤, 빌드가 성공했을 때만 앱을 재시작한다. Linux에서는 inotify, BSD·macOS에서는 kqueue로 변경을 감지하며, 변경을 놓치지 않도록 수정 시각 스냅샷과 폴링 대체 경로도 사용한다. 빌드가 실패하면 이전 프로세스가 계속 실행된다.

sh
cwist watcher

CI 또는 재빌드만 수행하려면 cwist watcher --no-run, 폴링을 강제하려면 --poll을 쓴다. 매니페스트의 dev.debounce_ms와 dev.stop_timeout_ms는 원자적 저장 이벤트 병합과 정상 종료 대기 시간을 제어한다.

make install은 $(PREFIX)/bin에 cwist CLI도 설치한다. CLI로 프로젝트를 만들고 실행하거나, 핫 리로드·OpenAPI·Protobuf 생성·프로젝트 정보 확인을 할 수 있다.

sh
# Scaffold a project: src/main.c, Makefile, and a demo.cwpro manifest
cwist new project demo --directory ~/src
cd ~/src/demo

# Build and run like the generated Makefile does
make # cc src/main.c -lcwist -lpthread (needs CWIST installed)
./bin/demo

# Develop with hot reload (incremental rebuild + zero-downtime restart)
cwist watcher

# Generate OpenAPI 3.1 from Doxygen @openapi.* route annotations
cwist openapi

# Generate C models and gRPC method paths from proto3 definitions
cwist proto api.proto

# Inspect the project manifest and detected routes
cwist describe

cwist-project/v1 형식의 .cwpro 매니페스트는 watcher와 생성기가 함께 사용하는 단일 기준이다. 진입점, include 경로, 개발용 디바운스·종료 제한 시간, 경로 탐색 범위를 지정해 별도 설정 없이 여러 컴퓨터에서 빌드를 재현하도록 한다.

libcwist.a는 CWIST 객체만 담는 얇은 정적 아카이브다. make install PREFIX=/opt/cwist는 빌드한 외부 하위 모듈을 /opt/cwist/lib/cwist에 별도로 설치하고 공개 헤더는 /opt/cwist/include/cwist/vendor에 설치한다. 두 헤더 디렉터리와 두 라이브러리 디렉터리를 모두 사용해 링크하면 제3자 아카이브를 따로 교체할 수 있고, 하나로 합친 아카이브에서 생기는 중복 심볼을 피할 수 있다.

sh
gcc -I/opt/cwist/include -I/opt/cwist/include/cwist/vendor main.c \
 -L/opt/cwist/lib -L/opt/cwist/lib/cwist -lcwist \
 -llsquic -lssl -lcrypto -lnats_static -lttak -lcjson -luriparser \
 -lz -lzstd -ldl -lpthread -lm -lstdc++

스테이징 패키지에는 DESTDIR을 사용할 수 있다. 예를 들어 make install PREFIX=/usr DESTDIR=/tmp/cwist-package처럼 지정한다. 정적 링크에서는 CWIST를 먼저, 의존성을 뒤에 둬야 한다.

항상 필요한 링크 플래그는 -lcwist(프레임워크), -llsquic -lssl -lcrypto(HTTP/3·QUIC·TLS와 번들 BoringSSL), -lz(zlib), -lzstd(Zstandard), -lbrotlienc -lbrotlicommon(Brotli), -lnats_static -lttak -luriparser -lcjson(번들 NATS·libttak·URI 파싱·JSON), -ldl(RDBMS 자동 연결), -lpthread(POSIX 스레드), -lm(libttak에서 사용하는 수학 라이브러리)다. HTTP/2에는 선택적으로 -lnghttp2, RDBMS 자동 연결의 와이어 프로토콜 탐색에는 -lcurl이 필요하다.

v3.2부터 make install이 cwist.pc를 설치하므로 pkg-config로 플래그를 한 줄로 가져올 수 있다.

sh
gcc -o server main.c $(pkg-config --cflags --libs cwist)

정적 링크는 선택 라이브러리인 -lcurl, -lnghttp2까지 포함하도록 다음처럼 지정한다.

sh
pkg-config --cflags --libs --static cwist

Makefile에서 쓸 수 있는 예시는 다음과 같다.

make
CWIST_LIBS := -lcwist \
 -lssl -lcrypto \
 -lz -lzstd -lbrotlienc -lbrotlicommon \
 -luriparser -lcjson \
 -ldl -lpthread -lm

# Append optional libs if present on the build host
CWIST_LIBS += $(shell pkg-config --libs libnghttp2 2>/dev/null)
CWIST_LIBS += $(shell pkg-config --libs libcurl 2>/dev/null || echo -lcurl)

your_target: your_source.c
 $(CC) -o $@ $< $(CWIST_LIBS)

Debian·Ubuntu에서는 brotlienc와 brotlicommon이 libbrotli-dev에, Fedora·RHEL에서는 brotli-devel에 들어 있다. Zstandard 패키지는 각각 libzstd-dev, libzstd-devel이다.

설정과 데이터 기능

설정 로더는 .env 파일과 환경 변수를 읽으며 선택적인 접두사를 지원한다.

c
cwist_config *cfg = cwist_config_create();
cwist_config_load_file(cfg, ".env");

const char *db_url = cwist_config_get(cfg, "DATABASE_URL");
int workers = cwist_config_get_int(cfg, "WORKERS", 4);
bool debug = cwist_config_get_bool(cfg, "DEBUG", false);

cwist_config_destroy(cfg);

접두사가 있는 환경 변수는 다음처럼 불러온다.

c
cwist_config_load_env(cfg, "CWIST_");
/* Now CWIST_PORT=8080 is accessible as cwist_config_get(cfg, "CWIST_PORT") */

.env 형식은 =로 키와 값을 나눈다. 큰따옴표로 값을 감쌀 수 있고 키와 값의 앞뒤 공백은 자동으로 제거한다. #으로 시작하는 행은 주석이다.

text
# Lines starting with # are comments
PORT=8080
DATABASE_URL="sqlite3:data.db"
DEBUG=true
WORKERS=4

런타임은 접두사 없이도 일부 환경 변수를 직접 읽는다. 예를 들어 CWIST_WORKERS는 이벤트 루프에 들어가기 전에 fork할 작업자 프로세스 수로 기본값은 1이다. CWIST_C1M_MODE는 기본값이 true이며 true·1은 리액터, false·0은 Classic pool을 선택한다. CWIST_HTTP_BATCH는 이벤트 루프 한 차례에 전달할 HTTP/1.1 파이프라인 요청 수로 기본값은 16, 범위는 [1, 1024]이며 초과 요청은 리액터 연속 작업으로 미룬다. CWIST_HTTP_YIELD_BATCH는 기본적으로 batch에서 정하며 배치 안에서 요청을 처리한 뒤 리액터 큐에 다시 올릴 때까지의 단위를 제어한다. CWIST_ASYNC_DEBUG를 설정하면 비동기 경로와 리액터의 드문 재등록·제출·SQ 실패를 stderr에 기록하며, 평상시에는 출력하지 않는다. CWIST_ASYNC_STRICT=1이면 디스패치 확인 전에 생산자가 res->deferred나 res->async를 바꾸거나 cwist_async_defer() 없이 res->deferred를 설정하는 등 지연 응답을 잘못 사용했을 때 경고 대신 중단하고 코어 파일을 남긴다. 디스패치는 어느 경우에도 해당 필드를 신뢰하지 않는다. 전체 CWIST_* 변수의 기본값과 v4 지원 여부는 runtime-config.md에 있다.

Linux에서 리액터는 liburing 의존성 없이 원시 시스템 호출로 io_uring을 사용해 src/sys/io/reactor.c의 epoll_wait 경로를 대체한다. 기본 경로는 일회성 IORING_OP_POLL_ADD를 설정하고 깨어난 작업자가 I/O를 직접 처리한다. 실제 io_uring 링이 있는 리액터에서는 비동기 HTTP 수신 대기를 단일 IORING_OP_RECV로 처리해 POLL과 recv를 합칠 수 있다(issue #179, PR #187). 연결별 학습 플래그로 파이프라이닝을 하지 않는 클라이언트는 기존 작업 수를 유지한다. CWIST_RX_URING=0은 POLL 경로를 복구하고 CWIST_LATENCY_PROBE=1은 리액터별 등록부터 디스패치까지의 시간과 콜백 시간 히스토그램을 기록한다(issue #166). io_uring 설정이 실패하면 동일한 동작을 유지하며 epoll로, macOS·BSD에서는 kqueue로 대체한다.

프로젝트는 준비 알림이 오면 여러 중간 완료 단계를 매번 사용자 공간 링 버퍼로 전달하지 않고 작업자가 이벤트를 직접 처리한다고 설명한다. 작업자별 동시성 제한은 작업이 무한히 쌓이는 것을 막고 포화 상태에서 부하를 버려 꼬리 지연이 커지는 것을 억제한다. 작업자 스택에서 요청을 처리하면 다단계 힙 콜백 체인보다 캐시 지역성을 높이고, 스레드 고정 실행과 세대별 아레나가 지연 변동을 줄인다는 것이 프로젝트의 설명이다.

Nuke DB는 디스크의 SQLite 파일을 sqlite3_deserialize로 메모리에 적재하고 PRAGMA integrity_check를 실행한 뒤 모든 쿼리를 메모리에서 처리한다. 각 COMMIT은 백그라운드 WAL 동기화를 발생시킨다. 초기화에 실패하면 읽기 전용 디스크 보호 모드로 전환한다.

c
cwist_nuke_init("data.db", 5000); /* 5-second auto-sync interval */
cwist_db *db = cwist_nuke_get_db();

libttak 기반 성능 도구에는 정적 자산과 BDR 데이터를 한 번에 해제해 RSS 단편화를 줄이는 세대별 아레나 할당자, 임계 구역을 고정하고 오래된 버퍼를 회수하는 EBR, TLS 쓰기 버퍼와 WebSocket 프레임용 신호 안전·캐시 정렬 아레나, 단일 원자 교환으로 작업을 넣고 분리된 노드를 재사용하는 잠금 없는 작업 큐가 포함된다.

WebTransport 서버는 다음 API로 설정한다.

c
cwist_app_use_webtransport(app, my_wt_handler);

CWIST가 HTTP/3의 CONNECT 협상을 처리하고 2xx 응답 뒤 스트림을 유지하며 cwist_webtransport_read/write/flush/close/open_bidi/open_uni API를 제공한다.

RDBMS 자동 연결은 로컬 TCP 포트를 지정한다.

c
if (cwist_app_auto_rdbms(app, 5432)) {
 /* PostgreSQL, MySQL, or MariaDB runtime mounted */
}

안정성, 문서와 라이선스

CWIST 4.0 전까지는 마이너 릴리스에서 공개 API가 바뀔 수 있으므로 운영 환경에서는 정확한 태그를 고정하라고 프로젝트는 권한다. 초안 단계 기능은 dev에서 바뀌며 태그 사이에 나타나거나 사라질 수 있고 안정성을 보장하지 않는다. 모든 푸시는 ASan·UBSan과 -Werror로 테스트하고, 고정 기준선과 비교하는 h2spec HTTP/2 적합성 차이 검사도 실행한다. 회귀가 있으면 빌드에 실패하며 알려진 적합성 차이는 scripts/ci/h2spec-baseline.txt에 기록한다.

문서는 전체 안내, 튜토리얼, 가이드, API 참조, 빠른 API 참조, Doxygen HTML, 로드맵에서 확인할 수 있다. 튜토리얼은 실행 가능한 main.c, CMakeLists.txt, 안내 README가 포함된 30개 모듈(tutorials/01..30)로 구성된다. 가이드에는 CRUD 블로그, NATS 통합, WebTransport 서버가 있다.

실행 가능한 예제는 저장소의 example 디렉터리에 있다. 최소 서버, HTTP 단계별 예제, SQLite, 암호화 열 데이터베이스, JWT 인증, WebSocket 오셀로 게임, 처리량 데모인 rps-showcase, JSON 빌더, HTML, 템플릿, 실험용 WebTransport 앱이 포함된다. 전체 목록은 예제 표에 있다. CWIST를 사용하는 운영 배포 사례는 fly.board다.

CWIST는 BoringSSL, lsquic, libttak, SQLite3를 저장소에 포함하며, cJSON·uriparser·zlib·Brotli·Zstandard도 의존성으로 열거한다. 각 구성 요소는 자체 라이선스를 유지한다. BoringSSL과 cnats는 Apache-2.0, lsquic은 MIT(일부 Chromium 파생 부분은 BSD-3-Clause), cJSON과 multipart-parser-c는 MIT, libttak과 uriparser는 BSD-3-Clause, SQLite는 퍼블릭 도메인이다. uriparser는 라이브러리만 해당하며 테스트 스위트는 LGPL-2.1-or-later이고 링크하지 않는다. 정적 링크는 연결한 바이너리에도 각 구성 요소의 라이선스 의무를 전파하므로 배포 전 NOTICE.md와 각 하위 모듈의 라이선스를 확인해야 한다. 질문·설계 논의·기여 조정은 CWIST Discord에서 진행한다.