32 Commits
Author SHA1 Message Date
tidwall 9e8e19e4bc Allow all tests to run before fail
Vanilla C CI / build (push) Has been cancelled
2024-07-25 19:34:58 -07:00
tidwall a15af5b486 Ensure clang llvm versions align 2024-07-25 19:24:15 -07:00
tidwall 5a22d6f322 Update sco, support for cosmopolitan
This commit adds basic support for the Cosmopolitan libc, which
allows for portable executables.

https://github.com/jart/cosmopolitan

See #18
2024-07-25 08:12:27 -07:00
tidwall 86a9a26fde Update tests run script
Move user CFLAGS to be included at the end of the cc options.
Ensure zig-cc on Mac uses -O1 to workaround a quirk.
2024-07-25 06:52:30 -07:00
tidwall b53d0a29fe Add C++ support 2024-07-25 06:52:06 -07:00
tidwall 1bc011c83a Change opaque signatures for sync types
Removes the alignas keyword in favor of int64 and pointer types.
2024-07-25 06:49:37 -07:00
Josh 9967411f02 Explicitly define handler
This fixes a bug with zig-cc

See #20
2024-07-25 00:55:23 +00:00
tidwall 6827ebaf18 Update README
Remove metion of c11. See #17
2024-07-04 08:46:15 -07:00
tidwall 4788e0eef7 Fix typos in neco.h
From @yeh-sudo in #16
2024-06-25 06:08:19 -07:00
tidwall 43725ee34a Add port scanner example 2024-05-31 19:48:14 -07:00
tidwall 7d03e805d8 Align inner types too 2024-05-13 20:22:15 -07:00
tidwall 052e4c5e5d Fix worker test
Github actions were running low on memory causing ENOMEM errors
when running the test_work test.

This commit lowers the number of background coroutines in hopes
to alleviate the memory pressure.
2024-05-13 05:55:58 -07:00
tidwall d808a81cd9 Faster neco_start
This commit alters the behavior of neco_start in order to shorten
the time it takes to start a coroutine and switching back to the
parent coroutine. Making for faster neco_start.

See #12
2024-05-13 05:21:53 -07:00
tidwall 5f48ee01b6 Add gettid for emscripten 2024-05-11 18:13:51 -07:00
Josh Baker 3d4da9f841 Merge pull request #13 from deckarep/fix-opaque-type-alignment
fixes alignment on opaque types as discueed with @tidwall
2024-05-11 18:04:27 -07:00
Ralph Caraveo d829d94d8d fixes alignment on opaque types as discueed with @tidwall 2024-05-11 14:20:30 -07:00
tidwall d5d9d4fe82 Adjust neco_main specifiers to remove warnings
Remove the static inline specifier from the __neco_main signature
to avoid old-style-declaration warning when -Wextra is provided.
2024-04-24 19:57:20 -07:00
tidwall 2c3852d9e6 Update llco
See #9
See https://github.com/tidwall/llco/commit/b6a5a5b886e8a3e06806d741b84571c0b1e1f8d0
2024-04-19 19:41:30 -07:00
tidwall e7a1b30928 Fixed flag toggle 2024-04-15 10:30:44 -07:00
tidwall a11fd9993f Add option to toggle workers
This also disables read workers by default
2024-04-15 10:00:09 -07:00
tidwall 3a6550fcab Fix example 2024-04-14 19:21:57 -07:00
tidwall 66c497c7d3 Ignore unused argument 2024-04-14 06:02:09 -07:00
Josh Baker fdfe46b008 Merge pull request #4 from eltociear/patch-1
Update README.md
2024-04-13 18:16:34 -07:00
tidwall f7728975d6 Support older glibc
Replace gettid() with syscall(SYS_gettid)

