neco: init

This commit is contained in:
Kindy Lin
2024-07-15 21:52:45 +08:00
parent 6e02dace18
commit a0ee11c300
6 changed files with 9408 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
#include "../../../_neco/neco.h"
#include <stdio.h>
#include <unistd.h>
void coroutine(int argc, void *argv[]) {
// Yield each int to the caller, one at a time.
for (int i = 0; i < 10; i++) {
neco_gen_yield(&i);
}
}
static int __neco_main() {
// Create a new generator coroutine that is used to send ints.
neco_gen *gen;
neco_gen_start(&gen, sizeof(int), coroutine, 0);
// Iterate over each int until the generator is closed.
int i;
while (neco_gen_next(gen, &i) != NECO_CLOSED) {
printf("%d\n", i);
}
// This coroutine no longer needs the generator.
neco_gen_release(gen);
return 0;
}
static void _neco_main() { __neco_exit_prog(__neco_main()); }
void run_main() {
neco_env_setpaniconerror(true);
neco_env_setcanceltype(NECO_CANCEL_ASYNC);
int ret = neco_start(_neco_main, 0);
fprintf(stderr, "neco_start: %s (code %d)\n", neco_strerror(ret), ret);
};

View File

@@ -0,0 +1,19 @@
package main
import (
_ "unsafe"
_ "github.com/goplus/llgo/c"
)
const (
LLGoFiles = "_c/generators.c; ../../_neco/neco.c"
LLGoPackage = "link"
)
func main() {
run_main()
}
//go:linkname run_main C.run_main
func run_main() {}

View File

@@ -0,0 +1,70 @@
package main
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/neco"
)
func main() {
// c.Printf(c.Str("main"))
run_main()
}
func run_main() {
// c.Printf(c.Str("run_main"))
// c.Fflush(c.Stdout)
neco.Neco_env_setpaniconerror(true)
neco.Neco_env_setcanceltype(neco.NECO_CANCEL_ASYNC)
ret := neco.Neco_start(main2, 0)
c.Fprintf(c.Stderr, c.Str("neco_start: %s (code %d)\n"), neco.Neco_strerror(int(ret)), ret)
}
func main2(argc c.Int, argv ...any) {
// c.Printf(c.Str("main2"))
// c.Fflush(c.Stdout)
neco.Neco_exit_prog(main3())
}
func main3() c.Int {
// c.Printf(c.Str("main3"))
// c.Fflush(c.Stdout)
// Create a new generator coroutine that is used to send ints.
gen := new(neco.Neco_gen)
neco.Neco_gen_start(&gen, unsafe.Sizeof(int(0)), coroutine, 0)
// Iterate over each int until the generator is closed.
var i c.Int
for {
ret := neco.Neco_gen_next(gen, c.Pointer(&i))
// c.Printf(c.Str("gen [%d, %d] "), ret, c.Int(neco.NECO_CLOSED))
// c.Fflush(c.Stdout)
if ret != c.Int(neco.NECO_CLOSED) {
c.Printf(c.Str("%d\n"), i)
} else {
break
}
}
// This coroutine no longer needs the generator.
neco.Neco_gen_release(gen)
// c.Printf(c.Str("main3 end"))
// c.Fflush(c.Stdout)
return 0
}
func coroutine(argc c.Int, argv ...any) {
// Yield each int to the caller, one at a time.
for i := 0; i < 10; i++ {
neco.Neco_gen_yield(c.Pointer(&i))
}
// c.Printf(c.Str("coroutine end"))
// c.Fflush(c.Stdout)
}

8762
c/neco/_neco/neco.c Normal file

File diff suppressed because it is too large Load Diff

453
c/neco/_neco/neco.h Normal file
View File

