feat: 运行模式/优化/C17/pcmake/多线程 五项功能
1. -run 脚本模式完善
- shebang 脚本 (#!/usr/bin/env pcc -run) 支持
- -rstdin 自定义输入、argv 参数传递
- win32/pcc-run.cmd 便捷启动器
2. -O2 优化等级
- -O0/-O1/-O2/-O3/-Os 全等级验证
- __OPTIMIZE__ 宏正确设置,常量折叠生效
3. C17 全特性
- 默认标准改为 C11 (__STDC_VERSION__=201112L)
- 新增 -std=c17/c18/c99/c89 支持
- _Generic/_Atomic/_Alignas/匿名结构/TLS 全通过
4. pcmake 构建工具 (tools/)
- 纯 C 编写,用 pcc 自身编译
- pcmake.conf 配置: sources/include/libs/output/flags
- CreateProcess 正确处理带空格路径
- pcmake.bat 一键构建 + clean
- 示例: examples/demo 多文件项目
5. 多线程库封装
- win32/include/pthread.h: POSIX 线程 Windows 实现
(CreateThread 封装, 事件驱动条件变量, 惰性初始化互斥锁)
- win32/include/threads.h: C11 标准线程 <threads.h>
- 4线程并发/互斥/条件变量/生产者消费者 测试通过
这个提交包含在:
@@ -0,0 +1,9 @@
|
||||
/* greet.c */
|
||||
#include <stdio.h>
|
||||
#include "greet.h"
|
||||
|
||||
int calc(int a, int b) { return a * b + a - b; }
|
||||
|
||||
void greet(const char *name) {
|
||||
printf("Hello, %s! Welcome to PCC.\n", name);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/* greet.h */
|
||||
#ifndef GREET_H
|
||||
#define GREET_H
|
||||
int calc(int a, int b);
|
||||
void greet(const char *name);
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
/* main.c - demo project */
|
||||
#include <stdio.h>
|
||||
#include "greet.h"
|
||||
|
||||
int main(void) {
|
||||
printf("calc(5,3) = %d\n", calc(5, 3));
|
||||
greet("PCC user");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# pcmake.conf - demo project config
|
||||
sources = main.c greet.c
|
||||
include = .
|
||||
output = demo
|
||||
flags = -O2
|
||||
@@ -880,7 +880,7 @@ LIBPCCAPI PCCState *pcc_new(void)
|
||||
s->pcc_ext = 1;
|
||||
s->nocommon = 1;
|
||||
s->dollars_in_identifiers = 1; /*on by default like in gcc/clang*/
|
||||
s->cversion = 199901; /* default unless -std=c11 is supplied */
|
||||
s->cversion = 201112; /* default C11 (C17 is a defect-fix of C11) */
|
||||
s->warn_implicit_function_declaration = 1;
|
||||
s->warn_discarded_qualifiers = 1;
|
||||
s->ms_extensions = 1;
|
||||
@@ -1992,8 +1992,18 @@ PUB_FUNC int pcc_parse_args(PCCState *s, int *pargc, char ***pargv)
|
||||
s->static_link = 1;
|
||||
break;
|
||||
case PCC_OPTION_std:
|
||||
if (strcmp(optarg, "=c11") == 0 || strcmp(optarg, "=gnu11") == 0)
|
||||
/* C17 is a defect-fix of C11, same __STDC_VERSION__ (201112L) */
|
||||
if (strcmp(optarg, "=c11") == 0 || strcmp(optarg, "=gnu11") == 0
|
||||
|| strcmp(optarg, "=c17") == 0 || strcmp(optarg, "=gnu17") == 0
|
||||
|| strcmp(optarg, "=c18") == 0 || strcmp(optarg, "=gnu18") == 0)
|
||||
s->cversion = 201112;
|
||||
else if (strcmp(optarg, "=c99") == 0 || strcmp(optarg, "=gnu99") == 0)
|
||||
s->cversion = 199901;
|
||||
else if (strcmp(optarg, "=c89") == 0 || strcmp(optarg, "=c90") == 0
|
||||
|| strcmp(optarg, "=gnu89") == 0 || strcmp(optarg, "=gnu90") == 0)
|
||||
s->cversion = 199001;
|
||||
else
|
||||
pcc_warning("unsupported -std%s", optarg);
|
||||
break;
|
||||
case PCC_OPTION_shared:
|
||||
x = PCC_OUTPUT_DLL;
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
@echo off
|
||||
rem ============================================================
|
||||
rem pcmake.bat - build C projects with PCC
|
||||
rem
|
||||
rem Reads pcmake.conf in the current directory and builds
|
||||
rem the project using the pcc compiler.
|
||||
rem
|
||||
rem Usage:
|
||||
rem pcmake build (uses pcmake.conf in cwd)
|
||||
rem pcmake clean remove build artifacts
|
||||
rem ============================================================
|
||||
setlocal
|
||||
set "TOOLSDIR=%~dp0"
|
||||
for %%I in ("%TOOLSDIR%..") do set "ROOT=%%~fI"
|
||||
set "WIN32=%ROOT%\win32"
|
||||
set "PCCEXE=%WIN32%\pcc.exe"
|
||||
|
||||
if not exist "%PCCEXE%" (
|
||||
echo pcmake.bat: cannot find pcc.exe at "%PCCEXE%"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
"%TOOLSDIR%pcmake.exe" -pcc "%PCCEXE%" -B "%WIN32%" %*
|
||||
exit /b %errorlevel%
|
||||
+348
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* pcmake.c - a tiny build tool for PCC
|
||||
*
|
||||
* Reads a "pcmake.conf" file and builds a C project.
|
||||
* Written in pure C, compiles with pcc itself.
|
||||
*
|
||||
* pcmake.conf format:
|
||||
* sources = main.c util.c
|
||||
* include = include
|
||||
* libs = m
|
||||
* output = myprog
|
||||
* flags = -O2 -Wall
|
||||
*
|
||||
* Usage:
|
||||
* pcmake # build using pcmake.conf in current dir
|
||||
* pcmake -f file # use a different config file
|
||||
* pcmake clean # remove build artifacts
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# define IS_DIRSEP(c) ((c) == '/')
|
||||
#endif
|
||||
|
||||
#define MAX_TOKENS 256
|
||||
#define MAX_LINE 1024
|
||||
#define MAX_STR 4096
|
||||
|
||||
typedef struct {
|
||||
char *tokens[MAX_TOKENS];
|
||||
int count;
|
||||
} TokenList;
|
||||
|
||||
static char *g_sources[MAX_TOKENS];
|
||||
static char *g_include[MAX_TOKENS];
|
||||
static char *g_libs[MAX_TOKENS];
|
||||
static char g_output_buf[128] = "a";
|
||||
static char *g_output = g_output_buf; /* never free() this */
|
||||
static char *g_flags[MAX_TOKENS];
|
||||
static int n_sources, n_include, n_libs, n_flags;
|
||||
|
||||
static void die(const char *msg) {
|
||||
fprintf(stderr, "pcmake: %s\n", msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static char *xstrdup(const char *s) {
|
||||
char *p = (char*)malloc(strlen(s) + 1);
|
||||
if (!p) die("out of memory");
|
||||
strcpy(p, s);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* trim whitespace */
|
||||
static char *trim(char *s) {
|
||||
char *end;
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
if (*s == 0) return s;
|
||||
end = s + strlen(s) - 1;
|
||||
while (end > s && isspace((unsigned char)*end)) end--;
|
||||
end[1] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* split a line into tokens on whitespace */
|
||||
static TokenList tokenize(const char *line) {
|
||||
TokenList tl;
|
||||
char buf[MAX_LINE];
|
||||
char *p, *tok;
|
||||
tl.count = 0;
|
||||
strncpy(buf, line, sizeof buf - 1);
|
||||
buf[sizeof buf - 1] = 0;
|
||||
p = buf;
|
||||
while (*p) {
|
||||
while (*p && isspace((unsigned char)*p)) p++;
|
||||
if (!*p) break;
|
||||
tok = p;
|
||||
while (*p && !isspace((unsigned char)*p)) p++;
|
||||
if (*p) *p++ = 0;
|
||||
if (tl.count < MAX_TOKENS)
|
||||
tl.tokens[tl.count++] = xstrdup(tok);
|
||||
}
|
||||
return tl;
|
||||
}
|
||||
|
||||
static void add_token(char **arr, int *n, const char *tok) {
|
||||
if (*n < MAX_TOKENS)
|
||||
arr[(*n)++] = xstrdup(tok);
|
||||
}
|
||||
|
||||
/* parse one config line: key = value tokens */
|
||||
static void parse_line(const char *line) {
|
||||
char buf[MAX_LINE];
|
||||
char *eq, *key, *val;
|
||||
TokenList tl;
|
||||
int i;
|
||||
|
||||
strncpy(buf, line, sizeof buf - 1);
|
||||
buf[sizeof buf - 1] = 0;
|
||||
key = trim(buf);
|
||||
if (*key == 0 || *key == '#') /* empty or comment */
|
||||
return;
|
||||
eq = strchr(key, '=');
|
||||
if (!eq)
|
||||
return; /* ignore lines without '=' */
|
||||
*eq = 0;
|
||||
val = trim(eq + 1);
|
||||
key = trim(key);
|
||||
|
||||
tl = tokenize(val);
|
||||
if (strcmp(key, "sources") == 0) {
|
||||
for (i = 0; i < tl.count; i++) add_token(g_sources, &n_sources, tl.tokens[i]);
|
||||
} else if (strcmp(key, "include") == 0) {
|
||||
for (i = 0; i < tl.count; i++) add_token(g_include, &n_include, tl.tokens[i]);
|
||||
} else if (strcmp(key, "libs") == 0) {
|
||||
for (i = 0; i < tl.count; i++) add_token(g_libs, &n_libs, tl.tokens[i]);
|
||||
} else if (strcmp(key, "output") == 0) {
|
||||
if (tl.count > 0) {
|
||||
strncpy(g_output_buf, tl.tokens[0], sizeof g_output_buf - 1);
|
||||
g_output_buf[sizeof g_output_buf - 1] = 0;
|
||||
g_output = g_output_buf;
|
||||
}
|
||||
} else if (strcmp(key, "flags") == 0) {
|
||||
for (i = 0; i < tl.count; i++) add_token(g_flags, &n_flags, tl.tokens[i]);
|
||||
}
|
||||
/* unknown keys ignored */
|
||||
for (i = 0; i < tl.count; i++) free(tl.tokens[i]);
|
||||
}
|
||||
|
||||
static void read_config(const char *fname) {
|
||||
FILE *f = fopen(fname, "r");
|
||||
char line[MAX_LINE];
|
||||
if (!f) die("cannot open config file (expected 'pcmake.conf')");
|
||||
while (fgets(line, sizeof line, f))
|
||||
parse_line(line);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* compile one source file into an object file */
|
||||
static int run_command(const char *cmd); /* fwd decl */
|
||||
|
||||
static int compile_file(const char *pcc, const char *bopt, const char *src, const char *obj) {
|
||||
char cmd[MAX_STR];
|
||||
int len = 0, i;
|
||||
len += snprintf(cmd + len, sizeof cmd - len, "\"%s\" %s \"%s\" -c -o \"%s\"",
|
||||
pcc, bopt, src, obj);
|
||||
for (i = 0; i < n_include; i++)
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " -I \"%s\"", g_include[i]);
|
||||
for (i = 0; i < n_flags; i++)
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " %s", g_flags[i]);
|
||||
printf(" pcc -c %s\n", src);
|
||||
return run_command(cmd);
|
||||
}
|
||||
|
||||
/* replace .c extension with .o */
|
||||
static void objname(const char *src, char *out, int outsize) {
|
||||
const char *slash = NULL, *p;
|
||||
const char *base;
|
||||
const char *dot;
|
||||
int n;
|
||||
/* find last slash of either kind */
|
||||
for (p = src; *p; p++) {
|
||||
if (*p == '/' || *p == '\\')
|
||||
slash = p;
|
||||
}
|
||||
base = slash ? slash + 1 : src;
|
||||
dot = strrchr(base, '.');
|
||||
n = (dot && dot > base) ? (int)(dot - base) : (int)strlen(base);
|
||||
if (n >= outsize) n = outsize - 1;
|
||||
memcpy(out, base, n);
|
||||
out[n] = 0;
|
||||
strncat(out, ".o", outsize - n - 1);
|
||||
}
|
||||
|
||||
static int file_exists(const char *f) {
|
||||
FILE *p = fopen(f, "r");
|
||||
if (p) { fclose(p); return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* run a command line; Windows uses CreateProcess to handle
|
||||
quoted paths with spaces correctly (system() misbehaves) */
|
||||
static int run_command(const char *cmd) {
|
||||
#ifdef _WIN32
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
DWORD code = 1;
|
||||
char *cmdline = (char*)malloc(strlen(cmd) + 1);
|
||||
if (!cmdline) return -1;
|
||||
strcpy(cmdline, cmd);
|
||||
memset(&si, 0, sizeof si);
|
||||
si.cb = sizeof si;
|
||||
if (CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, &code);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
} else {
|
||||
fprintf(stderr, "pcmake: CreateProcess failed (%lu): %s\n",
|
||||
(unsigned long)GetLastError(), cmd);
|
||||
code = 1;
|
||||
}
|
||||
free(cmdline);
|
||||
return (int)code;
|
||||
#else
|
||||
return system(cmd);
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *cfg = "pcmake.conf";
|
||||
const char *pcc = "pcc";
|
||||
char bopt[600] = ""; /* "-B <dir>" or empty when pcc.bat used */
|
||||
char objbuf[512];
|
||||
char cmd[MAX_STR];
|
||||
int i, len, rc;
|
||||
int is_clean = 0;
|
||||
int any_failed = 0;
|
||||
|
||||
/* parse args */
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
|
||||
cfg = argv[++i];
|
||||
} else if (strcmp(argv[i], "-pcc") == 0 && i + 1 < argc) {
|
||||
pcc = argv[++i];
|
||||
} else if (strcmp(argv[i], "-B") == 0 && i + 1 < argc) {
|
||||
snprintf(bopt, sizeof bopt, "-B \"%s\"", argv[++i]);
|
||||
} else if (strcmp(argv[i], "clean") == 0) {
|
||||
is_clean = 1;
|
||||
}
|
||||
}
|
||||
|
||||
read_config(cfg);
|
||||
|
||||
/* locate pcc:
|
||||
1. if -pcc was given, use it
|
||||
2. else if pcc.bat wrapper exists in cwd, use it (it sets -B)
|
||||
3. else search common install locations for pcc.exe */
|
||||
if (strcmp(pcc, "pcc") == 0) {
|
||||
const char *cands[] = {
|
||||
"pcc.bat",
|
||||
"pcc.exe",
|
||||
"C:\\pcc\\pcc.exe",
|
||||
"C:\\Program Files\\pcc\\pcc.exe",
|
||||
"C:\\Program Files (x86)\\pcc\\pcc.exe",
|
||||
NULL
|
||||
};
|
||||
int k;
|
||||
for (k = 0; cands[k]; k++) {
|
||||
if (file_exists(cands[k])) {
|
||||
pcc = cands[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_clean) {
|
||||
/* remove object files and output */
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
objname(g_sources[i], objbuf, sizeof objbuf);
|
||||
printf(" rm %s\n", objbuf);
|
||||
remove(objbuf);
|
||||
}
|
||||
{
|
||||
char outbuf[512];
|
||||
strncpy(outbuf, g_output, sizeof outbuf - 1);
|
||||
outbuf[sizeof outbuf - 1] = 0;
|
||||
#ifdef _WIN32
|
||||
strncat(outbuf, ".exe", sizeof outbuf - strlen(outbuf) - 1);
|
||||
#endif
|
||||
printf(" rm %s\n", outbuf);
|
||||
remove(outbuf);
|
||||
}
|
||||
printf("pcmake: cleaned\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n_sources == 0) {
|
||||
fprintf(stderr, "pcmake: no sources specified in %s\n", cfg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("pcmake: building %s (%d source files)\n", g_output, n_sources);
|
||||
|
||||
/* compile each source */
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
objname(g_sources[i], objbuf, sizeof objbuf);
|
||||
rc = compile_file(pcc, bopt, g_sources[i], objbuf);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "pcmake: compile failed: %s\n", g_sources[i]);
|
||||
any_failed = 1;
|
||||
}
|
||||
}
|
||||
if (any_failed) {
|
||||
fprintf(stderr, "pcmake: build failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* link */
|
||||
len = 0;
|
||||
len += snprintf(cmd + len, sizeof cmd - len, "\"%s\" %s", pcc, bopt);
|
||||
for (i = 0; i < n_include; i++)
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " -I \"%s\"", g_include[i]);
|
||||
for (i = 0; i < n_flags; i++)
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " %s", g_flags[i]);
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
objname(g_sources[i], objbuf, sizeof objbuf);
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " \"%s\"", objbuf);
|
||||
}
|
||||
for (i = 0; i < n_libs; i++)
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " -l%s", g_libs[i]);
|
||||
{
|
||||
char outbuf[512];
|
||||
strncpy(outbuf, g_output, sizeof outbuf - 1);
|
||||
outbuf[sizeof outbuf - 1] = 0;
|
||||
#ifdef _WIN32
|
||||
strncat(outbuf, ".exe", sizeof outbuf - strlen(outbuf) - 1);
|
||||
#endif
|
||||
len += snprintf(cmd + len, sizeof cmd - len, " -o \"%s\"", outbuf);
|
||||
}
|
||||
printf(" link %s\n", g_output);
|
||||
rc = run_command(cmd);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "pcmake: link failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("pcmake: build OK -> %s%s\n", g_output,
|
||||
#ifdef _WIN32
|
||||
".exe"
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* pthread.h - POSIX threads for Windows (PCC)
|
||||
*
|
||||
* A lightweight pthread implementation on top of Win32
|
||||
* thread APIs. Provides the commonly used subset of POSIX
|
||||
* threads sufficient for most C programs.
|
||||
*
|
||||
* Condition variables are implemented with Win32 events,
|
||||
* so no modern kernel32 APIs are required.
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#ifndef _PCC_PTHREAD_H
|
||||
#define _PCC_PTHREAD_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------- types ---------- */
|
||||
|
||||
typedef HANDLE pthread_t;
|
||||
typedef DWORD pthread_attr_t;
|
||||
|
||||
typedef struct {
|
||||
CRITICAL_SECTION cs;
|
||||
} pthread_mutex_t;
|
||||
|
||||
typedef struct {
|
||||
int dummy;
|
||||
} pthread_mutexattr_t;
|
||||
|
||||
typedef struct {
|
||||
HANDLE event; /* auto-reset event used for signaling */
|
||||
CRITICAL_SECTION cs; /* protects waiters count */
|
||||
int waiters;
|
||||
} pthread_cond_t;
|
||||
|
||||
typedef struct {
|
||||
int dummy;
|
||||
} pthread_condattr_t;
|
||||
|
||||
typedef struct {
|
||||
int done;
|
||||
} pthread_once_control_t;
|
||||
|
||||
#define PTHREAD_ONCE_INIT { 0 }
|
||||
/* Zero-init is valid for our event-based cond; for mutex we
|
||||
lazily initialize the CRITICAL_SECTION on first use. */
|
||||
#define PTHREAD_MUTEX_INITIALIZER { { 0 } }
|
||||
#define PTHREAD_COND_INITIALIZER { NULL, { 0 }, 0 }
|
||||
|
||||
typedef DWORD pthread_key_t;
|
||||
|
||||
/* ---------- thread creation ---------- */
|
||||
|
||||
static DWORD WINAPI pthread_wrapper(LPVOID arg)
|
||||
{
|
||||
void *(*fn)(void *) = ((void **)arg)[0];
|
||||
void *data = ((void **)arg)[1];
|
||||
void *result;
|
||||
free(arg);
|
||||
result = fn(data);
|
||||
/* return value stored in thread exit code */
|
||||
return (DWORD)(uintptr_t)result;
|
||||
}
|
||||
|
||||
static int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg)
|
||||
{
|
||||
void **box = (void **)malloc(2 * sizeof(void *));
|
||||
DWORD tid;
|
||||
HANDLE h;
|
||||
(void)attr;
|
||||
if (!box) return -1;
|
||||
box[0] = (void *)start_routine;
|
||||
box[1] = arg;
|
||||
h = CreateThread(NULL, 0, pthread_wrapper, box, 0, &tid);
|
||||
if (!h) {
|
||||
free(box);
|
||||
return -1;
|
||||
}
|
||||
*thread = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_join(pthread_t thread, void **retval)
|
||||
{
|
||||
DWORD code = 0;
|
||||
if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
|
||||
return -1;
|
||||
GetExitCodeThread(thread, &code);
|
||||
CloseHandle(thread);
|
||||
if (retval)
|
||||
*retval = (void *)(uintptr_t)code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pthread_t pthread_self(void)
|
||||
{
|
||||
return GetCurrentThread();
|
||||
}
|
||||
|
||||
static int pthread_equal(pthread_t t1, pthread_t t2)
|
||||
{
|
||||
return t1 == t2;
|
||||
}
|
||||
|
||||
static void pthread_exit(void *retval)
|
||||
{
|
||||
ExitThread((DWORD)(uintptr_t)retval);
|
||||
}
|
||||
|
||||
static int pthread_detach(pthread_t thread)
|
||||
{
|
||||
CloseHandle(thread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- mutex (with lazy init for static initializers) ---------- */
|
||||
|
||||
static void pthread_mutex_ensure(pthread_mutex_t *m)
|
||||
{
|
||||
/* CRITICAL_SECTION is initialized when DebugInfo != NULL */
|
||||
if (m->cs.DebugInfo == NULL)
|
||||
InitializeCriticalSection(&m->cs);
|
||||
}
|
||||
|
||||
static int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a)
|
||||
{
|
||||
(void)a;
|
||||
memset(&m->cs, 0, sizeof m->cs);
|
||||
InitializeCriticalSection(&m->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_mutex_destroy(pthread_mutex_t *m)
|
||||
{
|
||||
if (m->cs.DebugInfo)
|
||||
DeleteCriticalSection(&m->cs);
|
||||
memset(&m->cs, 0, sizeof m->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_mutex_lock(pthread_mutex_t *m)
|
||||
{
|
||||
pthread_mutex_ensure(m);
|
||||
EnterCriticalSection(&m->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_mutex_trylock(pthread_mutex_t *m)
|
||||
{
|
||||
pthread_mutex_ensure(m);
|
||||
return TryEnterCriticalSection(&m->cs) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int pthread_mutex_unlock(pthread_mutex_t *m)
|
||||
{
|
||||
LeaveCriticalSection(&m->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- condition variable (event-based) ---------- */
|
||||
|
||||
static int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a)
|
||||
{
|
||||
(void)a;
|
||||
c->event = CreateEventA(NULL, FALSE, FALSE, NULL); /* auto-reset */
|
||||
if (!c->event) return -1;
|
||||
InitializeCriticalSection(&c->cs);
|
||||
c->waiters = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_cond_destroy(pthread_cond_t *c)
|
||||
{
|
||||
if (c->event) CloseHandle(c->event);
|
||||
DeleteCriticalSection(&c->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
|
||||
{
|
||||
EnterCriticalSection(&c->cs);
|
||||
c->waiters++;
|
||||
LeaveCriticalSection(&c->cs);
|
||||
|
||||
LeaveCriticalSection(&m->cs);
|
||||
/* wait for signal; spurious wakeups possible, caller must re-check */
|
||||
WaitForSingleObject(c->event, INFINITE);
|
||||
EnterCriticalSection(&m->cs);
|
||||
|
||||
EnterCriticalSection(&c->cs);
|
||||
if (c->waiters > 0) c->waiters--;
|
||||
LeaveCriticalSection(&c->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m,
|
||||
const struct timespec *abstime)
|
||||
{
|
||||
DWORD ms = INFINITE;
|
||||
if (abstime) {
|
||||
FILETIME ft;
|
||||
ULARGE_INTEGER ul;
|
||||
unsigned __int64 target, now;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
ul.LowPart = ft.dwLowDateTime;
|
||||
ul.HighPart = ft.dwHighDateTime;
|
||||
now = ul.QuadPart; /* 100ns since 1601 */
|
||||
target = ((unsigned __int64)abstime->tv_sec + 11644473600ULL)
|
||||
* 10000000ULL + abstime->tv_nsec / 100;
|
||||
if (target <= now)
|
||||
return 110; /* ETIMEDOUT */
|
||||
ms = (DWORD)((target - now) / 10000);
|
||||
}
|
||||
|
||||
EnterCriticalSection(&c->cs);
|
||||
c->waiters++;
|
||||
LeaveCriticalSection(&c->cs);
|
||||
|
||||
LeaveCriticalSection(&m->cs);
|
||||
{
|
||||
DWORD r = WaitForSingleObject(c->event, ms);
|
||||
EnterCriticalSection(&m->cs);
|
||||
EnterCriticalSection(&c->cs);
|
||||
if (c->waiters > 0) c->waiters--;
|
||||
LeaveCriticalSection(&c->cs);
|
||||
if (r == WAIT_TIMEOUT) return 110;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_cond_signal(pthread_cond_t *c)
|
||||
{
|
||||
EnterCriticalSection(&c->cs);
|
||||
if (c->waiters > 0)
|
||||
SetEvent(c->event);
|
||||
LeaveCriticalSection(&c->cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_cond_broadcast(pthread_cond_t *c)
|
||||
{
|
||||
int n;
|
||||
EnterCriticalSection(&c->cs);
|
||||
n = c->waiters;
|
||||
LeaveCriticalSection(&c->cs);
|
||||
/* set event multiple times; auto-reset wakes one waiter per set */
|
||||
while (n-- > 0)
|
||||
SetEvent(c->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- once ---------- */
|
||||
|
||||
static int pthread_once(pthread_once_control_t *once,
|
||||
void (*init_routine)(void))
|
||||
{
|
||||
if (!once->done) {
|
||||
once->done = 1;
|
||||
init_routine();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- keys (TLS) ---------- */
|
||||
|
||||
static int pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
|
||||
{
|
||||
(void)destructor;
|
||||
*key = TlsAlloc();
|
||||
return (*key == TLS_OUT_OF_INDEXES) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
return TlsFree(key) ? 0 : -1;
|
||||
}
|
||||
|
||||
static void *pthread_getspecific(pthread_key_t key)
|
||||
{
|
||||
return TlsGetValue(key);
|
||||
}
|
||||
|
||||
static int pthread_setspecific(pthread_key_t key, const void *value)
|
||||
{
|
||||
return TlsSetValue(key, (LPVOID)value) ? 0 : -1;
|
||||
}
|
||||
|
||||
/* ---------- attributes (minimal) ---------- */
|
||||
|
||||
static int pthread_attr_init(pthread_attr_t *a) { (void)a; return 0; }
|
||||
static int pthread_attr_destroy(pthread_attr_t *a) { (void)a; return 0; }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PCC_PTHREAD_H */
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* threads.h - C11 threads for Windows (PCC)
|
||||
*
|
||||
* Implements the C11 <threads.h> interface on top of the
|
||||
* Win32 thread APIs. C11 threads are a thin wrapper around
|
||||
* threads, mutexes, condition variables and TLS.
|
||||
*
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#ifndef _PCC_THREADS_H
|
||||
#define _PCC_THREADS_H
|
||||
|
||||
#include <time.h>
|
||||
#include "pthread.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------- types ---------- */
|
||||
|
||||
typedef pthread_t thrd_t;
|
||||
typedef pthread_mutex_t mtx_t;
|
||||
typedef pthread_cond_t cnd_t;
|
||||
typedef pthread_key_t tss_t;
|
||||
typedef int (*thrd_start_t)(void *);
|
||||
typedef void (*tss_dtor_t)(void *);
|
||||
|
||||
typedef struct {
|
||||
int type; /* unused */
|
||||
} mtx_t_attr;
|
||||
|
||||
/* thread start wrapper: convert int(*)(void*) to void*(*)(void*) */
|
||||
static void *thrd_wrapper(void *arg)
|
||||
{
|
||||
thrd_start_t fn = (thrd_start_t)((void **)arg)[0];
|
||||
void *data = ((void **)arg)[1];
|
||||
int result = fn(data);
|
||||
free(arg);
|
||||
return (void *)(intptr_t)result;
|
||||
}
|
||||
|
||||
/* ---------- constants ---------- */
|
||||
|
||||
enum {
|
||||
thrd_success = 0,
|
||||
thrd_nomem = 1,
|
||||
thrd_timedout = 2,
|
||||
thrd_busy = 3,
|
||||
thrd_error = 4
|
||||
};
|
||||
|
||||
enum {
|
||||
mtx_plain = 0,
|
||||
mtx_timed = 1,
|
||||
mtx_recursive = 2
|
||||
};
|
||||
|
||||
#define TSS_DTOR_ITERATIONS 4
|
||||
|
||||
/* ---------- thread functions ---------- */
|
||||
|
||||
static int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
|
||||
{
|
||||
void **box = (void **)malloc(2 * sizeof(void *));
|
||||
if (!box) return thrd_nomem;
|
||||
box[0] = (void *)func;
|
||||
box[1] = arg;
|
||||
if (pthread_create(thr, NULL, thrd_wrapper, box) != 0) {
|
||||
free(box);
|
||||
return thrd_error;
|
||||
}
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
static int thrd_equal(thrd_t a, thrd_t b) { return pthread_equal(a, b); }
|
||||
static thrd_t thrd_current(void) { return pthread_self(); }
|
||||
static int thrd_sleep(const struct timespec *duration, struct timespec *remaining)
|
||||
{
|
||||
DWORD ms = (DWORD)(duration->tv_sec * 1000 + duration->tv_nsec / 1000000);
|
||||
(void)remaining;
|
||||
Sleep(ms);
|
||||
return 0;
|
||||
}
|
||||
static void thrd_yield(void) { Sleep(0); }
|
||||
static void thrd_exit(int res) { pthread_exit((void *)(intptr_t)res); }
|
||||
static int thrd_detach(thrd_t t) { return pthread_detach(t) ? thrd_error : thrd_success; }
|
||||
|
||||
static int thrd_join(thrd_t t, int *res)
|
||||
{
|
||||
void *ret;
|
||||
if (pthread_join(t, &ret) != 0) return thrd_error;
|
||||
if (res) *res = (int)(intptr_t)ret;
|
||||
return thrd_success;
|
||||
}
|
||||
|
||||
/* ---------- mutex functions ---------- */
|
||||
|
||||
static int mtx_init(mtx_t *mtx, int type)
|
||||
{
|
||||
(void)type;
|
||||
return pthread_mutex_init(mtx, NULL) ? thrd_error : thrd_success;
|
||||
}
|
||||
static void mtx_destroy(mtx_t *mtx) { pthread_mutex_destroy(mtx); }
|
||||
static int mtx_lock(mtx_t *mtx) { return pthread_mutex_lock(mtx) ? thrd_error : thrd_success; }
|
||||
static int mtx_trylock(mtx_t *mtx) { return pthread_mutex_trylock(mtx) ? thrd_busy : thrd_success; }
|
||||
static int mtx_unlock(mtx_t *mtx) { return pthread_mutex_unlock(mtx) ? thrd_error : thrd_success; }
|
||||
static int mtx_timedlock(mtx_t *mtx, const struct timespec *at)
|
||||
{
|
||||
(void)mtx; (void)at;
|
||||
return mtx_lock(mtx);
|
||||
}
|
||||
|
||||
/* ---------- condition variable functions ---------- */
|
||||
|
||||
static int cnd_init(cnd_t *cond) { return pthread_cond_init(cond, NULL) ? thrd_error : thrd_success; }
|
||||
static void cnd_destroy(cnd_t *cond) { pthread_cond_destroy(cond); }
|
||||
static int cnd_signal(cnd_t *cond) { return pthread_cond_signal(cond) ? thrd_error : thrd_success; }
|
||||
static int cnd_broadcast(cnd_t *cond) { return pthread_cond_broadcast(cond) ? thrd_error : thrd_success; }
|
||||
static int cnd_wait(cnd_t *cond, mtx_t *mtx) { return pthread_cond_wait(cond, mtx) ? thrd_error : thrd_success; }
|
||||
static int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *at)
|
||||
{
|
||||
int r = pthread_cond_timedwait(cond, mtx, at);
|
||||
if (r == 110) return thrd_timedout;
|
||||
return r ? thrd_error : thrd_success;
|
||||
}
|
||||
|
||||
/* ---------- TSS (thread-specific storage) ---------- */
|
||||
|
||||
static int tss_create(tss_t *key, tss_dtor_t dtor)
|
||||
{
|
||||
(void)dtor;
|
||||
return pthread_key_create(key, NULL) ? thrd_error : thrd_success;
|
||||
}
|
||||
static void tss_delete(tss_t key) { pthread_key_delete(key); }
|
||||
static void *tss_get(tss_t key) { return pthread_getspecific(key); }
|
||||
static int tss_set(tss_t key, void *val) { return pthread_setspecific(key, val) ? thrd_error : thrd_success; }
|
||||
|
||||
/* ---------- call_once ---------- */
|
||||
|
||||
typedef pthread_once_control_t once_flag;
|
||||
#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
|
||||
static void call_once(once_flag *flag, void (*func)(void)) { pthread_once(flag, func); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PCC_THREADS_H */
|
||||
@@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
rem ============================================================
|
||||
rem pcc-run.cmd - run a C source file directly (script mode)
|
||||
rem
|
||||
rem Usage:
|
||||
rem pcc-run hello.c [args...]
|
||||
rem pcc-run myscript [args...] (no extension works too)
|
||||
rem
|
||||
rem This compiles the C file in memory and runs it immediately,
|
||||
rem just like a script. No .exe file is produced.
|
||||
rem ============================================================
|
||||
setlocal
|
||||
set "PCCDIR=%~dp0"
|
||||
|
||||
if "%~1"=="" (
|
||||
echo Usage: pcc-run script.c [arguments...]
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
"%PCCDIR%pcc.exe" -B "%PCCDIR%win32" -I "%PCCDIR%include" -L "%PCCDIR%lib" -run %*
|
||||
exit /b %errorlevel%
|
||||
在新工单中引用
屏蔽一个用户