See #6
2024-04-13 17:57:15 -07:00
Ikko Eltociear Ashimine a49a93d411 Update README.md
Addtional -> Additional
2024-04-12 03:19:02 +09:00
tidwall 5364a33b50 Increase concurrent workers 2024-04-11 07:02:15 -07:00
tidwall 7a96dfc202 Updated worker defaults 2024-04-11 06:55:12 -07:00
tidwall a9af544d34 Use neco_join instead of neco_yield 2024-04-11 05:34:16 -07:00
tidwall 6947b2939a Add package file 2024-04-10 06:08:41 -07:00
Josh Baker ec26837192 Merge pull request #2 from j4cobgarby/fix-detach-segfault
fix the possibility for pthread_detach(0)
2024-04-10 05:27:43 -07:00
Jacob Garby ff9a69c6a4 fix the possibility for pthread_detach(0) 2024-04-10 14:23:03 +02:00
Josh Baker 8258e56e5e Update README.md 2024-04-09 10:20:59 -07:00
13 changed files with 232 additions and 134 deletions
+2
View File
@@ -14,3 +14,5 @@ tmp.*
*.testdata
*.sqlite3
*.db
*.elf
*.dbg
+2
View File
@@ -0,0 +1,2 @@
file neco.c
file neco.h
+4 -3
View File
@@ -21,10 +21,11 @@ easy.
- [Synchronization](docs/API.md#channels): channels, generators, mutexes, condition variables, and waitgroups.
- Support for [deadlines and cancelation](docs/API.md#deadlines-and-cancelation).
- [Posix friendly](docs/API.md#posix-wrappers) interface using file descriptors.
- Addtional APIs for [networking](docs/API.md#networking-utilities),
- Additional APIs for [networking](docs/API.md#networking-utilities),
[signals](docs/API.md#signals), [random data](docs/API.md#random-number-generator), [streams](docs/API.md#streams-and-buffered-io), and [buffered I/O](docs/API.md#streams-and-buffered-io).
- Lightweight runtime with a fair and deterministic [scheduler](#the-scheduler).
- [Fast](#fast-context-switching) user-space context switching. Uses assembly in most cases.
- Stackful coroutines that are nestable, with their life times fully managed by the scheduler.
- Cross-platform. Linux, Mac, FreeBSD. _(Also WebAssembly and Windows with [some limitations](#platform-notes))_.
- Single file amalgamation. No dependencies.
- [Test suite](tests/README.md) with 100% coverage using sanitizers and [Valgrind](https://valgrind.org).
@@ -47,7 +48,7 @@ concurrency models like async/await.
## Using
Just drop the "neco.c" and "neco.h" files into your project. Uses standard C11 so most modern C compilers should work.
Just drop the "neco.c" and "neco.h" files into your project. Most modern C compilers should work.
```sh
cc -c neco.c
@@ -168,7 +169,7 @@ int neco_main(int argc, char *argv[]) {
*arg1 = 1;
neco_start(coroutine, 5, &arg0, arg1, &(int){2}, NULL, "hello world");
free(arg2);
free(arg1);
neco_sleep(NECO_SECOND);
printf("first done\n");
+4 -2
View File
@@ -1,2 +1,4 @@
import github.com/tidwall/sco v0.1.0
import github.com/tidwall/stack v0.1.0
import github.com/tidwall/sco v0.2.1
sum 9610203b4ce6f6e3cac862a3deb99cb418acac7c sco.c
sum 92baa4119d14dbb2b1effcd2eaeb4adcba8befc1 sco.h
Vendored
+12 -7
View File
@@ -135,6 +135,11 @@ static void llco_exit(void) {
#error LLCO_ASM must not be defined
#endif
#if defined(__COSMOCC__) && !defined(LLCO_NOASM)
// Cosmopolitan has issues with asm code
#define LLCO_NOASM
#endif
// Passing the entry function into assembly requires casting the function
// pointer to an object pointer, which is forbidden in the ISO C spec but
// allowed in posix. Ignore the warning attributed to this requirement when
@@ -1169,7 +1174,7 @@ const char *llco_method(void *caps) {
}
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) && !defined(_WIN32) && \
!defined(LLCO_NOUNWIND)
!defined(LLCO_NOUNWIND) && !defined(__COSMOCC__)
#include <unwind.h>
#include <string.h>
@@ -1182,7 +1187,7 @@ struct llco_dlinfo {
void *dli_saddr; /* Address of nearest symbol */
};
#ifdef __linux__
#if defined(__linux__) && !defined(_GNU_SOURCE)
int dladdr(const void *, void *);
#endif
@@ -1853,11 +1858,11 @@ static void sco_entry(void *udata) {
co->prev = co;
co->next = co;
if (sco_cur) {
// Reschedule the coroutine that started this one
sco_list_push_back(&sco_yielders, co);
sco_list_push_back(&sco_yielders, sco_cur);
sco_nyielders += 2;
sco_switch(false, false);
// Reschedule the coroutine that started this one immediately after
// all running coroutines, but before any yielding coroutines, and
// continue running the started coroutine.
sco_list_push_back(&sco_runners, sco_cur);
sco_nrunners++;
}
sco_cur = co;
if (sco_user_entry) {
+10 -1
View File
@@ -12,6 +12,12 @@
#include <string.h>
#include <pthread.h>
#ifdef WORKER_STATIC
#define WORKER_API static
#else
#define WORKER_API
#endif
#define WORKER_DEF_TIMEOUT INT64_C(1000000000) // one second
#define WORKER_DEF_MAX_THREADS 2
#define WORKER_DEF_MAX_THREAD_ENTRIES 32
@@ -46,6 +52,7 @@ struct worker {
void (*free)(void*);
};
WORKER_API
void worker_free(struct worker *worker) {
if (worker) {
if (worker->threads) {
@@ -70,6 +77,7 @@ void worker_free(struct worker *worker) {
}
}
WORKER_API
struct worker *worker_new(struct worker_opts *opts) {
// Load options
int nthreads = opts ? opts->max_threads : 0;
@@ -144,10 +152,10 @@ static void *worker_entry(void *arg) {
ts.tv_sec += 1;
pthread_cond_timedwait(&thread->cond, &thread->mu, &ts);
if (thread->len == 0) {
thread->th = 0;
if (!thread->end) {
pthread_detach(thread->th);
}
thread->th = 0;
thread->end = false;
break;
}
@@ -164,6 +172,7 @@ static void *worker_entry(void *arg) {
/// @param udata any user data
/// @return true for success or false if no worker is available.
/// @return false for invalid arguments. Worker and work must no be null.
WORKER_API
bool worker_submit(struct worker *worker, int64_t pin, void(*work)(void *udata),
void *udata)
{
+48
View File
@@ -0,0 +1,48 @@
// Port scanning
//
// Thanks to @sigidagi for recommending this example.
// See https://github.com/tidwall/neco/issues/14
#include <stdio.h>
#include <unistd.h>
#include "../neco.h"
void co_dial_port(int argc, void** argv) {
const char *host = argv[0]; // host
int port = *(int*)argv[1]; // port
neco_waitgroup *wg = argv[2]; // waitgroup
// Dial the port with a one second deadline
char addr[256];
snprintf(addr, sizeof(addr), "%s:%d", host, port);
int fd = neco_dial_dl("tcp", addr, neco_now() + NECO_SECOND);
if (fd < 0) {
printf("%-5d FAIL\t%s\n", port, neco_strerror(neco_lasterr()));
} else {
printf("%-5d OK\n", port);
close(fd);
}
// Notify the waitgroup that we are done.
neco_waitgroup_done(wg);
}
int neco_main(int argc, char** argv) {
const char *host = "scanme.nmap.org";
int ports[] = { 22, 80, 8080, 443 };
// Use a waitgroup to synchronize the coroutines
neco_waitgroup wg;
neco_waitgroup_init(&wg);
// Dial all the ports
printf("%s\n", host);
for (int i = 0; i < sizeof(ports)/sizeof(int); i++) {
neco_waitgroup_add(&wg, 1);
neco_start(co_dial_port, 3, host, &ports[i], &wg);
}
// Wait for all coroutines to finish
neco_waitgroup_wait(&wg);
return 0;
}
+99 -52
View File
@@ -17,7 +17,8 @@ NECO_MAXCAP // Max stack_group capacity
NECO_GAPSIZE // Size of gap (guard) pages
NECO_SIGSTKSZ // Size of signal stack on main thread
NECO_BURST // Number of read attempts before waiting, def: disabled
NECO_MAXWORKER // Max number of worker threads, def: 2
NECO_MAXWORKERS // Max number of worker threads, def: 64
NECO_MAXIOWORKERS // Max number of io threads, def: 2
// Additional options that activate features
@@ -27,8 +28,11 @@ NECO_NOSTACKFREELIST // Do not use a stack free list
NECO_NOPOOL // Do not use a coroutine and channel pools
NECO_USEHEAPSTACK // Allocate stacks on the heap using malloc
NECO_NOSIGNALS // Disable signal handling
NECO_USEROUNDROBINPIN // Use Round-robin when pinning background work
NECO_NOWORKERS // Disable all worker threads, work will run in coroutine
NECO_USEREADWORKERS // Use read workers, disabled by default
NECO_USEWRITEWORKERS // Use write workers, enabled by default on Linux
NECO_NOREADWORKERS // Disable all read workers
NECO_NOWRITEWORKERS // Disable all write workers
*/
// Windows and Webassembly have limited features.
@@ -41,7 +45,7 @@ NECO_NOWORKERS // Disable all worker threads, work will run in coroutine
#define DEF_BURST -1
#define NECO_USEHEAPSTACK
#define NECO_NOSIGNALS
#define NECO_NOWORKER
#define NECO_NOWORKERS
#else
#define DEF_STACKSIZE 8388608
#define DEF_DEFCAP 4
@@ -49,8 +53,15 @@ NECO_NOWORKERS // Disable all worker threads, work will run in coroutine
#define DEF_GAPSIZE 1048576
#define DEF_SIGSTKSZ 1048576
#define DEF_BURST -1
#define DEF_MAXWORKER 2
#define DEF_MAXWORKERS 64
#define DEF_MAXRINGSIZE 32
#define DEF_MAXIOWORKERS 2
#endif
#ifdef __linux__
#ifndef NECO_USEWRITEWORKERS
#define NECO_USEWRITEWORKERS
#endif
#endif
#ifndef NECO_STACKSIZE
@@ -71,18 +82,29 @@ NECO_NOWORKERS // Disable all worker threads, work will run in coroutine
#ifndef NECO_BURST
#define NECO_BURST DEF_BURST
#endif
#ifndef NECO_MAXWORKER
#define NECO_MAXWORKER DEF_MAXWORKER
#ifndef NECO_MAXWORKERS
#define NECO_MAXWORKERS DEF_MAXWORKERS
#endif
#ifndef NECO_MAXRINGSIZE
#define NECO_MAXRINGSIZE DEF_MAXRINGSIZE
#endif
#ifndef NECO_MAXIOWORKERS
#define NECO_MAXIOWORKERS DEF_MAXIOWORKERS
#endif
#ifdef NECO_TESTING
#if NECO_BURST == -1
#if NECO_BURST <= 0
#undef NECO_BURST
#define NECO_BURST 1
#endif
#if NECO_MAXWORKERS > 8
#undef NECO_MAXWORKERS
#define NECO_MAXWORKERS 8
#endif
#if NECO_MAXRINGSIZE > 4
#undef NECO_MAXRINGSIZE
#define NECO_MAXRINGSIZE 4
#endif
#endif
// The following is only needed when LLCO_NOASM or LLCO_STACKJMP is defined.
@@ -114,7 +136,7 @@ NECO_NOWORKERS // Disable all worker threads, work will run in coroutine
#include "deps/aat.h"
#include "deps/stack.h"
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
#include "deps/worker.h"
#endif
@@ -122,6 +144,7 @@ NECO_NOWORKERS // Disable all worker threads, work will run in coroutine
#define SCO_STATIC
#define STACK_STATIC
#define WORKER_STATIC
#if defined(__GNUC__)
#pragma GCC diagnostic push
@@ -266,6 +289,11 @@ static void llco_exit(void) {
#error LLCO_ASM must not be defined
#endif
#if defined(__COSMOCC__) && !defined(LLCO_NOASM)
// Cosmopolitan has issues with asm code
#define LLCO_NOASM
#endif
// Passing the entry function into assembly requires casting the function
// pointer to an object pointer, which is forbidden in the ISO C spec but
// allowed in posix. Ignore the warning attributed to this requirement when
@@ -1300,7 +1328,7 @@ const char *llco_method(void *caps) {
}
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__) && !defined(_WIN32) && \
!defined(LLCO_NOUNWIND)
!defined(LLCO_NOUNWIND) && !defined(__COSMOCC__)
#include <unwind.h>
#include <string.h>
@@ -1313,7 +1341,7 @@ struct llco_dlinfo {
void *dli_saddr; /* Address of nearest symbol */
};
#ifdef __linux__
#if defined(__linux__) && !defined(_GNU_SOURCE)
int dladdr(const void *, void *);
#endif
@@ -1984,11 +2012,11 @@ static void sco_entry(void *udata) {
co->prev = co;
co->next = co;
if (sco_cur) {
// Reschedule the coroutine that started this one
sco_list_push_back(&sco_yielders, co);
sco_list_push_back(&sco_yielders, sco_cur);
sco_nyielders += 2;
sco_switch(false, false);
// Reschedule the coroutine that started this one immediately after
// all running coroutines, but before any yielding coroutines, and
// continue running the started coroutine.
sco_list_push_back(&sco_runners, sco_cur);
sco_nrunners++;
}
sco_cur = co;
if (sco_user_entry) {
@@ -2591,7 +2619,7 @@ void *stack_addr(struct stack *stack) {
}
// END stack.c
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
// BEGIN worker.c
// https://github.com/tidwall/worker.c
//
@@ -2607,6 +2635,12 @@ void *stack_addr(struct stack *stack) {
#include <string.h>
#include <pthread.h>
#ifdef WORKER_STATIC
#define WORKER_API static
#else
#define WORKER_API
#endif
#define WORKER_DEF_TIMEOUT INT64_C(1000000000) // one second
#define WORKER_DEF_MAX_THREADS 2
#define WORKER_DEF_MAX_THREAD_ENTRIES 32
@@ -2641,6 +2675,7 @@ struct worker {
void (*free)(void*);
};
WORKER_API
void worker_free(struct worker *worker) {
if (worker) {
if (worker->threads) {
@@ -2665,6 +2700,7 @@ void worker_free(struct worker *worker) {
}
}
WORKER_API
struct worker *worker_new(struct worker_opts *opts) {
// Load options
int nthreads = opts ? opts->max_threads : 0;
@@ -2739,10 +2775,10 @@ static void *worker_entry(void *arg) {
ts.tv_sec += 1;
pthread_cond_timedwait(&thread->cond, &thread->mu, &ts);
if (thread->len == 0) {
thread->th = 0;
if (!thread->end) {
pthread_detach(thread->th);
}
thread->th = 0;
thread->end = false;
break;
}
@@ -2759,6 +2795,7 @@ static void *worker_entry(void *arg) {
/// @param udata any user data
/// @return true for success or false if no worker is available.
/// @return false for invalid arguments. Worker and work must no be null.
WORKER_API
bool worker_submit(struct worker *worker, int64_t pin, void(*work)(void *udata),
void *udata)
{
@@ -2795,7 +2832,7 @@ bool worker_submit(struct worker *worker, int64_t pin, void(*work)(void *udata),
return submitted;
}
// END worker.c
#endif // NECO_NOWORKER
#endif // NECO_NOWORKERS
#if defined(__GNUC__)
#pragma GCC diagnostic pop
@@ -2827,9 +2864,11 @@ bool worker_submit(struct worker *worker, int64_t pin, void(*work)(void *udata),
#include <netinet/in.h>
#include <sys/un.h>
#include <dlfcn.h>
#include <sys/syscall.h>
#endif
#include <pthread.h>
#include "neco.h"
#if defined(__FreeBSD__) || defined(__APPLE__)
@@ -2839,7 +2878,7 @@ bool worker_submit(struct worker *worker, int64_t pin, void(*work)(void *udata),
#include <sys/epoll.h>
#include <sys/eventfd.h>
#define NECO_POLL_EPOLL
#elif defined(__EMSCRIPTEN__) || defined(_WIN32)
#elif defined(__EMSCRIPTEN__) || defined(_WIN32) || defined(__COSMOCC__)
// #warning Webassembly has no polling
#define NECO_POLL_DISABLED
#else
@@ -3218,7 +3257,7 @@ struct cleanup {
void (*routine)(void *);
void *arg;
struct cleanup *next;
} aligned16;
};
////////////////////////////////////////////////////////////////////////////////
// colist - The standard queue type that is just a doubly linked list storing
@@ -3230,7 +3269,7 @@ struct coroutine;
struct colink {
struct coroutine *prev;
struct coroutine *next;
} aligned16;
};
struct colist {
struct colink head;
@@ -3583,7 +3622,7 @@ struct runtime {
void (*arc4random_buf)(void *, size_t);
#endif
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
struct worker *worker;
pthread_mutex_t iomu;
struct colist iolist;
@@ -3652,7 +3691,11 @@ static struct coroutine *evexists(int fd, enum evkind kind) {
static int is_main_thread(void) {
return IsGUIThread(false);
}
#elif defined(__linux__) || defined(__EMSCRIPTEN__)
#elif defined(__linux__)
static int is_main_thread(void) {
return getpid() == (pid_t)syscall(SYS_gettid);
}
#elif defined(__EMSCRIPTEN__) || defined(__COSMOCC__)
int gettid(void);
static int is_main_thread(void) {
return getpid() == gettid();
@@ -4146,7 +4189,7 @@ static void rt_sched_paused_step(void) {
}
timeout = CLAMP(timeout, 0, MAX_TIMEOUT);
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
if (rt->niowaiters > 0) {
timeout = 0;
while (1) {
@@ -4319,9 +4362,9 @@ static int run(void(*coroutine)(int, void**), int nargs, va_list *args,
goto fail;
}
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
struct worker_opts wopts = {
.max_threads = NECO_MAXWORKER,
.max_threads = NECO_MAXWORKERS,
.max_thread_entries = NECO_MAXRINGSIZE,
.malloc = malloc0,
.free = free0,
@@ -4335,7 +4378,6 @@ static int run(void(*coroutine)(int, void**), int nargs, va_list *args,
colist_init(&rt->iolist);
#endif
// Start the main coroutine. Actually, it's just queued to run first.
ret = start(coroutine, nargs, args, argv, 0, 0);
if (ret != NECO_OK) {
@@ -4350,7 +4392,7 @@ fail:
rt_freezchanpool();
rt_restore_signal_handlers();
rt_release_dlhandles();
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
worker_free(rt->worker);
#endif
rt_release();
@@ -4981,7 +5023,8 @@ static void cowait(int fd, enum evkind kind, int64_t deadline) {
// Networking code
////////////////////////////////////////////////////////////////////////////////
#ifndef NECO_NOWORKER
#if !defined(NECO_NOWORKERS) && defined(NECO_USEREADWORKERS) && \
!defined(NECO_NOREADWORKERS)
struct ioread {
int fd;
@@ -5005,11 +5048,14 @@ static void ioread(void *udata) {
static ssize_t read1(int fd, void *data, size_t nbytes) {
ssize_t n;
bool nowork = false;
bool nowork = true;
#ifdef NECO_TESTING
if (neco_fail_read_counter > 0) {
nowork = true;
}
#endif
#if NECO_MAXIOWORKERS <= 0
nowork = true;
#endif
if (!nowork) {
nowork = true;
@@ -5021,11 +5067,7 @@ static ssize_t read1(int fd, void *data, size_t nbytes) {
.co = co,
.rt = rt,
};
#ifdef NECO_USEROUNDROBINPIN
int64_t pin = -1;
#else
int64_t pin = co->id;
#endif
int64_t pin = co->id % NECO_MAXIOWORKERS;
if (worker_submit(rt->worker, pin, ioread, &info)) {
rt->niowaiters++;
sco_pause();
@@ -5144,7 +5186,8 @@ static ssize_t write1(int fd, const void *data, size_t nbytes) {
#define write2 write1
#endif
#ifndef NECO_NOWORKER
#if !defined(NECO_NOWORKERS) && defined(NECO_USEWRITEWORKERS) && \
!defined(NECO_NOWRITEWORKERS)
struct iowrite {
int fd;
const void *data;
@@ -5172,6 +5215,9 @@ static ssize_t write3(int fd, const void *data, size_t nbytes) {
if (neco_fail_write_counter > 0 || neco_partial_write > 0) {
nowork = true;
}
#endif
#if NECO_MAXIOWORKERS <= 0
nowork = true;
#endif
if (!nowork) {
nowork = true;
@@ -5183,11 +5229,7 @@ static ssize_t write3(int fd, const void *data, size_t nbytes) {
.co = co,
.rt = rt,
};
#ifdef NECO_USEROUNDROBINPIN
int64_t pin = -1;
#else
int64_t pin = co->id;
#endif
int64_t pin = co->id % NECO_MAXIOWORKERS;
if (worker_submit(rt->worker, pin, iowrite, &info)) {
rt->niowaiters++;
sco_pause();
@@ -7039,13 +7081,13 @@ int neco_serve(const char *network, const char *address) {
struct neco_mutex {
int64_t rtid; // runtime id
bool locked; // mutex is locked (read or write)
int rlocked; // read lock counter
struct colist queue; // coroutine doubly linked list
int rlocked; // read lock counter
bool locked; // mutex is locked (read or write)
};
static_assert(sizeof(neco_mutex) >= sizeof(struct neco_mutex), "");
static_assert(_Alignof(neco_mutex) == _Alignof(struct neco_mutex), "");
static int mutex_init(neco_mutex *mutex) {
struct neco_mutex *mu = (void*)mutex;
@@ -7296,12 +7338,13 @@ int neco_mutex_destroy(neco_mutex *mutex) {
}
struct neco_waitgroup {
int64_t rtid;
int count;
struct colist queue;
int64_t rtid; // runtime id
struct colist queue; // coroutine doubly linked list
int count; // current wait count
};
static_assert(sizeof(neco_waitgroup) >= sizeof(struct neco_waitgroup), "");
static_assert(_Alignof(neco_waitgroup) == _Alignof(struct neco_waitgroup), "");
inline
static int check_waitgroup(struct neco_waitgroup *wg) {
@@ -7444,11 +7487,12 @@ int neco_waitgroup_destroy(neco_waitgroup *waitgroup) {
}
struct neco_cond {
int64_t rtid; // runtime id
struct colist queue; // coroutine doubly linked list
int64_t rtid; // runtime id
struct colist queue; // coroutine doubly linked list
};
static_assert(sizeof(neco_cond) >= sizeof(struct neco_cond), "");
static_assert(_Alignof(neco_cond) == _Alignof(struct neco_cond), "");
static int cond_init0(struct neco_cond *cv) {
memset(cv, 0, sizeof(struct neco_cond));
@@ -7749,11 +7793,13 @@ static int _pipe_(int pipefd[2]) {
char path[PATH_MAX];
snprintf(path, sizeof(path), "/tmp/neco.%" PRIu64 ".sock", tmpkey);
int ln = neco_serve("unix", path);
int64_t childid = 0;
if (ln > 0) {
// State a coroutine to connect to the listener.
int nret = neco_start(co_pipe, 3, path, &secret, &fd0);
neco_errconv_to_sys(nret);
if (nret == NECO_OK) {
childid = neco_lastid();
int64_t dl = neco_now() + NECO_SECOND * 5;
fd1 = neco_accept_dl(ln, 0, 0, dl);
neco_errconv_to_sys(fd1);
@@ -7773,8 +7819,8 @@ static int _pipe_(int pipefd[2]) {
unlink(path);
}
int perrno = errno;
neco_yield();
if (ret == 0) {
neco_join(childid);
pipefd[0] = fd0;
pipefd[1] = fd1;
fd0 = -1;
@@ -8649,7 +8695,7 @@ int neco_resume(int64_t id) {
return ret;
}
#ifndef NECO_NOWORKER
#ifndef NECO_NOWORKERS
struct iowork {
void(*work)(void *udata);
void *udata;
@@ -8667,6 +8713,7 @@ static void iowork(void *udata) {
#endif
static int workfn(int64_t pin, void(*work)(void *udata), void *udata) {
(void)pin;
struct coroutine *co = coself();
if (!work) {
return NECO_INVAL;
@@ -8674,7 +8721,7 @@ static int workfn(int64_t pin, void(*work)(void *udata), void *udata) {
if (!co) {
return NECO_PERM;
}
#ifdef NECO_NOWORKER
#ifdef NECO_NOWORKERS
// Run in foreground
work(udata);
#else
+21 -13
View File
@@ -19,6 +19,10 @@
#include <sys/socket.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
////////////////////////////////////////////////////////////////////////////////
// basic operations
////////////////////////////////////////////////////////////////////////////////
@@ -97,7 +101,7 @@ int neco_gen_close(neco_gen *gen);
/// @}
////////////////////////////////////////////////////////////////////////////////
// synchonization mechanisms
// synchronization mechanisms
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Mutexes Mutexes
@@ -106,7 +110,7 @@ int neco_gen_close(neco_gen *gen);
/// to a variable or set of variables and helps to avoid data inconsistencies
/// due to race conditions.
/// @{
typedef struct { char _[48]; } neco_mutex;
typedef struct { int64_t _0; intptr_t _1[5]; } neco_mutex;
#define NECO_MUTEX_INITIALIZER { 0 }
@@ -128,8 +132,7 @@ int neco_mutex_tryrdlock(neco_mutex *mutex);
/// At the same time, neco_waitgroup_wait() can be used to block until all
/// coroutines are completed.
/// @{
typedef struct { char _[48]; } neco_waitgroup;
typedef struct { int64_t _0; intptr_t _1[5]; } neco_waitgroup;
#define NECO_WAITGROUP_INITIALIZER { 0 }
int neco_waitgroup_init(neco_waitgroup *waitgroup);
@@ -143,7 +146,7 @@ int neco_waitgroup_wait_dl(neco_waitgroup *waitgroup, int64_t deadline);
/// A condition variable is a synchronization mechanism that allows coroutines
/// to suspend execution until some condition is true.
/// @{
typedef struct { char _[48]; } neco_cond;
typedef struct { int64_t _0; intptr_t _1[5]; } neco_cond;
#define NECO_COND_INITIALIZER { 0 }
int neco_cond_init(neco_cond *cond);
@@ -208,7 +211,7 @@ int neco_dial_dl(const char *network, const char *address, int64_t deadline);
/// @}
////////////////////////////////////////////////////////////////////////////////
// cancellation
// cancelation
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Cancelation Cancelation
@@ -225,8 +228,9 @@ int neco_cancel_dl(int64_t id, int64_t deadline);
int neco_setcanceltype(int type, int *oldtype);
int neco_setcancelstate(int state, int *oldstate);
#define neco_cleanup_push(routine, arg) {__neco_c0(&(char[32]){0},routine,arg);
#define neco_cleanup_pop(execute) __neco_c1(execute);}
#define neco_cleanup_push(routine, arg) {char __neco_handler[32]={0};\
__neco_c0(__neco_handler,routine,arg);
#define neco_cleanup_pop(execute) __neco_c1(execute);}
/// @}
@@ -261,13 +265,12 @@ int neco_signal_unwatch(int signo);
/// @}
////////////////////////////////////////////////////////////////////////////////
// background worker
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Worker Background worker
/// Run arbritary code in a background worker thread
/// Run arbitrary code in a background worker thread
/// @{
int neco_work(int64_t pin, void(*work)(void *udata), void *udata);
@@ -422,9 +425,10 @@ ssize_t neco_stream_buffered_write_size(neco_stream *stream);
#include <stdlib.h>
#define neco_main \
static inline __neco_main0(int argc, char *argv[]); \
__neco_main(int argc, char *argv[]); \
static void _neco_main(int argc, void *argv[]) { \
__neco_exit_prog(__neco_main0(*(int*)argv[0], *(char***)argv[1])); \
(void)argc; \
__neco_exit_prog(__neco_main(*(int*)argv[0], *(char***)argv[1])); \
} \
int main(int argc, char *argv[]) { \
neco_env_setpaniconerror(true); \
@@ -433,7 +437,7 @@ int main(int argc, char *argv[]) { \
fprintf(stderr, "neco_start: %s (code %d)\n", neco_strerror(ret), ret); \
return -1; \
}; \
int static inline __neco_main0
int __neco_main
////////////////////////////////////////////////////////////////////////////////
// private functions, not to be call directly
@@ -449,4 +453,8 @@ void __neco_exit_prog(int);
#define EAI_SYSTEM 11
#endif
#ifdef __cplusplus
}
#endif
#endif // NECO_H
+26 -13
View File
@@ -6,6 +6,7 @@ set -e
cd $(dirname "${BASH_SOURCE[0]}")
OK=0
FAILS=
finish() {
rm -fr *.o
rm -fr *.out
@@ -16,7 +17,7 @@ finish() {
rm -fr *.c.worker.js
rm -fr *.c.wasm
if [[ "$OK" != "1" ]]; then
echo "FAIL"
echo "FAIL [`echo "$FAILS" | xargs`]"
fi
}
trap finish EXIT
@@ -43,8 +44,10 @@ fi
if [[ "$CC" == "" ]]; then
CC=cc
fi
UCFLAGS=$CFLAGS
CFLAGS=
if [[ "$1" != "bench" ]]; then
CFLAGS="-O0 -g3 -Wall -Wextra -fstrict-aliasing $CFLAGS"
CFLAGS="-O0 -g2 -Wall -Wextra -fstrict-aliasing $CFLAGS"
CCVERSHEAD="$($CC --version | head -n 1)"
if [[ "$CCVERSHEAD" == "" ]]; then
exit 1
@@ -52,8 +55,10 @@ if [[ "$1" != "bench" ]]; then
if [[ "$CCVERSHEAD" == *"clang"* ]]; then
CLANGVERS="$(echo "$CCVERSHEAD" | awk '{print $4}' | awk -F'[ .]+' '{print $1}')"
INSTALLDIR="$($CC --version | grep InstalledDir)"
INSTALLDIR="${INSTALLDIR#* }"
fi
if [[ "$CC" == *"zig"* ]]; then
# echo Zig does not support asans
NOSANS=1
@@ -109,14 +114,15 @@ elif [[ "`uname`" == *"_NT-"* ]]; then
fi
if [[ "$CC" == *"zig"* ]]; then
# Without -O3, 'zig cc' has quirks issues on Mac OS.
CFLAGS="$CFLAGS -O3"
# Stack unwinding is not supported yet
# https://github.com/ziglang/zig/issues/9046
CFLAGS="$CFLAGS -DLLCO_NOUNWIND"
# Without -O1, 'zig cc' has quirks issues on Mac OS.
if [[ "`uname`" == "Darwin" ]]; then
CFLAGS="$CFLAGS -O1"
fi
fi
CFLAGS="$CFLAGS $UCFLAGS"
CC=${CC:-cc}
echo "CC: $CC"
@@ -205,6 +211,7 @@ else
if [[ "$WITHCOV" == "1" ]]; then
export LLVM_PROFILE_FILE="$f.profraw"
fi
set +e
if [[ "$VALGRIND" == "1" ]]; then
valgrind --leak-check=yes ./$f.test $@
elif [[ "$CC" == "emcc" ]]; then
@@ -212,24 +219,30 @@ else
else
./$f.test $@
fi
if [[ "$?" != "0" ]]; then
FAILS="$FAILS$(echo "$f" | cut -f 1 -d '.') "
fi
set -e
done
OK=1
echo "OK"
if [[ "$FAILS" == "" ]]; then
OK=1
echo "OK"
fi
if [[ "$COVREGIONS" == "" ]]; then
COVREGIONS="false"
fi
if [[ "$WITHCOV" == "1" ]]; then
$LLVM_PROFDATA merge *.profraw -o test.profdata
$LLVM_COV report *.test ../neco.c -ignore-filename-regex=.test. \
$INSTALLDIR/$LLVM_PROFDATA merge *.profraw -o test.profdata
$INSTALLDIR/$LLVM_COV report *.test ../neco.c -ignore-filename-regex=.test. \
-j=4 \
-show-functions=true \
-instr-profile=test.profdata > /tmp/test.cov.sum.txt
# echo coverage: $(cat /tmp/test.cov.sum.txt | grep TOTAL | awk '{ print $NF }')
echo covered: "$(cat /tmp/test.cov.sum.txt | grep TOTAL | awk '{ print $7; }') (lines)"
$LLVM_COV show *.test ../neco.c -ignore-filename-regex=.test. \
$INSTALLDIR/$LLVM_COV show *.test ../neco.c -ignore-filename-regex=.test. \
-j=4 \
-show-regions=true \
-show-expansions=$COVREGIONS \
+3 -3
View File
@@ -96,14 +96,14 @@ void co_sched1(int argc, void *argv[]) {
int *i = argv[1];
a[(*i)++] = 'B';
assert(neco_yield() == NECO_OK);
a[(*i)++] = 'D';
a[(*i)++] = 'F';
}
void co_sched2(int argc, void *argv[]) {
assert(argc == 2);
char *a = argv[0];
int *i = argv[1];
a[(*i)++] = 'E';
a[(*i)++] = 'D';
assert(neco_yield() == NECO_OK);
a[(*i)++] = 'G';
}
@@ -116,7 +116,7 @@ void co_sched(int argc, void *argv[]) {
assert(neco_start(co_sched1, 2, a, i) == NECO_OK);
a[(*i)++] = 'C';
assert(neco_start(co_sched2, 2, a, i) == NECO_OK);
a[(*i)++] = 'F';
a[(*i)++] = 'E';
assert(neco_yield() == NECO_OK);
a[(*i)++] = 'H';
}
-39
View File
@@ -343,30 +343,6 @@ void test_sync_cond_deadline(void) {
expect(neco_start(co_sync_cond_deadline, 0), NECO_OK);
}
void co_sync_mutex_rw_order_child2(int argc, void *argv[]) {
assert(argc == 2);
neco_mutex *mu = argv[0];
struct order *order = argv[1];
order_add(order, 17);
expect(neco_mutex_rdlock(mu), NECO_OK);
order_add(order, 23);
expect(neco_mutex_unlock(mu), NECO_OK);
order_add(order, 26);
}
void co_sync_mutex_rw_order_child3(int argc, void *argv[]) {
assert(argc == 2);
neco_mutex *mu = argv[0];
struct order *order = argv[1];
order_add(order, 19);
expect(neco_mutex_trylock(mu), NECO_BUSY);
order_add(order, 20);
expect(neco_mutex_rdlock(mu), NECO_OK);
order_add(order, 24);
expect(neco_mutex_unlock(mu), NECO_OK);
order_add(order, 27);
}
void co_sync_mutex_rw_order_child(int argc, void *argv[]) {
assert(argc == 2);
neco_mutex *mu = argv[0];
@@ -382,12 +358,6 @@ void co_sync_mutex_rw_order_child(int argc, void *argv[]) {
order_add(order, 13);
expect(neco_yield(), NECO_OK);
order_add(order, 14);
expect(neco_start(co_sync_mutex_rw_order_child2, 2, mu, order), NECO_OK);
order_add(order, 18);
expect(neco_start(co_sync_mutex_rw_order_child3, 2, mu, order), NECO_OK);
order_add(order, 21);
expect(neco_mutex_unlock(mu), NECO_OK);
order_add(order, 28);
}
void co_sync_mutex_rw_order(int argc, void *argv[]) {
@@ -414,15 +384,6 @@ void co_sync_mutex_rw_order(int argc, void *argv[]) {
order_add(&order, 12);
expect(neco_mutex_unlock(mu), NECO_OK);
order_add(&order, 15);
expect(neco_mutex_tryrdlock(mu), NECO_BUSY);
order_add(&order, 16);
expect(neco_mutex_rdlock(mu), NECO_OK);
order_add(&order, 22);
expect(neco_mutex_unlock(mu), NECO_OK);
order_add(&order, 25);
expect(neco_yield(), NECO_OK);
order_add(&order, 29);
expect(neco_mutex_destroy(mu), NECO_OK);
order_check(&order);
}
+1 -1
View File
@@ -15,7 +15,7 @@ void co_work_runner(int argc, void *argv[]) {
void co_work(int argc, void *argv[]) {
(void)argc; (void)argv;
for (int i = 0; i < 1000; i++) {
for (int i = 0; i < 100; i++) {
expect(neco_start(co_work_runner, 1, &i), NECO_OK);
}
}