@@ -0,0 +1,453 @@
// https://github.com/tidwall/neco
//
// Copyright 2024 Joshua J Baker. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//
// Neco -- Coroutine library for C
#ifndef NECO_H
#define NECO_H
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#ifdef _WIN32
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#endif
////////////////////////////////////////////////////////////////////////////////
// basic operations
////////////////////////////////////////////////////////////////////////////////
/// @defgroup BasicOperations Basic operations
/// Neco provides standard operations for starting a coroutine, sleeping,
/// suspending, resuming, yielding to another coroutine, joining/waiting for
/// child coroutines, and exiting a running coroutine.
/// @{
int neco_start(void(*coroutine)(int argc, void *argv[]), int argc, ...);
int neco_startv(void(*coroutine)(int argc, void *argv[]), int argc, void *argv[]);
int neco_yield(void);
int neco_sleep(int64_t nanosecs);
int neco_sleep_dl(int64_t deadline);
int neco_join(int64_t id);
int neco_join_dl(int64_t id, int64_t deadline);
int neco_suspend(void);
int neco_suspend_dl(int64_t deadline);
int neco_resume(int64_t id);
void neco_exit(void);
int64_t neco_getid(void);
int64_t neco_lastid(void);
int64_t neco_starterid(void);
/// @}
////////////////////////////////////////////////////////////////////////////////
// channels
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Channels Channels
/// Channels allow for sending and receiving values between coroutines.
/// By default, sends and receives will block until the other side is ready.
/// This allows the coroutines to synchronize without using locks or condition
/// variables.
/// @{
typedef struct neco_chan neco_chan;
int neco_chan_make(neco_chan **chan, size_t data_size, size_t capacity);
int neco_chan_retain(neco_chan *chan);
int neco_chan_release(neco_chan *chan);
int neco_chan_send(neco_chan *chan, void *data);
int neco_chan_send_dl(neco_chan *chan, void *data, int64_t deadline);
int neco_chan_broadcast(neco_chan *chan, void *data);
int neco_chan_recv(neco_chan *chan, void *data);
int neco_chan_recv_dl(neco_chan *chan, void *data, int64_t deadline);
int neco_chan_tryrecv(neco_chan *chan, void *data);
int neco_chan_close(neco_chan *chan);
int neco_chan_select(int nchans, ...);
int neco_chan_select_dl(int64_t deadline, int nchans, ...);
int neco_chan_selectv(int nchans, neco_chan *chans[]);
int neco_chan_selectv_dl(int nchans, neco_chan *chans[], int64_t deadline);
int neco_chan_tryselect(int nchans, ...);
int neco_chan_tryselectv(int nchans, neco_chan *chans[]);
int neco_chan_case(neco_chan *chan, void *data);
/// @}
////////////////////////////////////////////////////////////////////////////////
// generators
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Generators Generators
/// A generator is a specialized iterator-bound coroutine that can produce a
/// sequence of values to be iterated over.
/// @{
typedef struct neco_gen neco_gen;
int neco_gen_start(neco_gen **gen, size_t data_size, void(*coroutine)(int argc, void *argv[]), int argc, ...);
int neco_gen_startv(neco_gen **gen, size_t data_size, void(*coroutine)(int argc, void *argv[]), int argc, void *argv[]);
int neco_gen_retain(neco_gen *gen);
int neco_gen_release(neco_gen *gen);
int neco_gen_yield(void *data);
int neco_gen_yield_dl(void *data, int64_t deadline);
int neco_gen_next(neco_gen *gen, void *data);
int neco_gen_next_dl(neco_gen *gen, void *data, int64_t deadline);
int neco_gen_close(neco_gen *gen);
/// @}
////////////////////////////////////////////////////////////////////////////////
// synchronization mechanisms
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Mutexes Mutexes
/// A mutex is synchronization mechanism that blocks access to variables by
/// multiple coroutines at once. This enforces exclusive access by a coroutine
/// to a variable or set of variables and helps to avoid data inconsistencies
/// due to race conditions.
/// @{
typedef struct { _Alignas(16) char _[48]; } neco_mutex;
#define NECO_MUTEX_INITIALIZER { 0 }
int neco_mutex_init(neco_mutex *mutex);
int neco_mutex_lock(neco_mutex *mutex);
int neco_mutex_lock_dl(neco_mutex *mutex, int64_t deadline);
int neco_mutex_trylock(neco_mutex *mutex);
int neco_mutex_unlock(neco_mutex *mutex);
int neco_mutex_rdlock(neco_mutex *mutex);
int neco_mutex_rdlock_dl(neco_mutex *mutex, int64_t deadline);
int neco_mutex_tryrdlock(neco_mutex *mutex);
/// @}
/// @defgroup WaitGroups WaitGroups
/// A WaitGroup waits for a multiple coroutines to finish.
/// The main coroutine calls neco_waitgroup_add() to set the number of
/// coroutines to wait for. Then each of the coroutines runs and calls
/// neco_waitgroup_done() when complete.
/// At the same time, neco_waitgroup_wait() can be used to block until all
/// coroutines are completed.
/// @{
typedef struct { _Alignas(16) char _[48]; } neco_waitgroup;
#define NECO_WAITGROUP_INITIALIZER { 0 }
int neco_waitgroup_init(neco_waitgroup *waitgroup);
int neco_waitgroup_add(neco_waitgroup *waitgroup, int delta);
int neco_waitgroup_done(neco_waitgroup *waitgroup);
int neco_waitgroup_wait(neco_waitgroup *waitgroup);
int neco_waitgroup_wait_dl(neco_waitgroup *waitgroup, int64_t deadline);
/// @}
/// @defgroup CondVar Condition variables
/// A condition variable is a synchronization mechanism that allows coroutines
/// to suspend execution until some condition is true.
/// @{
typedef struct { _Alignas(16) char _[48]; } neco_cond;
#define NECO_COND_INITIALIZER { 0 }
int neco_cond_init(neco_cond *cond);
int neco_cond_signal(neco_cond *cond);
int neco_cond_broadcast(neco_cond *cond);
int neco_cond_wait(neco_cond *cond, neco_mutex *mutex);
int neco_cond_wait_dl(neco_cond *cond, neco_mutex *mutex, int64_t deadline);
/// @}
////////////////////////////////////////////////////////////////////////////////
// file descriptors
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Posix Posix wrappers
/// Functions that work like their Posix counterpart but do not block, allowing
/// for usage in a Neco coroutine.
/// @{
// wrappers for various posix operations.
ssize_t neco_read(int fd, void *data, size_t nbytes);
ssize_t neco_read_dl(int fd, void *data, size_t nbytes, int64_t deadline);
ssize_t neco_write(int fd, const void *data, size_t nbytes);
ssize_t neco_write_dl(int fd, const void *data, size_t nbytes, int64_t deadline);
int neco_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int neco_accept_dl(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int64_t deadline);
int neco_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int neco_connect_dl(int sockfd, const struct sockaddr *addr, socklen_t addrlen, int64_t deadline);
int neco_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res);
int neco_getaddrinfo_dl(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res, int64_t deadline);
/// @}
/// @defgroup Posix2 File descriptor helpers
/// Functions for working with file descriptors.
/// @{
// utility for enabling non-blocking on existing file descriptors
int neco_setnonblock(int fd, bool nonblock, bool *oldnonblock);
// wait for a file descriptor to be readable or writeable.
#define NECO_WAIT_READ 1
#define NECO_WAIT_WRITE 2
int neco_wait(int fd, int mode);
int neco_wait_dl(int fd, int mode, int64_t deadline);
/// @}
////////////////////////////////////////////////////////////////////////////////
// networking
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Networking Networking utilities
/// @{
int neco_serve(const char *network, const char *address);
int neco_serve_dl(const char *network, const char *address, int64_t deadline);
int neco_dial(const char *network, const char *address);
int neco_dial_dl(const char *network, const char *address, int64_t deadline);
/// @}
////////////////////////////////////////////////////////////////////////////////
// cancelation
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Cancelation Cancelation
/// @{
int neco_cancel(int64_t id);
int neco_cancel_dl(int64_t id, int64_t deadline);
#define NECO_CANCEL_ASYNC 1
#define NECO_CANCEL_INLINE 2
#define NECO_CANCEL_ENABLE 3
#define NECO_CANCEL_DISABLE 4
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);}
/// @}
////////////////////////////////////////////////////////////////////////////////
// random number generator
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Random Random number generator
/// @{
#define NECO_CSPRNG 0 // Cryptographically secure pseudorandom number generator
#define NECO_PRNG 1 // Pseudorandom number generator (non-crypto, faster)
int neco_rand_setseed(int64_t seed, int64_t *oldseed);
int neco_rand(void *data, size_t nbytes, int attr);
int neco_rand_dl(void *data, size_t nbytes, int attr, int64_t deadline);
/// @}
////////////////////////////////////////////////////////////////////////////////
// signal handling
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Signals Signals
/// Allows for signals, such as SIGINT (Ctrl-C), to be intercepted or ignored.
/// @{
int neco_signal_watch(int signo);
int neco_signal_wait(void);
int neco_signal_wait_dl(int64_t deadline);
int neco_signal_unwatch(int signo);
/// @}
////////////////////////////////////////////////////////////////////////////////
// background worker
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Worker Background worker
/// Run arbitrary code in a background worker thread
/// @{
int neco_work(int64_t pin, void(*work)(void *udata), void *udata);
/// @}
////////////////////////////////////////////////////////////////////////////////
// Stats and information
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Stats Stats and information
/// @{
typedef struct neco_stats {
size_t coroutines; ///< Number of active coroutines
size_t sleepers; ///< Number of sleeping coroutines
size_t evwaiters; ///< Number of coroutines waiting on I/O events
size_t sigwaiters; ///<
size_t senders; ///<
size_t receivers; ///<
size_t locked; ///<
size_t waitgroupers; ///<
size_t condwaiters; ///<
size_t suspended; ///<
size_t workers; ///< Number of background worker threads
} neco_stats;
int neco_getstats(neco_stats *stats);
int neco_is_main_thread(void);
const char *neco_switch_method(void);
/// @}
////////////////////////////////////////////////////////////////////////////////
// global behaviors
////////////////////////////////////////////////////////////////////////////////
/// @defgroup GlobalFuncs Global environment
/// @{
void neco_env_setallocator(void *(*malloc)(size_t), void *(*realloc)(void*, size_t), void (*free)(void*));
void neco_env_setpaniconerror(bool paniconerror);
void neco_env_setcanceltype(int type);
void neco_env_setcancelstate(int state);
/// @}
////////////////////////////////////////////////////////////////////////////////
// time and duration
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Time Time
/// Functions for working with time.
///
/// The following defines are available for convenience.
///
/// ```c
/// #define NECO_NANOSECOND INT64_C(1)
/// #define NECO_MICROSECOND INT64_C(1000)
/// #define NECO_MILLISECOND INT64_C(1000000)
/// #define NECO_SECOND INT64_C(1000000000)
/// #define NECO_MINUTE INT64_C(60000000000)
/// #define NECO_HOUR INT64_C(3600000000000)
/// ```
///
/// @{
#define NECO_NANOSECOND INT64_C(1)
#define NECO_MICROSECOND INT64_C(1000)
#define NECO_MILLISECOND INT64_C(1000000)
#define NECO_SECOND INT64_C(1000000000)
#define NECO_MINUTE INT64_C(60000000000)
#define NECO_HOUR INT64_C(3600000000000)
int64_t neco_now(void);
///@}
////////////////////////////////////////////////////////////////////////////////
// errors
////////////////////////////////////////////////////////////////////////////////
/// @defgroup ErrorFuncs Error handling
/// Functions for working with [Neco errors](./API.md#neco-errors).
/// @{
#define NECO_OK 0 ///< Successful result (no error)
#define NECO_ERROR -1 ///< System error (check errno)
#define NECO_INVAL -2 ///< Invalid argument
#define NECO_PERM -3 ///< Operation not permitted
#define NECO_NOMEM -4 ///< Cannot allocate memory
#define NECO_EOF -5 ///< End of file or stream (neco_stream_*)
#define NECO_NOTFOUND -6 ///< No such coroutine (neco_cancel)
#define NECO_NOSIGWATCH -7 ///< Not watching on a signal
#define NECO_CLOSED -8 ///< Channel is closed
#define NECO_EMPTY -9 ///< Channel is empty (neco_chan_tryrecv)
#define NECO_TIMEDOUT -10 ///< Deadline has elapsed (neco_*_dl)
#define NECO_CANCELED -11 ///< Operation canceled (by neco_cancel)
#define NECO_BUSY -12 ///< Resource busy (mutex_trylock)
#define NECO_NEGWAITGRP -13 ///< Negative waitgroup counter
#define NECO_GAIERROR -14 ///< Error with getaddrinfo (check neco_gai_error)
#define NECO_UNREADFAIL -15 ///< Failed to unread byte (neco_stream_unread_byte)
#define NECO_PARTIALWRITE -16 ///< Failed to write all data (neco_stream_flush)
#define NECO_NOTGENERATOR -17 ///< Coroutine is not a generator (neco_gen_yield)
#define NECO_NOTSUSPENDED -18 ///< Coroutine is not suspended (neco_resume)
const char *neco_strerror(ssize_t errcode);
int neco_lasterr(void);
int neco_gai_lasterr(void);
int neco_panic(const char *fmt, ...);
/// @}
////////////////////////////////////////////////////////////////////////////////
// streama
////////////////////////////////////////////////////////////////////////////////
/// @defgroup Streams Streams and Buffered I/O
/// Create a Neco stream from a file descriptor using neco_stream_make() or
/// a buffered stream using neco_stream_make_buffered().
/// @{
typedef struct neco_stream neco_stream;
int neco_stream_make(neco_stream **stream, int fd);
int neco_stream_make_buffered(neco_stream **stream, int fd);
int neco_stream_close(neco_stream *stream);
int neco_stream_close_dl(neco_stream *stream, int64_t deadline);
ssize_t neco_stream_read(neco_stream *stream, void *data, size_t nbytes);
ssize_t neco_stream_read_dl(neco_stream *stream, void *data, size_t nbytes, int64_t deadline);
ssize_t neco_stream_write(neco_stream *stream, const void *data, size_t nbytes);
ssize_t neco_stream_write_dl(neco_stream *stream, const void *data, size_t nbytes, int64_t deadline);
ssize_t neco_stream_readfull(neco_stream *stream, void *data, size_t nbytes);
ssize_t neco_stream_readfull_dl(neco_stream *stream, void *data, size_t nbytes, int64_t deadline);
int neco_stream_read_byte(neco_stream *stream);
int neco_stream_read_byte_dl(neco_stream *stream, int64_t deadline);
int neco_stream_unread_byte(neco_stream *stream);
int neco_stream_flush(neco_stream *stream);
int neco_stream_flush_dl(neco_stream *stream, int64_t deadline);
ssize_t neco_stream_buffered_read_size(neco_stream *stream);
ssize_t neco_stream_buffered_write_size(neco_stream *stream);
/// @}
////////////////////////////////////////////////////////////////////////////////
// happy convenience macro
////////////////////////////////////////////////////////////////////////////////
// int neco_main(int argc, char *argv[]);
#include <stdio.h>
#include <stdlib.h>
#define neco_main \
__neco_main(int argc, char *argv[]); \
static void _neco_main(int argc, void *argv[]) { \
(void)argc; \
__neco_exit_prog(__neco_main(*(int*)argv[0], *(char***)argv[1])); \
} \
int main(int argc, char *argv[]) { \
neco_env_setpaniconerror(true); \
neco_env_setcanceltype(NECO_CANCEL_ASYNC); \
int ret = neco_start(_neco_main, 2, &argc, &argv); \
fprintf(stderr, "neco_start: %s (code %d)\n", neco_strerror(ret), ret); \
return -1; \
}; \
int __neco_main
////////////////////////////////////////////////////////////////////////////////
// private functions, not to be call directly
////////////////////////////////////////////////////////////////////////////////
void __neco_c0(void*,void(*)(void*),void*);
void __neco_c1(int);
void __neco_exit_prog(int);
////////////////////////////////////////////////////////////////////////////////
#ifndef EAI_SYSTEM
#define EAI_SYSTEM 11
#endif
#endif // NECO_H

70
c/neco/neco.go Normal file
View File

@@ -0,0 +1,70 @@
package neco
import (
_ "unsafe"
"github.com/goplus/llgo/c"
)
const (
LLGoFiles = "_neco/neco.c"
LLGoPackage = "link"
)
const (
NECO_OK = 0 ///< Successful result (no error)
NECO_ERROR = -1 ///< System error (check errno)
NECO_INVAL = -2 ///< Invalid argument
NECO_PERM = -3 ///< Operation not permitted
NECO_NOMEM = -4 ///< Cannot allocate memory
NECO_EOF = -5 ///< End of file or stream (neco_stream_*)
NECO_NOTFOUND = -6 ///< No such coroutine (neco_cancel)
NECO_NOSIGWATCH = -7 ///< Not watching on a signal
NECO_CLOSED = -8 ///< Channel is closed
NECO_EMPTY = -9 ///< Channel is empty (neco_chan_tryrecv)
NECO_TIMEDOUT = -10 ///< Deadline has elapsed (neco_*_dl)
NECO_CANCELED = -11 ///< Operation canceled (by neco_cancel)
NECO_BUSY = -12 ///< Resource busy (mutex_trylock)
NECO_NEGWAITGRP = -13 ///< Negative waitgroup counter
NECO_GAIERROR = -14 ///< Error with getaddrinfo (check neco_gai_error)
NECO_UNREADFAIL = -15 ///< Failed to unread byte (neco_stream_unread_byte)
NECO_PARTIALWRITE = -16 ///< Failed to write all data (neco_stream_flush)
NECO_NOTGENERATOR = -17 ///< Coroutine is not a generator (neco_gen_yield)
NECO_NOTSUSPENDED = -18 ///< Coroutine is not suspended (neco_resume)
NECO_CANCEL_ASYNC = 1
NECO_CANCEL_INLINE = 2
NECO_CANCEL_ENABLE = 3
NECO_CANCEL_DISABLE = 4
)
//go:linkname Neco_gen_yield C.neco_gen_yield
func Neco_gen_yield(data c.Pointer) c.Int
//go:linkname Neco_gen_next C.neco_gen_next
func Neco_gen_next(gen *Neco_gen, data c.Pointer) c.Int
//go:linkname Neco_gen_start C.neco_gen_start
func Neco_gen_start(gen **Neco_gen, data_size uintptr, co Neco_coro, argc c.Int, argv ...any) c.Int
//go:linkname Neco_gen_release C.neco_gen_release
func Neco_gen_release(gen *Neco_gen) c.Int
//go:linkname Neco_exit_prog C.__neco_exit_prog
func Neco_exit_prog(code c.Int)
//go:linkname Neco_env_setpaniconerror C.neco_env_setpaniconerror
func Neco_env_setpaniconerror(paniconerror bool)
//go:linkname Neco_env_setcanceltype C.neco_env_setcanceltype
func Neco_env_setcanceltype(type_ c.Int)
//go:linkname Neco_strerror C.neco_strerror
func Neco_strerror(errcode int) *c.Char
//go:linkname Neco_start C.neco_start
func Neco_start(co Neco_coro, argc c.Int, argv ...any) c.Int
type Neco_gen struct{}
type Neco_coro = func(argc c.Int, argv ...any)